import { memo, useEffect, useState, useCallback, useRef } from 'react';
import { ffmpegExtractWindow } from '../util/constants';
import { RenderableWaveform } from '../types';


const BigWaveform = memo(({ waveforms, relevantTime, playing, durationSafe, zoom, seekRel }: {
  waveforms: RenderableWaveform[], relevantTime: number, playing: boolean, durationSafe: number, zoom: number, seekRel: (a: number) => void,
}) => {
  const windowSize = ffmpegExtractWindow * 2;
  const windowStart = Math.max(0, relevantTime - windowSize);
  const windowEnd = relevantTime + windowSize;
  const filtered = waveforms.filter((waveform) => waveform.from >= windowStart && waveform.to <= windowEnd);

  const scaleFactor = zoom;

  const [smoothTimeRaw, setSmoothTime] = useState<number | undefined>(relevantTime);

  const smoothTime = smoothTimeRaw ?? relevantTime;

  const mouseDownRef = useRef<{ relevantTime: number, x }>();
  const containerRef = useRef<HTMLDivElement>(null);

  const getRect = useCallback(() => containerRef.current!.getBoundingClientRect(), []);

  const handleMouseDown = useCallback((e) => {
    const rect = e.target.getBoundingClientRect();
    const x = e.clientX - rect.left;

    mouseDownRef.current = { relevantTime, x };
    e.preventDefault();
  }, [relevantTime]);

  const scaleToTime = useCallback((v) => (((v) / getRect().width) * windowSize) / zoom, [getRect, windowSize, zoom]);

  const handleMouseMove = useCallback((e) => {
    if (mouseDownRef.current == null) return;

    seekRel(-scaleToTime(e.movementX));

    e.preventDefault();
  }, [scaleToTime, seekRel]);

  const handleWheel = useCallback((e) => {
    seekRel(scaleToTime(e.deltaX));
  }, [scaleToTime, seekRel]);

  const handleMouseUp = useCallback((e) => {
    if (!mouseDownRef.current) return;
    mouseDownRef.current = undefined;
    e.preventDefault();
  }, []);


  useEffect(() => {
    const startTime = Date.now();

    if (playing) {
      let raf;
      // eslint-disable-next-line no-inner-declarations
      function render() {
        raf = window.requestAnimationFrame(() => {
          setSmoothTime(relevantTime + (Date.now() - startTime) / 1000);
          render();
        });
      }

      render();
      return () => window.cancelAnimationFrame(raf);
    }

    setSmoothTime(undefined);

    return undefined;
  }, [relevantTime, playing]);

  return (
    // eslint-disable-next-line jsx-a11y/no-static-element-interactions
    <div
      ref={containerRef}
      style={{ height: '100%', width: '100%', position: 'relative', cursor: 'grab' }}
      onMouseDown={handleMouseDown}
      onMouseUp={handleMouseUp}
      onMouseLeave={handleMouseUp}
      onMouseMove={handleMouseMove}
      onWheel={handleWheel}
    >
      {filtered.map((waveform) => {
        const left = 0.5 + ((waveform.from - smoothTime) / windowSize) * scaleFactor;
        const width = ((waveform.to - waveform.from) / windowSize) * scaleFactor;
        const leftPercent = `${left * 100}%`;
        const widthPercent = `${width * 100}%`;

        return (
          <img
            key={`${waveform.from}-${waveform.to}`}
            src={waveform.url}
            draggable={false}
            alt=""
            style={{
              pointerEvents: 'none',
              backgroundColor: 'var(--gray3)',
              position: 'absolute',
              height: '100%',
              width: widthPercent,
              left: leftPercent,
              borderLeft: waveform.from === 0 ? '1px solid var(--gray11)' : undefined,
              borderRight: waveform.to >= durationSafe ? '1px solid var(--gray11)' : undefined,
            }}
          />
        );
      })}

      <div style={{ pointerEvents: 'none', position: 'absolute', height: '100%', backgroundColor: 'var(--red11)', width: 1, left: '50%', top: 0 }} />
    </div>
  );
});

export default BigWaveform;
