/** @jsx jsx */
import { jsx } from '@emotion/core';
import { useCallback, useEffect, useState, useRef, useContext } from 'react';
import styled from '@emotion/styled';
import useWaveformRenderFunction from '../../hooks/useWaveformRenderFunction';
import { ScreenConfigurationContext } from '../../hooks/useScreenConfiguration';
import { gray100 } from '../../styles/colors_v2';
import { meterWidth } from '../../styles/dimensions';
import { TimelineViewControllerContext } from '../../hooks/useTimelineViewController';
import useAnimationFrame from '../../hooks/useAnimationFrame';

const BufferCanvas = styled.canvas`
  height: 100%;
  width: 100%;
  background-color: ${gray100};
`;

interface WaveformProps {
  height: number;
  audioBuffers: AudioBuffer[];
  fillColor: string;
  strokeColor: string;
  selectionStart: number | null;
  selectionEnd: number | null;
}

export default ({
  height,
  audioBuffers,
  selectionStart,
  selectionEnd,
  fillColor,
  strokeColor
}: WaveformProps) => {
  const { timelineViewportWidth } = useContext(ScreenConfigurationContext);
  const { getCombinedState } = useContext(TimelineViewControllerContext);

  const canvasHeight = height * window.devicePixelRatio;
  const canvasWidth = (timelineViewportWidth - (meterWidth + 4)) * window.devicePixelRatio;
  const [canvas, setCanvas] = useState<HTMLCanvasElement>()
  const render = useWaveformRenderFunction(canvas, canvasWidth, canvasHeight, audioBuffers, getCombinedState, fillColor, strokeColor);
  const lastRenderProps = useRef<[typeof selectionStart, typeof selectionEnd]>([null, null]);

  const renderLastProps = useCallback(
    () => {
      if (canvas && render) {
        render(...lastRenderProps.current);
      }
    },
    [canvas, render]
  );

  useEffect(
    () => {
      if (canvas) {
        const onResize = () => renderLastProps();
        window.addEventListener('resize', onResize);
        return () => window.removeEventListener('resize', onResize);
      }
    },
    [renderLastProps, canvas]
  );

  const frameCallback = useCallback(
    () => {
      render && render(selectionStart, selectionEnd);
      lastRenderProps.current = [selectionStart, selectionEnd];
    },
    [render, selectionStart, selectionEnd]
  );

  useAnimationFrame(frameCallback);

  const receiveCanvas = useCallback(
    (newCanvas: HTMLCanvasElement | null) => {
      if (newCanvas) {
        setCanvas(newCanvas || undefined);
      }
    },
    [setCanvas]
  );

  return audioBuffers
    ? <BufferCanvas ref={receiveCanvas} height={canvasHeight} width={canvasWidth} />
    : <div>Loading...</div>;
}
