import * as THREE from "three";

export const POLYGON_CLOSE_THRESHOLD = 0.1;
export const SNAP_POINT_THRESHOLD = 0.2;
export const SNAP_LINE_THRESHOLD = 0.2;
type PointData = {
  pointId: string;
  position: [number, number, number];
  isCurved: boolean;
  handleA: [number, number, number] | null;
  handleB: [number, number, number] | null;
};

interface PathDataInterface {
  pathId: string;
  pathPoints: [PointData, PointData]; // always two points
}

type PathData = PathDataInterface[];

export function handleMouseDown(
  evt: MouseEvent,
  isLeftClickDown: React.MutableRefObject<boolean>,
  isDragging: React.MutableRefObject<boolean>
) {
  if (evt.button === 0) {
    if (evt.ctrlKey || evt.shiftKey) return;
    isLeftClickDown.current = true;
    isDragging.current = false;
  }
}

export function handleMouseUp(
  evt: MouseEvent,
  isLeftClickDown: React.MutableRefObject<boolean>
) {
  if (evt.button === 0) isLeftClickDown.current = false;
}

export function handleMouseMove(
  isLeftClickDown: React.MutableRefObject<boolean>,
  isDragging: React.MutableRefObject<boolean>
) {
  if (isLeftClickDown.current) isDragging.current = true;
}

export function handleMouseClick({
  evt,
  isDragging,
  raycaster,
  plane,
  pointer,
  currentTempPath,
  setCurrentTempPath,
  pathPointsList,
  allPaths,
  setAllPaths,
  addPointToCurrentTemp,
}: {
  evt: MouseEvent;
  isDragging: React.MutableRefObject<boolean>;
  raycaster: THREE.Raycaster;
  plane: THREE.Plane;
  pointer: { x: number; y: number };
  currentTempPath: any[];
  setCurrentTempPath: (val: any[]) => void;
  pathPointsList: any[];
  allPaths: PathData;
  setAllPaths: React.Dispatch<React.SetStateAction<PathData>>;
  addPointToCurrentTemp: (point: any) => void;
}) {
  if (isDragging.current) return;
  if (evt.ctrlKey || evt.shiftKey) return;

  const intersectPoint = new THREE.Vector3();
  const pos = raycaster.ray.intersectPlane(plane, intersectPoint);
  if (!pos) return;

  let clickedPoint = new THREE.Vector3(pos.x, 0, pos.z); // force y = 0

  let snapPoint: any = null;
  for (let p of pathPointsList) {
    const pVec = new THREE.Vector3(p.position[0], 0, p.position[2]); // force y = 0
    if (pVec.distanceTo(clickedPoint) < SNAP_POINT_THRESHOLD) {
      snapPoint = {
        ...p,
        position: [p.position[0], 0, p.position[2]], // force y = 0
      };
      clickedPoint = pVec;
      break;
    }
  }

  let newPoint = snapPoint ?? {
    pointId: THREE.MathUtils.generateUUID(),
    position: [clickedPoint.x, 0, clickedPoint.z], // y = 0
    isCurved: false,
    handleA: null,
    handleB: null,
  };

  if (currentTempPath.length > 2) {
    const firstVec = new THREE.Vector3(
      currentTempPath[0].position[0],
      0,
      currentTempPath[0].position[2]
    ); // y = 0
    if (firstVec.distanceTo(clickedPoint) < POLYGON_CLOSE_THRESHOLD) {
      const closingPoint = {
        ...currentTempPath[0],
        position: [
          currentTempPath[0].position[0],
          0,
          currentTempPath[0].position[2],
        ], // y = 0
      };
      addPointToCurrentTemp(closingPoint);
      setCurrentTempPath([]);
      return;
    }
  }

  const getNearestPointOnLine = (
    a: THREE.Vector3,
    b: THREE.Vector3,
    p: THREE.Vector3
  ) => {
    const ab = new THREE.Vector3().subVectors(b, a);
    const t = Math.max(
      0,
      Math.min(1, p.clone().sub(a).dot(ab) / ab.lengthSq())
    );
    return a.clone().add(ab.multiplyScalar(t));
  };

  for (let path of allPaths) {
    const a = new THREE.Vector3(
      path.pathPoints[0].position[0],
      0,
      path.pathPoints[0].position[2]
    );
    const b = new THREE.Vector3(
      path.pathPoints[1].position[0],
      0,
      path.pathPoints[1].position[2]
    );
    const closest = getNearestPointOnLine(a, b, clickedPoint);
    closest.y = 0; // force y = 0

    if (closest.distanceTo(clickedPoint) < SNAP_LINE_THRESHOLD) {
      const splitPoint = {
        pointId: THREE.MathUtils.generateUUID(),
        position: [closest.x, 0, closest.z], // y = 0
        isCurved: false,
        handleA: null,
        handleB: null,
      };

      setAllPaths((prev) =>
        prev
          .filter((pa) => pa.pathId !== path.pathId)
          .concat([
            {
              pathId: THREE.MathUtils.generateUUID(),
              pathPoints: [
                {
                  ...path.pathPoints[0],
                  position: [
                    path.pathPoints[0].position[0],
                    0,
                    path.pathPoints[0].position[2],
                  ] as [number, number, number],
                },
                splitPoint,
              ] as [PointData, PointData],
            },
            {
              pathId: THREE.MathUtils.generateUUID(),
              pathPoints: [
                splitPoint,
                {
                  ...path.pathPoints[1],
                  position: [
                    path.pathPoints[1].position[0],
                    0,
                    path.pathPoints[1].position[2],
                  ] as [number, number, number],
                },
              ] as [PointData, PointData],
            },
          ])
      );
      console.log("path.pathPoints[1]: ", path.pathPoints);

      addPointToCurrentTemp(splitPoint);
      return;
    }
  }

  addPointToCurrentTemp(newPoint);
}

export function handleContextMenu(
  evt: MouseEvent,
  setCurrentTempPath: (val: any[]) => void
) {
  evt.preventDefault();
  setCurrentTempPath([]);
}
