import React, { FC, useState, useRef, useEffect, useCallback } from "react";
import {
  ActionDot,
  ActionLine,
  AdditionalElementsWrapper,
  Dot,
} from "./AdditionalElements.styles";
import {
  getCordinates,
  calculateMapElementHeight,
  calculateMapElementWidth,
  calculateMapElementArc,
  getArc,
  getHeight,
  getWidth,
} from "./additionalHelper";
import {
  ADDITIONAL_TYPES,
  IAdditionalElements,
  IElementData,
  TAdditionalType,
} from "../../definitions/additional";
import { getLineArc, getLineLength } from "../../helpers/mathHelpers";

interface IGlobalPos {
  left: number;
  top: number;
}

export const DotElement: FC<IAdditionalElements> = ({
  element,
  scale,
  active,
  editMode,
  pdfHeight = 0,
  pdfWidth = 0,
  showDetails,
  saveChanges,
  handleShortcut,
  setAction,
  cancelAction,
  isAction,
  workspaceTop,
  workspaceLeft,
  focused,
  inputFocused
}) => {
  const pointRef = useRef<HTMLDivElement>(null);

  const [startPos, setStartPos] = useState({ x: 0, y: 0 });
  const [pointPos, setPointPos] = useState({ left: 0, top: 0 });
  const [globalPos, _setGlobalPos] = useState({ left: 0, top: 0 });
  const [initWidth, _setInitWidth] = useState(0);
  const [workspacePos, _setWorkspacePos] = useState({
    top: workspaceTop,
    left: workspaceLeft,
  });

  const [elementData, _setElementData] = useState<IElementData>({
    move: false,
    x: element.x,
    y: element.y,
    editMode: editMode,
    elementMode: element.type,
    pageHeight: pdfHeight,
    pageWidth: pdfWidth,
    rotate: getArc(element),
    width: getWidth(element),
    height: getHeight(element),
    size: element.size || 0,
    end_x: element.end_x || 0,
    end_y: element.end_y || 0,
    mode: "element",
    scale: scale,
    scaleAction: false,
    rotateAction: false,
  });

  const globalPosRef = React.useRef(globalPos);
  const setGlobalPos = (x: IGlobalPos) => {
    globalPosRef.current = x;
    _setGlobalPos(x);
  };

  const initWidthRef = React.useRef(initWidth);
  const setInitWidth = (x: number) => {
    initWidthRef.current = x;
    _setInitWidth(x);
  };

  const workspacePosRef = React.useRef(workspacePos);
  const setWorkspacePos = (x: { top: number; left: number }) => {
    workspacePosRef.current = x;
    _setWorkspacePos(x);
  };

  const elementDataRef = React.useRef(elementData);
  const setElementData = (x: IElementData) => {
    elementDataRef.current = x;
    _setElementData(x);
  };

  

  const updateElement = useCallback(() => {
    saveChanges(
      {
        ...element,
        x: elementDataRef.current.x,
        y: elementDataRef.current.y,
        end_x: elementDataRef.current.end_x,
        end_y: elementDataRef.current.end_y,
        size: elementDataRef.current.size,
        rotation: elementDataRef.current.rotate,
        height: elementDataRef.current.height,
      },
      false
    );
  }, [elementData, saveChanges, elementDataRef.current.size]);

  const startScale = useCallback(() => {
    setElementData({
      ...elementDataRef.current,
      scaleAction: true,
    });
    setAction();
  }, [active]);

  const stopScale = useCallback(() => {
    setElementData({
      ...elementDataRef.current,
      scaleAction: false,
    });
    updateElement();
  }, [active, elementData]);

  useEffect(() => {
    if (!isAction && elementData.scaleAction) {
      stopScale();
    }
  }, [isAction]);

  const startMove = useCallback(
    (event: MouseEvent) => {
      event.stopPropagation();
      showDetails(element.id!, true, false);
      if (elementData.scaleAction) {
        stopScale();
      }
      if (element.lock) return;

      setStartPos({ x: event.clientX, y: event.clientY });
      setPointPos({
        left: elementDataRef.current.x * elementDataRef.current.scale,
        top: elementDataRef.current.y * elementDataRef.current.scale,
      });
      setElementData({
        ...elementDataRef.current,
        move: true,
      });
    },
    [element, elementData, showDetails]
  );

  const move = useCallback(
    (event: MouseEvent) => {
      if (active) {
        const newTop =
          event.clientY -
          workspacePosRef.current.top -
          elementData.y * elementData.scale -
          135;

        const newLeft =
          event.clientX -
          workspacePosRef.current.left -
          elementData.x * elementData.scale -
          25;

        const posLeft = event.clientX - workspacePosRef.current.left - 25;
        const posTop = event.clientY - workspacePosRef.current.top - 137;

        setGlobalPos({
          left: posLeft,
          top: posTop,
        });

        if (!elementData.scaleAction) {
          setInitWidth(getLineLength(newLeft, newTop));
        } else if (elementData.scaleAction) {
          const newWidth = getLineLength(newLeft, newTop);
          const newSize = parseFloat(
            ((newWidth * elementData.size) / initWidthRef.current).toFixed(0)
          );

          if (newSize < 10 || newSize > 300) {
            return false;
          }

          setElementData({
            ...elementDataRef.current,
            width: newSize,
            height: newSize,
            size: newSize,
          });
        }
      }

      if (!elementData.move || element.lock) return;
      event.stopPropagation();

      const newDelta = {
        left: event.clientX - startPos.x,
        top: event.clientY - startPos.y,
      };

      setElementData({
        ...elementDataRef.current,
        x: (pointPos.left + newDelta.left) / elementData.scale,
        y: (pointPos.top + newDelta.top) / elementData.scale,
      });
    },
    [
      elementData.move,
      element.lock,
      element.height,
      startPos,
      pointPos,
      elementData.scaleAction,
    ]
  );

  const endMove = useCallback(
    (event: MouseEvent) => {
      if (!elementData.move) return;
      event.stopPropagation();
      setElementData({
        ...elementDataRef.current,
        move: false,
      });
      updateElement();
    },
    [elementData.move, updateElement]
  );

  useEffect(() => {
    if (!active && elementData.scaleAction) {
      stopScale();
    }
  }, [active]);

  useEffect(() => {
    setWorkspacePos({ top: workspaceTop, left: workspaceLeft });
  }, [workspaceLeft, workspaceTop]);

  const shortScope = ["x", "X", "Backspace", "Delete"];
  
  useEffect(() => {
    const point = pointRef.current;
    if (!point) return;
    const handleKeyDown = (e: KeyboardEvent) => {
      if (active && !inputFocused) {
        if (shortScope.includes(e.key)) {
          handleShortcut('delete', element.id, "mapElement");
        } else if (e.key === "s") {
          startScale();
        } else if (e.key === "Escape") {
          stopScale();
          cancelAction();
        }
      }
    };
    point.addEventListener("mousedown", startMove);
    window.addEventListener("mousemove", move, { passive: true });
    window.addEventListener("mouseup", endMove);
    window.addEventListener("mouseleave", endMove);
    document.addEventListener("keydown", handleKeyDown);

    return () => {
      point.removeEventListener("mousedown", startMove);
      window.removeEventListener("mousemove", move);
      window.removeEventListener("mouseup", endMove);
      window.removeEventListener("mouseleave", endMove);
      document.removeEventListener("keydown", handleKeyDown);
    };
  }, [startMove, move, endMove]);

  useEffect(() => {
    setElementData({
      ...elementDataRef.current,
      x: element.x,
      y: element.y,
      width: getWidth(element),
      height: getHeight(element),
      size: element.size,
      pageHeight: pdfHeight,
      pageWidth: pdfWidth,
      editMode: editMode,
      scale: scale,
    });
  }, [element, pdfHeight, pdfWidth, scale, editMode]);

  return (
    <>
      <AdditionalElementsWrapper
        isActive={active || focused}
        isEditMode={elementData.editMode}
        origin={false}
        style={{
          left: `${getCordinates(
            elementData.x * elementData.scale,
            elementData.width * elementData.scale,
            ADDITIONAL_TYPES.dot as TAdditionalType
          )}px`,
          top: `${getCordinates(
            elementData.y * elementData.scale,
            elementData.height * elementData.scale,
            ADDITIONAL_TYPES.dot as TAdditionalType
          )}px`,
          height: `${
            calculateMapElementHeight(elementData) * elementData.scale
          }px`,
          width: `${
            calculateMapElementWidth(elementData) * elementData.scale
          }px`,
          transform: `rotate(${calculateMapElementArc(elementData)}deg)`,
        }}
      >
        <Dot isActive={active || focused} ref={pointRef} />
      </AdditionalElementsWrapper>
      {(elementData.scaleAction || elementData.scaleAction) && (
        <>
          <ActionLine
            style={{
              height:
                getLineLength(
                  globalPos.left - elementData.x,
                  globalPos.top - elementData.y + 1
                ) + "px",
              transform: `rotate(${getLineArc(
                globalPos.left - elementData.x,
                globalPos.top - elementData.y + 1
              )}deg)`,
              left: elementData.x * elementData.scale,
              top: elementData.y * elementData.scale,
            }}
          />
          <ActionDot
            style={{
              top: globalPos.top,
              left: globalPos.left,
            }}
          />
        </>
      )}
    </>
  );
};
