import { HTMLProps, useRef, useEffect } from "react";

type TimelineProps = {
  length: number;
  offset: number;
  setLength: (n: number) => void;
  setOffset: (n: number) => void;
} & Omit<HTMLProps<HTMLCanvasElement>, "ref">;

export function TimelineBar({
  length: pLength,
  offset: pOffset,
  setLength: pSetLength,
  setOffset: pSetOffset,
  ...props
}: TimelineProps) {
  const ref = useRef<HTMLCanvasElement | null>(null);

  function setupHandler(canvas: HTMLCanvasElement) {
    const rect = canvas.getBoundingClientRect();
    let draggingOffset = false;
    let draggingLength = false;
    let offset = pOffset;
    let length = pLength;

    function getBodyRect() {
      const x = canvas.width * offset;
      const w = Math.max(10, canvas.width * length);
      return {
        x,
        y: 0,
        w,
        h: canvas.height,
      };
    }

    function getDragHandleRect() {
      const x = canvas.width * (offset + length);
      const w = 5;
      return {
        x,
        y: 0,
        w,
        h: canvas.height,
      };
    }

    function render() {
      const ctx = canvas.getContext("2d");
      if (!ctx) return;
      ctx.clearRect(0, 0, canvas.width, canvas.height);

      ctx.lineWidth = 1;
      ctx.strokeStyle = "white";
      ctx.strokeRect(0, 0, canvas.width, canvas.height);

      const drawBody = () => {
        const { x, y, w, h } = getBodyRect();
        ctx.fillStyle = "white";
        ctx.fillRect(x, y, w, h);
      };
      const drawHandle = () => {
        const { x, y, w, h } = getDragHandleRect();
        ctx.fillStyle = "#ccc";
        ctx.fillRect(x, y, w, h);
      };

      drawBody();
      drawHandle();

      requestAnimationFrame(render);
    }

    function scaleX(x: number) {
      return (x / rect.width) * canvas.width;
    }

    function getEventLocation(event: MouseEvent | TouchEvent): { x: number } {
      if (event instanceof TouchEvent) {
        return {
          x: scaleX(event.touches[0].clientX - rect.x),
        };
      } else {
        // MouseEvent
        return {
          x: scaleX(event.clientX - rect.x),
        };
      }
    }

    function xOfBody(x: number) {
      const { w } = getBodyRect();
      return Math.min(1 - length, Math.max(0, (x - w / 2) / canvas.width));
    }

    function xOfHandle(x: number) {
      const { w } = getDragHandleRect();
      return Math.min(1, Math.max(0.1, (x - w / 2) / canvas.width - offset));
    }

    function handleStart(event: MouseEvent | TouchEvent) {
      event.preventDefault();
      const { x } = getEventLocation(event);
      const body = getBodyRect();
      if (x >= body.x && x <= body.x + body.w) {
        draggingOffset = true;
        console.debug("dragging offset");
      }
      const handle = getDragHandleRect();
      if (x >= handle.x && x <= handle.x + handle.w) {
        draggingLength = true;
        console.debug("dragging length");
      }
    }

    function handleMove(event: MouseEvent | TouchEvent) {
      event.preventDefault();
      const { x } = getEventLocation(event);
      if (draggingLength) {
        const newVal = xOfHandle(x);
        length = newVal;
      } else if (draggingOffset) {
        const newVal = xOfBody(x);
        offset = newVal;
      }
    }

    function handleEnd(event: MouseEvent | TouchEvent) {
      event.preventDefault();
      const { x } = getEventLocation(event);
      console.debug("drag end");
      if (draggingLength) {
        const newVal = xOfHandle(x);
        pSetLength(newVal);
      } else if (draggingOffset) {
        const newVal = xOfBody(x);
        pSetOffset(newVal);
      }
      draggingLength = false;
      draggingOffset = false;
    }

    // Add mouse event listeners
    canvas.addEventListener("mousedown", handleStart);
    canvas.addEventListener("mousemove", handleMove);
    canvas.addEventListener("mouseup", handleEnd);
    canvas.addEventListener("mouseleave", handleEnd);

    // Add touch event listeners
    canvas.addEventListener("touchstart", handleStart);
    canvas.addEventListener("touchmove", handleMove);
    canvas.addEventListener("touchend", handleEnd);

    requestAnimationFrame(render);
  }

  useEffect(() => {
    if (ref.current) {
      console.debug("Setup render loop");
      setupHandler(ref.current);
    }
  }, [ref.current]);
  return <canvas {...props} ref={ref}></canvas>;
}
