/*
 * Copyright 2025 coze-dev Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
 
/* eslint-disable max-lines */
/* eslint-disable max-params */
import {
  Control,
  controlsUtils,
  type FabricObject,
  type Transform,
  type TPointerEvent,
  type ControlCursorCallback,
} from 'fabric';

import { Snap } from '../typings';
import { snap } from './snap/snap';

const getAngle = (a: number) => {
  if (a >= 360) {
    return getAngle(a - 360);
  }
  if (a < 0) {
    return getAngle(a + 360);
  }
  return a;
};

const svg0 =
  '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="none"><g filter="url(#a)"><mask id="b" width="16" height="16" x="8" y="8" fill="#000" maskUnits="userSpaceOnUse"><path fill="#fff" d="M8 8h16v16H8z"/><path fill-rule="evenodd" d="M12.87 22 10 19.13h1.99v-.659a6.483 6.483 0 0 1 6.482-6.482h.723V10l2.869 2.87-2.87 2.869v-1.99h-.722a4.722 4.722 0 0 0-4.722 4.722v.66h1.989L12.869 22" clip-rule="evenodd"/></mask><path fill="#000" fill-rule="evenodd" d="M12.87 22 10 19.13h1.99v-.659a6.483 6.483 0 0 1 6.482-6.482h.723V10l2.869 2.87-2.87 2.869v-1.99h-.722a4.722 4.722 0 0 0-4.722 4.722v.66h1.989L12.869 22" clip-rule="evenodd"/><path fill="#fff" d="M10 19.13v-.8H8.068l1.366 1.367.566-.566M12.87 22l-.567.566.566.566.566-.566L12.87 22m-.88-2.87v.801h.8v-.8h-.8m6.482-7.141v-.8.8m.723 0v.8h.8v-.8h-.8m0-1.989.565-.566-1.366-1.366V10h.8m2.869 2.87.566.565.566-.566-.566-.566-.566.566m-2.87 2.869h-.8v1.932l1.366-1.366-.566-.566m0-1.99h.8v-.8h-.8v.8m-.722 0v-.8.8m-4.722 5.382h-.8v.8h.8v-.8m1.989 0 .566.566 1.366-1.367h-1.932v.8m-6.305.566 2.87 2.869 1.131-1.132-2.87-2.87-1.13 1.133m2.555-1.367H10v1.601h1.99v-1.6m-.8.142v.659h1.6v-.66h-1.6m7.283-7.284a7.283 7.283 0 0 0-7.283 7.283h1.6a5.682 5.682 0 0 1 5.683-5.682v-1.6m.723 0h-.723v1.601h.723v-1.6m.8.8V10h-1.6v1.989h1.6m-1.366-1.422 2.869 2.87 1.132-1.133-2.87-2.869-1.131 1.132m2.869 1.737-2.87 2.87 1.132 1.132 2.87-2.87-1.132-1.132m-1.503 3.436v-1.99h-1.6v1.99h1.6m-1.523-1.19h.723v-1.6h-.723v1.6m-3.922 3.922a3.922 3.922 0 0 1 3.922-3.921v-1.601a5.522 5.522 0 0 0-5.523 5.522h1.601m0 .66v-.66h-1.6v.66h1.6m1.189-.8h-1.99v1.6h1.99v-1.6m-2.304 4.235 2.87-2.87-1.132-1.131-2.87 2.87 1.132 1.13" mask="url(#b)"/></g><defs><filter id="a" width="18.728" height="18.665" x="6.268" y="7.268" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feColorMatrix in="SourceAlpha" result="hardAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/><feOffset dy="1"/><feGaussianBlur stdDeviation=".9"/><feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.65 0"/><feBlend in2="BackgroundImageFix" result="effect1_dropShadow_184_36430"/><feBlend in="SourceGraphic" in2="effect1_dropShadow_184_36430" result="shape"/></filter></defs></svg>';
const svg90 =
  '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="none"><g filter="url(#a)"><mask id="b" width="16" height="16" x="8" y="8" fill="#000" maskUnits="userSpaceOnUse"><path fill="#fff" d="M8 8h16v16H8z"/><path fill-rule="evenodd" d="M10 12.87 12.87 10v1.99h.658a6.482 6.482 0 0 1 6.482 6.482v.722H22l-2.87 2.87-2.868-2.87h1.989v-.722a4.722 4.722 0 0 0-4.722-4.722h-.659v1.988L10 12.87" clip-rule="evenodd"/></mask><path fill="#000" fill-rule="evenodd" d="M10 12.87 12.87 10v1.99h.658a6.482 6.482 0 0 1 6.482 6.482v.722H22l-2.87 2.87-2.868-2.87h1.989v-.722a4.722 4.722 0 0 0-4.722-4.722h-.659v1.988L10 12.87" clip-rule="evenodd"/><path fill="#fff" d="M12.87 10h.8V8.068l-1.367 1.366.566.566M10 12.87l-.566-.567-.566.566.566.566.566-.566m2.87-.88h-.801v.8h.8v-.8m7.14 7.204h-.8v.8h.8v-.8m1.989 0 .566.566 1.366-1.366H22v.8m-2.87 2.87-.565.565.566.566.566-.566-.566-.566m-2.868-2.87v-.8h-1.932l1.366 1.366.566-.566m1.989 0v.8h.8v-.8h-.8m-5.38-5.443v-.8h-.801v.8h.8m0 1.987-.567.566 1.366 1.366v-1.932h-.8m-.567-6.304-2.869 2.87 1.132 1.131 2.869-2.87-1.132-1.13m1.366 2.556V10h-1.6v1.99h1.6m-.141-.8h-.659v1.6h.659v-1.6m7.283 7.282a7.283 7.283 0 0 0-7.283-7.282v1.6a5.682 5.682 0 0 1 5.682 5.682h1.6m0 .722v-.722h-1.6v.722h1.6m-.8.8h1.988v-1.6H20.01v1.6m1.422-1.366-2.869 2.87 1.132 1.131 2.869-2.869-1.132-1.132m-1.737 2.87-2.87-2.87-1.131 1.132 2.869 2.87 1.132-1.132m-3.435-1.503h1.989v-1.6h-1.99v1.6m1.188-1.523v.722h1.601v-.722h-1.6m-3.921-3.921a3.921 3.921 0 0 1 3.921 3.921h1.601a5.522 5.522 0 0 0-5.522-5.522v1.6m-.659 0h.659v-1.6h-.659v1.6m.8 1.187v-1.987h-1.6v1.987h1.6m-4.235-2.303 2.87 2.87 1.131-1.133-2.87-2.869-1.13 1.132" mask="url(#b)"/></g><defs><filter id="a" width="18.663" height="18.727" x="7.068" y="7.268" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feColorMatrix in="SourceAlpha" result="hardAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/><feOffset dy="1"/><feGaussianBlur stdDeviation=".9"/><feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.65 0"/><feBlend in2="BackgroundImageFix" result="effect1_dropShadow_184_36425"/><feBlend in="SourceGraphic" in2="effect1_dropShadow_184_36425" result="shape"/></filter></defs></svg>';
const svg180 =
  '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="none"><g filter="url(#a)"><mask id="b" width="16" height="16" x="8" y="8" fill="#000" maskUnits="userSpaceOnUse"><path fill="#fff" d="M8 8h16v16H8z"/><path fill-rule="evenodd" d="M19.13 10 22 12.87h-1.99v.659a6.483 6.483 0 0 1-6.482 6.482h-.723V22l-2.869-2.87 2.87-2.869v1.99h.722a4.722 4.722 0 0 0 4.722-4.723v-.659h-1.989L19.131 10" clip-rule="evenodd"/></mask><path fill="#000" fill-rule="evenodd" d="M19.13 10 22 12.87h-1.99v.659a6.483 6.483 0 0 1-6.482 6.482h-.723V22l-2.869-2.87 2.87-2.869v1.99h.722a4.722 4.722 0 0 0 4.722-4.723v-.659h-1.989L19.131 10" clip-rule="evenodd"/><path fill="#fff" d="M22 12.87v.8h1.932l-1.366-1.367-.566.566M19.13 10l.567-.566-.566-.566-.566.566.566.566m.88 2.87v-.801h-.8v.8h.8m0 .659h-.8.8m-7.205 6.482v-.8h-.8v.8h.8m0 1.989-.565.566 1.366 1.366V22h-.8m-2.869-2.87-.566-.565-.566.566.566.566.566-.566m2.87-2.869h.8V14.33l-1.366 1.366.566.566m0 1.99h-.8v.8h.8v-.8m5.444-4.723h-.8.8m0-.659h.8v-.8h-.8v.8m-1.989 0-.566-.566-1.366 1.367h1.932v-.8m6.305-.566-2.87-2.869-1.131 1.132 2.87 2.87 1.13-1.133M20.01 13.67H22v-1.601h-1.99v1.6m.8-.142v-.659h-1.6v.66h1.6m-7.283 7.284a7.283 7.283 0 0 0 7.283-7.284h-1.6a5.682 5.682 0 0 1-5.683 5.683v1.6m-.723 0h.723V19.21h-.723v1.6m-.8-.8V22h1.6V20.01h-1.6m1.366 1.422-2.869-2.87-1.132 1.133 2.87 2.869 1.131-1.132m-2.869-1.737 2.87-2.87-1.132-1.132-2.87 2.87 1.132 1.132m1.503-3.436v1.99h1.6v-1.99h-1.6m1.523 1.19h-.723v1.6h.723v-1.6m3.922-3.923a3.922 3.922 0 0 1-3.922 3.922v1.601a5.522 5.522 0 0 0 5.522-5.522h-1.6m0-.659v.66h1.6v-.66h-1.6m-1.189.8h1.99v-1.6h-1.99v1.6m2.304-4.235-2.87 2.87 1.132 1.131 2.87-2.87-1.132-1.13" mask="url(#b)"/></g><defs><filter id="a" width="18.728" height="18.665" x="7.004" y="8.067" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feColorMatrix in="SourceAlpha" result="hardAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/><feOffset dy="1"/><feGaussianBlur stdDeviation=".9"/><feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.65 0"/><feBlend in2="BackgroundImageFix" result="effect1_dropShadow_184_36440"/><feBlend in="SourceGraphic" in2="effect1_dropShadow_184_36440" result="shape"/></filter></defs></svg>';
const svg270 =
  '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="none"><g filter="url(#a)"><mask id="b" width="16" height="16" x="8" y="8" fill="#000" maskUnits="userSpaceOnUse"><path fill="#fff" d="M8 8h16v16H8z"/><path fill-rule="evenodd" d="M22 19.13 19.13 22v-1.99h-.658a6.483 6.483 0 0 1-6.483-6.483v-.722H10l2.87-2.87 2.869 2.87h-1.99v.722a4.722 4.722 0 0 0 4.723 4.722h.659v-1.988L22 19.131" clip-rule="evenodd"/></mask><path fill="#000" fill-rule="evenodd" d="M22 19.13 19.13 22v-1.99h-.658a6.483 6.483 0 0 1-6.483-6.483v-.722H10l2.87-2.87 2.869 2.87h-1.99v.722a4.722 4.722 0 0 0 4.723 4.722h.659v-1.988L22 19.131" clip-rule="evenodd"/><path fill="#fff" d="M19.13 22h-.8v1.932l1.367-1.366L19.13 22M22 19.13l.566.567.566-.566-.566-.566-.566.566m-2.87.88h.801v-.8h-.8v.8m-7.141-6.483h.8-.8m0-.722h.8v-.8h-.8v.8m-1.989 0-.566-.566-1.366 1.366H10v-.8m2.87-2.87.565-.565-.566-.566-.566.566.566.566m2.869 2.87v.8h1.932l-1.366-1.366-.566.566m-1.99 0v-.8h-.8v.8h.8m0 .722h.801-.8m5.382 4.722v.8h.8v-.8h-.8m0-1.988.566-.566-1.367-1.366v1.932h.8m.566 6.305 2.869-2.87-1.132-1.131-2.87 2.87 1.133 1.13M18.33 20.01V22h1.601v-1.99h-1.6m.142.8h.659v-1.6h-.66v1.6m-7.284-7.283a7.283 7.283 0 0 0 7.284 7.283v-1.6a5.682 5.682 0 0 1-5.683-5.683h-1.6m0-.722v.722h1.601v-.722h-1.6m.8-.8H10v1.6h1.989v-1.6m-1.422 1.366 2.87-2.87-1.133-1.131-2.869 2.869 1.132 1.132m1.737-2.87 2.87 2.87 1.132-1.132-2.87-2.87-1.132 1.133m3.436 1.504h-1.99v1.6h1.99v-1.6m-1.189 1.522v-.722h-1.6v.722h1.6m3.922 3.922a3.922 3.922 0 0 1-3.922-3.922h-1.6a5.522 5.522 0 0 0 5.522 5.523v-1.601m.659 0h-.66v1.6h.66v-1.6m-.8-1.188v1.988h1.6v-1.988h-1.6m4.235 2.304-2.87-2.87-1.131 1.132 2.87 2.87 1.13-1.132" mask="url(#b)"/></g><defs><filter id="a" width="18.664" height="18.727" x="6.268" y="8.005" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feColorMatrix in="SourceAlpha" result="hardAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/><feOffset dy="1"/><feGaussianBlur stdDeviation=".9"/><feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.65 0"/><feBlend in2="BackgroundImageFix" result="effect1_dropShadow_184_36435"/><feBlend in="SourceGraphic" in2="effect1_dropShadow_184_36435" result="shape"/></filter></defs></svg>';

const svg2Base64 = (svg: string) => `data:image/svg+xml;base64,${btoa(svg)}`;

const cursorRotate0 = svg2Base64(svg0);
const cursorRotate90 = svg2Base64(svg90);
const cursorRotate180 = svg2Base64(svg180);
const cursorRotate270 = svg2Base64(svg270);

const getCursor = (angle: number) => {
  const a = getAngle(angle);
  if (a >= 225 && a < 315) {
    return cursorRotate270;
  } else if (a >= 135 && a < 225) {
    return cursorRotate180;
  } else if (a >= 45 && a < 135) {
    return cursorRotate90;
  } else {
    return cursorRotate0;
  }
};

const {
  scalingEqually,
  scaleCursorStyleHandler,
  rotationWithSnapping,
  scalingX,
  scalingY,
} = controlsUtils;

type GetControls = (props?: {
  x?: number;
  y?: number;
  callback?: (data: { element: FabricObject }) => void;
  needResetScaleAndSnap?: boolean;
}) => Control;
/**
 * 直线起点控制点
 */
export const getLineStartControl: GetControls = (props = {}) => {
  const { x, y, callback } = props;
  return new Control({
    x,
    y,
    actionHandler: (e, transformData, _x, _y) => {
      transformData.target.set({
        x1: _x,
        y1: _y,
        x2:
          transformData.lastX +
          transformData.width * (transformData.corner === 'tl' ? 1 : -1),
        y2: transformData.lastY + transformData.height,
      });

      callback?.({ element: transformData.target });
      return true;
    },
    actionName: 'startControl', // 控制点的名称
  });
};

/**
 * 直线终点控制点
 */
export const getLineEndControl: GetControls = (props = {}) => {
  const { x, y, callback } = props;
  return new Control({
    x,
    y,
    actionHandler: (e, transformData, _x, _y) => {
      transformData.target.set({
        x1:
          transformData.lastX -
          transformData.width * (transformData.corner === 'br' ? 1 : -1),
        y1: transformData.lastY - transformData.height,
        x2: _x,
        y2: _y,
      });

      callback?.({ element: transformData.target });
      return true;
    },
    actionName: 'endControl', // 控制点的名称
  });
};

const originData = {
  width: 0,
  height: 0,
  top: 0,
  left: 0,
};

type LeftTopCalcFn = (originData: {
  angle: number;
  originTop: number;
  originLeft: number;
  originWidth: number;
  originHeight: number;
  newWidth: number;
  newHeight: number;
}) => {
  left: number;
  top: number;
};

const scaleToSize = (
  transformData: Transform,
  options?: {
    scaleEqual?: boolean;
    leftTopCalcFn?: LeftTopCalcFn;
  },
) => {
  const { width, height, scaleX, scaleY, strokeWidth, angle } =
    transformData.target;

  let targetWidth = Math.max((width + strokeWidth) * scaleX - strokeWidth, 1);
  let targetHeight = Math.max((height + strokeWidth) * scaleY - strokeWidth, 1);

  if (options?.scaleEqual) {
    if (targetWidth - originData.width > targetHeight - originData.height) {
      targetHeight = (originData.height / originData.width) * targetWidth;
    } else {
      targetWidth = (originData.width / originData.height) * targetHeight;
    }
  }

  let targetLeft = originData.left;
  let targetTop = originData.top;
  if (options?.leftTopCalcFn) {
    const rs = options.leftTopCalcFn({
      angle,
      originTop: originData.top,
      originLeft: originData.left,
      originWidth: originData.width,
      originHeight: originData.height,
      newWidth: targetWidth,
      newHeight: targetHeight,
    });
    targetLeft = rs.left;
    targetTop = rs.top;
  }

  transformData.target.set({
    width: targetWidth,
    height: targetHeight,
    // textBox 特化属性
    customFixedHeight: targetHeight,
    scaleX: 1,
    scaleY: 1,
    top: targetTop,
    left: targetLeft,
  });
};
/**
 * 直接问 GPT：
 一个矩形，宽度 w ，高度 h
 将矩形顺时针旋转角度 a，左上角坐标为 x1 y1
 拉伸矩形左上角，使矩形右下角保持不变，宽度增加到 w1，高度增加到 h1
 求左上角坐标
 */
const calcLeftTopByTopLeft: LeftTopCalcFn = ({
  angle,
  originTop,
  originLeft,
  originWidth,
  originHeight,
  newWidth,
  newHeight,
}) => {
  const anglePI = angle * (Math.PI / 180);
  return {
    left:
      originLeft +
      originWidth * Math.cos(anglePI) -
      originHeight * Math.sin(anglePI) -
      newWidth * Math.cos(anglePI) +
      newHeight * Math.sin(anglePI),
    top:
      originTop +
      originWidth * Math.sin(anglePI) +
      originData.height * Math.cos(anglePI) -
      newWidth * Math.sin(anglePI) -
      newHeight * Math.cos(anglePI),
  };
};

/**
 * 直接问 GPT：
一个矩形，宽度 w ，高度 h
将矩形顺时针旋转角度 a , 左上角坐标为 x1 y1
拉伸矩形右上角，使矩形左下角保持不变，宽度增加到 w1，高度增加到 h1
求左上角坐标
（GPT 给的答案不准确，需要稍微理解下，修改加减）
 */
const calcLeftTopByTopRight: LeftTopCalcFn = ({
  angle,
  originTop,
  originLeft,
  originHeight,
  newHeight,
}) => {
  const anglePI = angle * (Math.PI / 180);
  return {
    left: originLeft - (originHeight - newHeight) * Math.sin(anglePI),
    top: originTop + (originHeight - newHeight) * Math.cos(anglePI),
  };
};

/**
 * 直接问 GPT：
一个矩形，宽度 w ，高度 h
将矩形顺时针旋转角度 a , 左上角坐标为 x1 y1
拉伸矩形左下角，使矩形右上角保持不变，宽度增加到 w1，高度增加到 h1
求左上角坐标

GPT 给的答案不准确，这个比较麻烦，所以写出了每一步的推导过程
 */
const calcLeftTopByBottomLeft: LeftTopCalcFn = ({
  angle,
  originTop,
  originLeft,
  originWidth,
  newWidth,
  newHeight,
}) => {
  // 将角度转换为弧度
  const aRad = (angle * Math.PI) / 180;

  // 计算旋转后的右上角坐标
  const x2 = originLeft + originWidth * Math.cos(aRad);
  const y2 = originTop + originWidth * Math.sin(aRad);

  // 计算拉伸后的左下角坐标
  const x3 = x2 - newHeight * Math.sin(aRad) - newWidth * Math.cos(aRad);
  const y3 = y2 + newHeight * Math.cos(aRad) - newWidth * Math.sin(aRad);

  // 计算拉伸后的左上角坐标
  const x1New = x3 + newHeight * Math.sin(aRad);
  const y1New = y3 - newHeight * Math.cos(aRad);
  return {
    left: x1New,
    top: y1New,
  };
};

const _mouseDownHandler = (
  e: TPointerEvent,
  transformData: Transform,
): boolean => {
  originData.width = transformData.target.width;
  originData.height = transformData.target.height;
  originData.top = transformData.target.top;
  originData.left = transformData.target.left;
  return false;
};

const cursorMap: Record<string, string> = {
  'e-resize': 'ew-resize',
  'w-resize': 'ew-resize',
  'n-resize': 'ns-resize',
  's-resize': 'ns-resize',
  'nw-resize': 'nwse-resize',
  'ne-resize': 'nesw-resize',
  'sw-resize': 'nesw-resize',
  'se-resize': 'nwse-resize',
};
const customCursorStyleHandler: ControlCursorCallback = (a, b, c) => {
  const cursor = scaleCursorStyleHandler(a, b, c);
  return cursorMap[cursor] ?? cursor;
};

const _actionHandler = ({
  e,
  transformData,
  x,
  y,
  needResetScaleAndSnap,
  fn,
  callback,
  snapPosition,
  leftTopCalcFn,
}: {
  e: TPointerEvent;
  transformData: Transform;
  x: number;
  y: number;
  needResetScaleAndSnap?: boolean;
  callback: ((data: { element: FabricObject }) => void) | undefined;
  fn: (
    eventData: TPointerEvent,
    transform: Transform,
    x: number,
    y: number,
  ) => boolean;
  snapPosition: Snap.ControlType;
  leftTopCalcFn?: LeftTopCalcFn;
}) => {
  const rs = fn(
    // 如果使用吸附则禁用默认缩放；否则取反
    { ...e, shiftKey: needResetScaleAndSnap ? true : !e.shiftKey },
    transformData,
    x,
    y,
  );

  if (needResetScaleAndSnap) {
    scaleToSize(transformData, {
      scaleEqual: e.shiftKey,
      leftTopCalcFn,
    });
    snap.resize(transformData.target, snapPosition);
  }
  callback?.({ element: transformData.target });
  return rs;
};

/**
 * 上左
 */
export const getResizeTLControl: GetControls = (props = {}) => {
  const { callback, needResetScaleAndSnap = true } = props;
  return new Control({
    x: -0.5,
    y: -0.5,
    cursorStyleHandler: customCursorStyleHandler,
    actionHandler: (e, transformData, _x, _y) => {
      const rs = _actionHandler({
        e,
        transformData,
        x: _x,
        y: _y,
        needResetScaleAndSnap,
        callback,
        fn: scalingEqually,
        leftTopCalcFn: calcLeftTopByTopLeft,
        snapPosition: Snap.ControlType.TopLeft,
      });
      return rs;
    },
    mouseDownHandler: _mouseDownHandler,
    actionName: 'resizeTLControl', // 控制点的名称
  });
};
/**
 * 上中
 */
export const getResizeMTControl: GetControls = (props = {}) => {
  const { callback, needResetScaleAndSnap = true } = props;
  return new Control({
    x: 0,
    y: -0.5,
    cursorStyleHandler: customCursorStyleHandler,
    actionHandler: (e, transformData, _x, _y) => {
      const rs = _actionHandler({
        e,
        transformData,
        x: _x,
        y: _y,
        needResetScaleAndSnap,
        callback,
        fn: scalingY,
        leftTopCalcFn: calcLeftTopByTopLeft,
        snapPosition: Snap.ControlType.Top,
      });
      return rs;
    },
    mouseDownHandler: _mouseDownHandler,
    actionName: 'resizeMTControl', // 控制点的名称
  });
};

/**
 * 上右
 */
export const getResizeTRControl: GetControls = (props = {}) => {
  const { callback, needResetScaleAndSnap } = props;
  return new Control({
    x: 0.5,
    y: -0.5,
    cursorStyleHandler: customCursorStyleHandler,
    actionHandler: (e, transformData, _x, _y) => {
      const rs = _actionHandler({
        e,
        transformData,
        x: _x,
        y: _y,
        needResetScaleAndSnap,
        callback,
        fn: scalingEqually,
        leftTopCalcFn: calcLeftTopByTopRight,
        snapPosition: Snap.ControlType.TopRight,
      });
      return rs;
    },
    mouseDownHandler: _mouseDownHandler,
    actionName: 'resizeTRControl', // 控制点的名称
  });
};

/**
 * 中左
 */
export const getResizeMLControl: GetControls = (props = {}) => {
  const { callback, needResetScaleAndSnap } = props;
  return new Control({
    x: -0.5,
    y: 0,
    cursorStyleHandler: customCursorStyleHandler,
    actionHandler: (e, transformData, _x, _y) => {
      const rs = _actionHandler({
        e,
        transformData,
        x: _x,
        y: _y,
        needResetScaleAndSnap,
        callback,
        fn: scalingX,
        leftTopCalcFn: calcLeftTopByBottomLeft,
        snapPosition: Snap.ControlType.Left,
      });

      return rs;
    },
    mouseDownHandler: _mouseDownHandler,
    actionName: 'resizeMLControl', // 控制点的名称
  });
};

/**
 * 中右
 */
export const getResizeMRControl: GetControls = (props = {}) => {
  const { callback, needResetScaleAndSnap } = props;
  return new Control({
    x: 0.5,
    y: 0,
    cursorStyleHandler: customCursorStyleHandler,
    actionHandler: (e, transformData, _x, _y) => {
      const rs = _actionHandler({
        e,
        transformData,
        x: _x,
        y: _y,
        needResetScaleAndSnap,
        callback,
        fn: scalingX,
        snapPosition: Snap.ControlType.Right,
      });

      return rs;
    },
    mouseDownHandler: _mouseDownHandler,
    actionName: 'resizeMRControl', // 控制点的名称
  });
};

/**
 * 下左
 */
export const getResizeBLControl: GetControls = (props = {}) => {
  const { callback, needResetScaleAndSnap } = props;
  return new Control({
    x: -0.5,
    y: 0.5,
    cursorStyleHandler: customCursorStyleHandler,
    actionHandler: (e, transformData, _x, _y) => {
      const rs = _actionHandler({
        e,
        transformData,
        x: _x,
        y: _y,
        needResetScaleAndSnap,
        callback,
        fn: scalingEqually,
        leftTopCalcFn: calcLeftTopByBottomLeft,
        snapPosition: Snap.ControlType.BottomLeft,
      });

      return rs;
    },
    mouseDownHandler: _mouseDownHandler,
    actionName: 'resizeBLControl', // 控制点的名称
  });
};

/**
 * 下中
 */
export const getResizeMBControl: GetControls = (props = {}) => {
  const { callback, needResetScaleAndSnap } = props;
  return new Control({
    x: 0,
    y: 0.5,
    cursorStyleHandler: customCursorStyleHandler,
    actionHandler: (e, transformData, _x, _y) => {
      const rs = _actionHandler({
        e,
        transformData,
        x: _x,
        y: _y,
        needResetScaleAndSnap,
        callback,
        fn: scalingY,
        snapPosition: Snap.ControlType.Bottom,
      });

      return rs;
    },
    mouseDownHandler: _mouseDownHandler,
    actionName: 'resizeMBControl', // 控制点的名称
  });
};

/**
 * 下右
 */
export const getResizeBRControl: GetControls = (props = {}) => {
  const { callback, needResetScaleAndSnap } = props;
  return new Control({
    x: 0.5,
    y: 0.5,
    cursorStyleHandler: customCursorStyleHandler,
    actionHandler: (e, transformData, _x, _y) => {
      const rs = _actionHandler({
        e,
        transformData,
        x: _x,
        y: _y,
        needResetScaleAndSnap,
        callback,
        fn: scalingEqually,
        snapPosition: Snap.ControlType.BottomRight,
      });

      return rs;
    },
    mouseDownHandler: _mouseDownHandler,
    actionName: 'resizeBRControl', // 控制点的名称
  });
};

const _getRotateControl =
  ({
    x,
    y,
    offsetY,
    offsetX,
    actionName,
    rotateStaff,
  }: {
    x: number;
    y: number;
    offsetY: -1 | 1;
    offsetX: -1 | 1;
    actionName: string;
    rotateStaff: number;
  }): GetControls =>
  (props = {}) => {
    const { callback } = props;
    // 这个大小，取决于 resize 控制点的大小
    const offset = 12;
    return new Control({
      x,
      y,
      sizeX: 20,
      sizeY: 20,
      offsetY: offsetY * offset,
      offsetX: offsetX * offset,
      // 覆盖旋转控制点渲染，预期不显示，所以啥都没写
      // eslint-disable-next-line @typescript-eslint/no-empty-function
      render: () => {},
      // 只能做到 hover 上时的 cursor，旋转过程中 cursor 无法修改
      cursorStyleHandler: (eventData, control, object) =>
        `url(${getCursor(object.angle + rotateStaff)}) 16 16, crosshair`,

      actionHandler: (e, transformData, _x, _y) => {
        // 旋转吸附，单位：角度 一圈 = 360度
        if (e.shiftKey) {
          transformData.target.set({
            snapAngle: 15,
          });
        } else {
          transformData.target.set({
            snapAngle: undefined,
          });
        }

        const rs = rotationWithSnapping(
          e,
          { ...transformData, originX: 'center', originY: 'center' },
          _x,
          _y,
        );

        // scaleToSize(transformData);

        // transformData.target.canvas?.requestRenderAll();
        callback?.({ element: transformData.target });

        return rs;
      },
      actionName, // 控制点的名称
    });
  };

// 上左旋转点
export const getRotateTLControl: GetControls = (props = {}) =>
  _getRotateControl({
    x: -0.5,
    y: -0.5,
    offsetY: -1,
    offsetX: -1,
    rotateStaff: 0,
    actionName: 'rotateTLControl',
  })(props);

// 上右旋转点
export const getRotateTRControl: GetControls = (props = {}) =>
  _getRotateControl({
    x: 0.5,
    y: -0.5,
    offsetY: -1,
    offsetX: 1,
    rotateStaff: 90,
    actionName: 'rotateTRControl',
  })(props);

// 下右旋转点
export const getRotateBRControl: GetControls = (props = {}) =>
  _getRotateControl({
    x: 0.5,
    y: 0.5,
    offsetY: 1,
    offsetX: 1,
    rotateStaff: 180,
    actionName: 'rotateBRControl',
  })(props);

// 下左旋转点
export const getRotateBLControl: GetControls = (props = {}) =>
  _getRotateControl({
    x: -0.5,
    y: 0.5,
    offsetY: 1,
    offsetX: -1,
    rotateStaff: 270,
    actionName: 'rotateBLControl',
  })(props);
