<template>
  <div>
    <div
      class="vue-crop-layout"
      ref="layoutRef"
      :style="{ width: sWidth + 'px', height: sHeight + 'px' }"
    >
      <VueCropper
        class="cropper-cls"
        ref="cropperRef"
        :img="url"
        :outputSize="1"
        canScale
        autoCrop
        centerBox
        :fixed="!!fixed"
        :fixedNumber="fixed"
      />
    </div>
    <div class="control">
      <el-button type="primary" @click="cropperRef.rotateRight()"> 旋转 </el-button>
    </div>
  </div>
</template>

<script setup lang="ts">
import "vue-cropper/dist/index.css";
import { ref, computed } from "vue";
import { VueCropper } from "vue-cropper";
import { QuiskExpose } from "@/helper/mount";
import { getDomMatrix } from "@/util";
import { inverse, multiply, positionTransform, rotateZ, translate } from "@/util/mt4";

type CropperProps = {
  img: Blob | string;
  fixed: [number, number];
};
const props = defineProps<CropperProps>();

// 样式控制
const sWidth = 500;
const sHeight = (props.fixed[1] / props.fixed[0]) * sWidth;

const realImage = new Image();
const url = computed(() => {
  const url = typeof props.img === "string" ? props.img : URL.createObjectURL(props.img);
  realImage.src = url;
  return url;
});
const layoutRef = ref<HTMLDivElement>();
const cropperRef = ref<any>();

const getDrawInfo = () => {
  const imgDom = layoutRef.value?.querySelector(".cropper-box-canvas") as HTMLElement;
  const cropDom = layoutRef.value?.querySelector(".cropper-crop-box") as HTMLElement;

  const imgMatrix = getDomMatrix(imgDom);
  const cropMatrix = getDomMatrix(cropDom);
  const cropSize = [cropperRef.value.cropW, cropperRef.value.cropH];

  // 屏幕位置
  const cropBox = [
    positionTransform([-cropSize[0] / 2, -cropSize[1] / 2, 0], cropMatrix),
    positionTransform([cropSize[0] / 2, cropSize[1] / 2, 0], cropMatrix),
  ];

  const scale = [
    realImage.width / imgDom.offsetWidth,
    realImage.height / imgDom.offsetHeight,
  ];

  const invImageMatrix = inverse(imgMatrix);
  const lt = positionTransform(cropBox[0], invImageMatrix);
  const rb = positionTransform(cropBox[1], invImageMatrix);
  const imgBound = [
    lt[0] * scale[0] + realImage.width / 2,
    lt[1] * scale[1] + realImage.height / 2,
    rb[0] * scale[0] + realImage.width / 2,
    rb[1] * scale[1] + realImage.height / 2,
  ];

  const realBound = {
    left: Math.round(imgBound[0]),
    top: Math.round(imgBound[1]),
    right: Math.round(imgBound[2]),
    bottom: Math.round(imgBound[3]),
  };
  // 旋转过
  if (realBound.left > realBound.right) {
    [realBound.left, realBound.right] = [realBound.right, realBound.left];
  }
  if (realBound.top > realBound.bottom) {
    [realBound.top, realBound.bottom] = [realBound.bottom, realBound.top];
  }

  return {
    ...realBound,
    rotate: (cropperRef.value.rotate * Math.PI) / 2,
  };
};

const clipImage = () => {
  const data = getDrawInfo();
  const canvas = document.createElement("canvas");
  const ctx = canvas.getContext("2d")!;
  const w = data.right - data.left;
  const h = data.bottom - data.top;

  const boxMatrix = multiply(
    translate(-w / 2, -h / 2, 0),
    rotateZ(data.rotate),
    translate(w / 2, h / 2, 0)
  );
  const start = positionTransform([0, 0, 0], boxMatrix);
  const end = positionTransform([w, h, 0], boxMatrix);
  const cw = (canvas.width = Math.abs(end[0] - start[0]));
  const ch = (canvas.height = Math.abs(end[1] - start[1]));
  ctx.translate(cw / 2, ch / 2);
  ctx.rotate(data.rotate);
  ctx.drawImage(realImage, data.left, data.top, w, h, -w / 2, -h / 2, w, h);

  return new Promise<Blob | null>((resolve) => canvas.toBlob(resolve));
};

defineExpose<QuiskExpose>({
  submit: clipImage,
});
</script>

<style lang="scss" scoped>
.vue-crop-layout {
  width: 100%;
}
.control {
  margin-top: 20px;
  text-align: center;
}
</style>

<style lang="scss">
.vue-crop-layout {
  .cropper-view-box {
    outline-color: var(--el-color-primary);
  }
}
</style>
