import { useRef, useEffect } from 'react';
import PropTypes from 'prop-types';
import { useResizeObserver } from "../../../hooks/useResizeObserver/index.js";
import { canvas } from "../../../utils/canvas/index.js";
import classes from './styles.module.scss';

/**
 * An HTML5 canvas element with some helpful support for drawing animated
 * frames into the canvas.
 */
const HtmlCanvas2d = function HtmlCanvas2d(props) {
  const {
    drawFrame,
    isAnimated
  } = props;
  const ref = useRef();
  const [resizeRef, [width, height]] = useResizeObserver();
  useEffect(() => {
    const resolution = window.devicePixelRatio;
    ref.current.width = width * resolution;
    ref.current.height = height * resolution;
  }, [width, height]);
  useEffect(() => {
    const context = ref.current.getContext('2d');
    const cvs = canvas({
      context,
      devicePixelRatio: window.devicePixelRatio
    });
    if (!drawFrame || !context) return undefined;
    if (!isAnimated) {
      drawFrame({
        context,
        canvas: cvs
      });
      return undefined;
    }
    let animationStart;
    let timer;
    const animate = ts => {
      if (animationStart === undefined) {
        animationStart = ts;
      }
      if (ts - animationStart < 1000 / 25) {
        timer = window.requestAnimationFrame(animate);
        return;
      }
      const dt = (ts - animationStart) / 1000;
      animationStart = ts;
      const isFinished = drawFrame({
        context,
        dt,
        canvas: cvs
      });
      if (!isFinished) {
        timer = window.requestAnimationFrame(animate);
      }
    };
    timer = window.requestAnimationFrame(animate);
    return () => {
      window.cancelAnimationFrame(timer);
    };
  }, [drawFrame, isAnimated, width, height]);
  return <div ref={resizeRef} className={classes.container}>
      <canvas ref={ref} className={classes.canvas} />
    </div>;
};
HtmlCanvas2d.defaultProps = {
  drawFrame: undefined,
  isAnimated: false
};
HtmlCanvas2d.propTypes = {
  /**
   * A callback to draw a single frame into the canvas. This gets called with
   * a canvas object (from the canvas utilities), the raw 2d context of the
   * canvas node itself, and `dt` when `isAnimated` is set to true, giving you
   * the elapsed time in the animation since the last frame. When `isAnimated`
   * is set to `true`, the `drawFrame` function can return `false` to indicate
   * that the animation should continue and `true` to indicate that the
   * animation is complete and we can stop calling `drawFrame` for now.
   */
  drawFrame: PropTypes.func,
  /**
   * Whether to keep calling `drawFrame` in an animated loop until it returns
   * `true`.
   */
  isAnimated: PropTypes.bool
};
export default HtmlCanvas2d;