import { useRef, useEffect, useState, MouseEvent } from 'react';
import { mouseEventToCanvasPoint } from './Helpers';

interface Props {
  position: number;
  duration: number;
  offsetPixels: number;
  onPositionChanged: (posiiton: number) => void;
}

enum Mode {
  Normal,
  Dragging,
}

const LogicalWidth = 2000;
const LogicalHeight = 100;
const InnerMargin = 40;

export const SeekBar: React.FC<Props> = ({
  position,
  duration,
  offsetPixels,
  onPositionChanged,
}: Props) => {
  const [mode, setMode] = useState(Mode.Normal);
  const [cursor, setCursor] = useState('auto');
  const canvasRef = useRef<HTMLCanvasElement>(null);

  // render canvas
  useEffect(() => {
    const canvas = canvasRef.current;
    if (canvas == null) {
      console.error('no seekbar canvas ref available');
      return;
    }

    const ctx = canvas.getContext('2d');
    if (ctx == null) {
      console.error('no seekbar 2d context available');
      return;
    }

    // Set aspect ratio.
    canvas.width = canvas.height * (canvas.clientWidth / canvas.clientHeight);

    // background
    ctx.fillStyle = '#444444';
    ctx.fillRect(0, 0, canvas.width, canvas.height);

    // seek bar
    const pixelRatio = canvas.width / canvas.clientWidth;
    const offset = offsetPixels * pixelRatio;
    const width = canvas.width - offset * 2;
    ctx.fillStyle = 'black';
    ctx.fillRect(offset, InnerMargin, width, canvas.height - InnerMargin * 2);

    // pointer
    const positionRatio = position / duration;
    const x = offset + width * positionRatio;
    const y = canvas.height / 2;

    ctx.beginPath();
    ctx.arc(x, y, 20, 0, 2 * Math.PI, false);
    ctx.fillStyle = 'green';
    ctx.fill();
  });

  // helpers

  const emitPositionEvent = (evt: MouseEvent<HTMLCanvasElement>) => {
    const canvas = evt.currentTarget;
    const { x } = mouseEventToCanvasPoint(evt);
    const pixelRatio = canvas.width / canvas.clientWidth;
    const offset = offsetPixels * pixelRatio;
    const ratio = (x - offset) / (canvas.width - offset * 2);
    onPositionChanged(ratio * duration);
  };

  // handlers

  const handleMouseDown = (evt: MouseEvent<HTMLCanvasElement>) => {
    if (mode != Mode.Normal) return;

    setMode(Mode.Dragging);

    emitPositionEvent(evt);
  };

  const handleMouseUp = () => {
    if (mode != Mode.Dragging) return;

    setMode(Mode.Normal);
  };

  const handleMouseMove = (evt: MouseEvent<HTMLCanvasElement>) => {
    const { y } = mouseEventToCanvasPoint(evt);

    // TODO: improve mouse detection around knob.
    if (y > InnerMargin && y < LogicalHeight - InnerMargin) {
      setCursor('pointer');
    } else {
      setCursor('auto');
    }

    if (mode == Mode.Normal) return;

    emitPositionEvent(evt);
  };

  const handleMouseEnter = () => {
    if (mode != Mode.Dragging) return;

    setMode(Mode.Normal);
  };

  // render component

  const styles = {
    width: '100%',
    height: '30px',
    margin: '0 auto',
    cursor: cursor,
  };

  return (
    <>
      <canvas
        style={styles}
        ref={canvasRef}
        width={LogicalWidth}
        height={LogicalHeight}
        onMouseDown={handleMouseDown}
        onMouseUp={handleMouseUp}
        onMouseMove={handleMouseMove}
        onMouseEnter={handleMouseEnter}
      ></canvas>
    </>
  );
};
