import * as React from "react";
import { MapTransformProps } from "../map/interfaces";
import { sortGroupBy, sortOptionsTable } from "./point_group_sort_selector";
import { sortBy } from "lodash";
import { PointsPathLine } from "./group_order_visual";
import { Color } from "../../ui";
import { PointGroupSortType } from "farmbot/dist/resources/api_resources";
import { t } from "../../i18next_wrapper";
import { Actions } from "../../constants";
import { edit } from "../../api/crud";
import { TaggedPointGroup, TaggedPoint } from "farmbot";
import { error } from "../../toast/toast";

const xy = (point: TaggedPoint) => ({ x: point.body.x, y: point.body.y });

const distance = (p1: { x: number, y: number }, p2: { x: number, y: number }) =>
  Math.pow(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2), 0.5);

const pathDistance = (pathPoints: TaggedPoint[]) => {
  let total = 0;
  let prev: { x: number, y: number } | undefined = undefined;
  pathPoints.map(xy)
    .map(p => {
      prev ? total += distance(p, prev) : 0;
      prev = p;
    });
  return Math.round(total);
};

const findNearest =
  (from: { x: number, y: number }, available: TaggedPoint[]) => {
    const distances = available.map(p => ({
      point: p, distance: distance(xy(p), from)
    }));
    return sortBy(distances, "distance")[0].point;
  };

export const nn = (pathPoints: TaggedPoint[]) => {
  let available = pathPoints.slice(0);
  const ordered: TaggedPoint[] = [];
  let from = { x: 0, y: 0 };
  pathPoints.map(() => {
    if (available.length < 1) { return; }
    const nearest = findNearest(from, available);
    ordered.push(nearest);
    from = { x: nearest.body.x, y: nearest.body.y };
    available = available.filter(p => p.uuid !== nearest.uuid);
  });
  return ordered;
};

const SORT_TYPES: (PointGroupSortType | "nn")[] = [
  "random", "xy_ascending", "xy_descending", "yx_ascending", "yx_descending"];

export interface PathInfoBarProps {
  sortTypeKey: PointGroupSortType | "nn";
  dispatch: Function;
  group: TaggedPointGroup;
  pathData: { [key: string]: number };
}

export const PathInfoBar = (props: PathInfoBarProps) => {
  const { sortTypeKey, dispatch, group } = props;
  const pathLength = props.pathData[sortTypeKey];
  const maxLength = Math.max(...Object.values(props.pathData));
  const normalizedLength = pathLength / maxLength * 100;
  const sortLabel =
    sortTypeKey == "nn" ? "Optimized" : sortOptionsTable()[sortTypeKey];
  return <div className={"sort-path-info-bar"}
    onMouseEnter={() =>
      dispatch({ type: Actions.TRY_SORT_TYPE, payload: sortTypeKey })}
    onMouseLeave={() =>
      dispatch({ type: Actions.TRY_SORT_TYPE, payload: undefined })}
    onClick={() =>
      sortTypeKey == "nn"
        ? error(t("Not supported yet."))
        : dispatch(edit(group, { sort_type: sortTypeKey }))}
    style={{ width: `${normalizedLength}%` }}>
    {`${sortLabel}: ${Math.round(pathLength / 10) / 100}m`}
  </div>;
};

export interface PathsProps {
  pathPoints: TaggedPoint[];
  dispatch: Function;
  group: TaggedPointGroup;
}

interface PathsState {
  pathData: { [key: string]: number };
}

export class Paths extends React.Component<PathsProps, PathsState> {
  state: PathsState = { pathData: {} };

  generatePathData = (pathPoints: TaggedPoint[]) => {
    SORT_TYPES.map((sortType: PointGroupSortType) =>
      this.state.pathData[sortType] =
      pathDistance(sortGroupBy(sortType, pathPoints)));
    this.state.pathData.nn = pathDistance(nn(pathPoints));
  };

  render() {
    if (!this.state.pathData.nn) { this.generatePathData(this.props.pathPoints); }
    return <div>
      <label>{t("Path lengths by sort type")}</label>
      {SORT_TYPES.concat("nn").map(st =>
        <PathInfoBar key={st}
          sortTypeKey={st}
          dispatch={this.props.dispatch}
          group={this.props.group}
          pathData={this.state.pathData} />)}
    </div>;
  }
}

interface NNPathProps {
  pathPoints: TaggedPoint[];
  mapTransformProps: MapTransformProps;
}

export const NNPath = (props: NNPathProps) =>
  localStorage.getItem("try_it") == "ok"
    ? <PointsPathLine
      color={Color.blue}
      strokeWidth={2}
      dash={1}
      orderedPoints={nn(props.pathPoints).map(xy)}
      mapTransformProps={props.mapTransformProps} />
    : <g />;
