import React, { CanvasHTMLAttributes, forwardRef, useEffect, useImperativeHandle, useRef } from "react";

import { Player } from 'svga';
import { loadImage, loadSvga } from "../utils/loader.ts";
import { VideoSprite } from "svga/dist/types";

type MergedHTMLAttributes = Omit<
  CanvasHTMLAttributes<HTMLCanvasElement>,
  'onPause'
>;

export type DisplayElement = HTMLImageElement | HTMLCanvasElement | ImageBitmap;

export interface SvgaPlayerRef {
  nativeElement: HTMLCanvasElement;
  getPlayer: () => Player;
  start: () => Player;
  pause: () => Player;
  resume: () => Player;
  stop: () => Player;
  replaceImage: (name: string, img: string | DisplayElement) => Promise<DisplayElement>;
  addImage: (parentName: string, targetName: string, img: string | DisplayElement, width?: number, height?: number) => void;
  removeImage: (targetName: string) => void;
}


export interface SvgaPlayerProps extends MergedHTMLAttributes {
  src: string;

  autoplay?: boolean;
  loop?: number;

  startFrame?: number,
  endFrame?: number,

  onProcess?: (current: number, total: number) => void;
  onStart?: () => void;
  onPause?: () => void;
  onResume?: () => void;
  onStop?: () => void;
  onEnd?: () => void;
}

export const SvgaPlayer = forwardRef<
  SvgaPlayerRef,
  SvgaPlayerProps
>((props, ref) => {
  const {
    className, style,
    onClick,
    src, autoplay = true, loop = true,
    startFrame = 0,
    endFrame = 0,
    onStart, onPause, onResume, onStop, onEnd,
    onProcess,
    ...rest
  } = props;
  const canvasRef = useRef<HTMLCanvasElement>(null);

  const playInfo = useRef<{
    playing: boolean;
    player: Player;
  }>({
    playing: false,
    player: null,
  });

  useImperativeHandle(ref, () => ({
    nativeElement: canvasRef.current,
    getPlayer: () => playInfo.current.player,
    start: () => {
      playInfo.current.player.start();
      return playInfo.current.player;
    },
    pause: () => {
      playInfo.current.player.pause();
      return playInfo.current.player;
    },
    resume: () => {
      playInfo.current.player.resume();
      return playInfo.current.player;
    },
    stop: () => {
      playInfo.current.player.stop();
      return playInfo.current.player;
    },
    async replaceImage(name: string, img: string | DisplayElement): Promise<DisplayElement> {
      const player = playInfo.current.player;
      if (playInfo.current.player && playInfo.current.player.videoEntity) {
        if (typeof img === 'string') {
          img = await loadImage(img)
        }
        // @ts-ignore
        player.bitmapsCache[name] = img
        return img;
      }
    },
    async addImage(parentName: string, targetName: string, img: string | DisplayElement, width?: number, height?: number) {
      const player = playInfo.current.player;
      if (player && player.videoEntity) {
        let videoItem = player.videoEntity
        let parent, target, parentIndex
        videoItem.sprites.forEach((sprite, index) => {
          if (sprite.imageKey === parentName) {
            parent = sprite
            parentIndex = index
          }
          if (sprite.imageKey === targetName) {
            target = sprite
          }
        })
        if (!parent) {
          console.warn(`父节点[${parentName}]不存在`)
          return
        }

        if (target) {
          console.warn(`节点[${targetName}]已存在`)
        } else {
          const imgEl = await this.replaceImage(targetName, img)

          let targetFrames = JSON.parse(JSON.stringify(parent.frames))
          for (let { layout, transform } of targetFrames) {
            layout.width = width || imgEl.width;
            layout.height = height || imgEl.height;

            transform._tx = transform.tx;
            transform._ty = transform.ty;
          }

          let frame = {
            imageKey: targetName,
            frames: targetFrames,
          }
          videoItem.sprites.splice(parentIndex + 1, 0, frame)
        }
      }
    },
    removeImage(targetName: string) {
      const player = playInfo.current.player;
      if (player && player.videoEntity) {
        let videoItem = player.videoEntity
        let target: VideoSprite
        videoItem.sprites.forEach((sprite, index) => {
          if (sprite.imageKey === targetName) {
            target = sprite
            videoItem.sprites.splice(index, 1)
          }
        })
        if (!target) {
          console.warn(`节点[${targetName}]不存在`)
        }
      }
    },

  }));

  useEffect(() => {
    const player = new Player({ container: canvasRef.current });
    playInfo.current.player = player;

    player.onStart = () => onStart && onStart();
    player.onPause = () => onPause && onPause();
    player.onResume = () => onResume && onResume();
    player.onStop = () => onStop && onStop();
    player.onProcess = () => onProcess && onProcess(player.currentFrame, player.totalFrames);
    player.onEnd = () => onEnd && onEnd();

    return () => {
      player.destroy();
    }
  }, []);

  useEffect(() => {
    updateSrc();
  }, [src]);

  const updateSrc = async () => {

    if (!src) return;

    const svga = await loadSvga(src);

    const { player } = playInfo.current;

    player.setConfig({
      loop,
      startFrame,
      endFrame,
    })

    player.clear();
    if (svga) {
      await player.mount(svga);
      if (autoplay) {
        player.start();
      } else {
        console.log(player);
        // @ts-ignore
        player.drawFrame(startFrame);
      }
    }

  }

  return <canvas {...rest} className={className} style={style} onClick={onClick} ref={canvasRef} />
});
