import { useEffect, useState, useRef, MouseEvent } from 'react';
import { Canvas as WaveformCanvas } from './Canvas';
import { CanvasLogicalWidth, CanvasLogicalHeight, Selection } from './Waveform';
import { mouseEventToCanvasX } from './Helpers';

interface Props {
  peaks: number[][] | null;
  numFrames: number;
  style: React.CSSProperties;
  onSelectionStart: (x1: number) => void;
  onSelectionChange: (selection: Selection) => void;
}

enum Mode {
  Normal,
  Selecting,
}

// TODO: render position marker during playback
export const Waveform: React.FC<Props> = (props: Props) => {
  const hudCanvasRef = useRef<HTMLCanvasElement>(null);
  const [mode, setMode] = useState(Mode.Normal);

  const defaultSelection: Selection = { x1: 0, x2: 0 };
  // selection is the current selection in canvas coordinates:
  const [selection, setSelection] = useState(defaultSelection);
  // newSelection is a new selection in the process of being drawn by the user.
  // It is only useful if Mode.Selecting is active.
  const [newSelection, setNewSelection] = useState(defaultSelection);

  // effects

  // draw the overview waveform
  useEffect(() => {
    (async function () {
      const canvas = hudCanvasRef.current;
      if (canvas == null) {
        console.error('no hud canvas ref available');
        return;
      }

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

      ctx.clearRect(0, 0, canvas.width, canvas.height);

      let currentSelection: Selection;
      if (mode == Mode.Selecting) {
        currentSelection = newSelection;
      } else {
        currentSelection = selection;
      }
      if (currentSelection.x1 >= currentSelection.x2) {
        return;
      }

      ctx.beginPath();
      ctx.strokeStyle = 'red';
      ctx.lineWidth = 2;
      ctx.fillStyle = 'rgba(255, 255, 255, 0.3)';
      ctx.rect(
        currentSelection.x1,
        2,
        currentSelection.x2 - currentSelection.x1,
        canvas.height - 8
      );
      ctx.fill();
      ctx.stroke();
    })();
  });

  // handlers

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

    setMode(Mode.Selecting);

    const x = mouseEventToCanvasX(evt);
    setNewSelection({ x1: x, x2: x });
    props.onSelectionStart(x);
  };

  const handleMouseMove = (evt: MouseEvent<HTMLCanvasElement>) => {
    if (mode != Mode.Selecting) {
      return;
    }

    const x = mouseEventToCanvasX(evt);
    if (x == newSelection.x2) {
      return;
    }

    setNewSelection({ ...newSelection, x2: x });
  };

  const handleMouseUp = () => {
    if (mode != Mode.Selecting) {
      return;
    }
    setMode(Mode.Normal);

    // TODO: better shallow equality check?
    if (selection.x1 !== newSelection.x1 || selection.x2 !== newSelection.x2) {
      setSelection(newSelection);
      props.onSelectionChange(newSelection);
    }
  };

  // render component

  const canvasStyles = {
    width: '100%',
    height: '100%',
    margin: '0 auto',
    display: 'block',
  } as React.CSSProperties;

  const hudCanvasStyles = {
    width: '100%',
    height: '100%',
    position: 'absolute',
    top: 0,
    left: 0,
    right: 0,
    bottom: 0,
    zIndex: 1,
  } as React.CSSProperties;

  return (
    <>
      <div style={props.style}>
        <WaveformCanvas
          peaks={props.peaks}
          fillStyle="grey"
          strokeStyle="black"
          style={canvasStyles}
        ></WaveformCanvas>
        <canvas
          ref={hudCanvasRef}
          style={hudCanvasStyles}
          width={CanvasLogicalWidth}
          height={CanvasLogicalHeight}
          onMouseDown={handleMouseDown}
          onMouseMove={handleMouseMove}
          onMouseUp={handleMouseUp}
        ></canvas>
      </div>
    </>
  );
};
