import React, { useCallback, useContext, useEffect, useRef, useState } from 'react';
import { fabric } from 'fabric';
import { FabricTexture } from 'fabric-texture';
import Skew from 'fabric-texture/modes/skew'
import Perspective from 'fabric-texture/modes/perspective'
import Warp from 'fabric-texture/modes/warp'
import { Canvas, Code, TextSegment, TextSwitch } from 'docs/components';
import type { CanvasHandlers } from 'docs/components/canvas';
import { DocsContext } from 'docs/docs';
import styles from './style.less';
/**
 * 分割点计算策略
 * @enum {string}
 */
enum ModeStrategy {
  /** 扭曲 */
  WARP = 'Warp',
  /** 倾斜 */
  SKEW = 'Skew',
  /** 透视 */
  PERSPECTIVE = 'Perspective',
}

/**
 * 内置交互模式
 */
const modes = {
  /** 扭曲模式 */
  Warp,
  /** 倾斜模式 */
  Skew,
  /** 透视模式 */
  Perspective,
};

const InteractiveCanvas = () => {
  const { placeholder } = useContext(DocsContext);

  const _canvasRef = useRef<CanvasHandlers>(null);
  const _fabricTextureRef = useRef<FabricTexture>();

  const [mode, setSplitStrategy] = useState<ModeStrategy>(ModeStrategy.PERSPECTIVE);
  const [showGrid, setShowGrid] = useState<boolean>(false);

  const render = useCallback(async () => {
    const canvas = _canvasRef.current?.getCanvas();
    if (!canvas) return;

    const width = canvas.getWidth();
    const height = canvas.getHeight();

    if (!placeholder) return;

    // 缩放以控制在合理渲染区域内
    const scale =
      Math.min(200, canvas.getWidth()) /
      Math.min(placeholder.naturalWidth, placeholder.naturalHeight);

    // 创建元素
    const object = new fabric.Image(placeholder, {
      left: width / 2,
      top: height / 2,
      scaleX: scale,
      scaleY: scale,
      originX: 'center',
      originY: 'center',
    });

    // 如果已存在则先结束上一轮编辑
    if (_fabricTextureRef.current) {
      _fabricTextureRef.current.leaveEditing();
    }

    // 进入交互态
    const fabricTexture = new FabricTexture(canvas, { history: true });
    fabricTexture.enterEditing(object, null, new modes[mode]());
    _fabricTextureRef.current = fabricTexture;

    // 隐藏源元素
    object.set({ visible: false });
    canvas.renderAll();
  }, [placeholder]);

  const handleChangeStrategy = useCallback((mode: ModeStrategy) => {
    const fabricTexture = _fabricTextureRef.current;
    if (!fabricTexture) return;

    const texture = fabricTexture.texture;
    if (!texture) return;

    const object = fabricTexture.target!;
    fabricTexture.leaveEditing();
    fabricTexture.enterEditing(object, null, new modes[mode]());
    setSplitStrategy(mode as ModeStrategy);
  }, []);

  const handleToggleGridShow = useCallback((showGrid: boolean) => {
    const fabricTexture = _fabricTextureRef.current;
    if (!fabricTexture) return;

    const texture = fabricTexture.texture;
    if (!texture) return;

    texture.setRenderOptions({ showGrid });
    fabricTexture.requestRender(true);
    setShowGrid(showGrid);
  }, []);

  useEffect(() => {
    render();
  }, [render]);

  return (
    <div className={styles.demo}>
      <Canvas className={styles.canvas} ref={_canvasRef} />
      <Code
        className={styles.code}
        lineCount={20}
        link="sections/quick-start/codes/interactive-canvas-demo.ts"
      >
        {(key) => {
          switch (key) {
            case 'mode':
              return (
                <TextSegment
                  value={mode}
                  options={[
                    { hoverLabel: '倾斜', label: ModeStrategy.SKEW, value: ModeStrategy.SKEW },
                    {
                      hoverLabel: '透视',
                      label: ModeStrategy.PERSPECTIVE,
                      value: ModeStrategy.PERSPECTIVE,
                    },
                    { hoverLabel: '扭曲', label: ModeStrategy.WARP, value: ModeStrategy.WARP },
                  ]}
                  onChange={handleChangeStrategy as (mode: unknown) => void}
                />
              );
            case 'show-grid':
              return <TextSwitch value={showGrid} onChange={handleToggleGridShow} />;
          }
        }}
      </Code>
    </div>
  );
};

export default InteractiveCanvas;
