<template>
  <TempTable
    :data="tData"
    ref="tableRef"
    :id="data.id"
    @update-content="submitInputHandler"
    editer
  />
  <PropertyUpdate
    :describes="describes"
    :data="data"
    :target="shape"
    @change="emit('updateShape', { ...data })"
    @delete="emit('delShape')"
  />
  <Operate
    :target="shape"
    :menus="operateMenus"
    @show="menuShowHandler"
    @hide="menuHideHandler"
  />
</template>

<script lang="ts" setup>
import TempTable from "./temp-table.vue";
import {
  TableData,
  getMouseStyle,
  defaultStyle,
  matResponse,
  getColMinSize,
} from "./index.ts";
import { PropertyUpdate, Operate } from "../../html-mount/propertys/index.ts";
import { useComponentStatus } from "@/core/hook/use-component.ts";
import { Transform } from "konva/lib/Util";
import { MathUtils } from "three";
import {
  useCustomTransformer,
  useGetTransformerOperType,
  useTransformer,
} from "@/core/hook/use-transformer.ts";
import { copy, getResizeCorsur } from "@/utils/shared.ts";
import { computed, nextTick, ref, watch, watchEffect } from "vue";
import { Group } from "konva/lib/Group";
import { DC } from "@/deconstruction.js";
import { Rect } from "konva/lib/shapes/Rect";
import { setShapeTransform } from "@/utils/shape.ts";
import { Text } from "konva/lib/shapes/Text";
import {
  useMouseShapesStatus,
  useMouseShapeStatus,
} from "@/core/hook/use-mouse-status.ts";
import { useCursor, usePointerPos } from "@/core/hook/use-global-vars.ts";
import { Pos } from "@/utils/math.ts";

const props = defineProps<{ data: TableData }>();
const emit = defineEmits<{
  (e: "updateShape", value: TableData): void;
  (e: "addShape", value: TableData): void;
  (e: "delShape"): void;
}>();
type TableRef = {
  shape: DC<Group>;
  texts: DC<Text>[][];
  getMouseIntersect: (
    pos?: Pos
  ) => {
    rowBorderNdx: number;
    colBorderNdx: number;
    rowNdx: number;
    colNdx: number;
  };
};
const tableRef = ref<TableRef>();
const status = useMouseShapeStatus(computed(() => tableRef.value?.shape));
let inter: Pick<
  ReturnType<TableRef["getMouseIntersect"]>,
  "rowBorderNdx" | "colBorderNdx"
> | null = null;
const pos = usePointerPos();
const cursor = useCursor();
const shapesStatus = useMouseShapesStatus();
watch(
  () => status.value.hover && !status.value.press,
  (hover, _, onCleanup) => {
    if (!hover) return;
    onCleanup(
      watch(
        () => pos.value && tableRef.value?.getMouseIntersect(pos.value),
        (inter, _, onCleanup) => {
          const $shape = shape.value?.getNode();
          if ($shape && inter && (~inter.colBorderNdx || ~inter.rowBorderNdx)) {
            onCleanup(
              cursor.push(getResizeCorsur(!~inter.rowBorderNdx, $shape.rotation()))
            );
          }
        },
        { immediate: true, flush: "post" }
      )
    );
  },
  { flush: "post" }
);
watch(
  () => status.value.press,
  (press, _, onCleanup) => {
    if (!press) return;
    inter = tableRef.value?.getMouseIntersect() || null;
    const $shape = shape.value?.getNode();
    if ($shape && inter && (~inter.colBorderNdx || ~inter.rowBorderNdx)) {
      const pop = cursor.push(
        getResizeCorsur(!~inter.rowBorderNdx, shape.value?.getNode().rotation())
      );
      const isActive = shapesStatus.actives.includes($shape);
      if (isActive) {
        shapesStatus.actives = shapesStatus.actives.filter((s) => s !== $shape);
      }
      onCleanup(() => {
        inter = null;
        pop();
      });
    }
  },
  { flush: "post" }
);

const getOperType = useGetTransformerOperType();
const matToData = (data: TableData, mat: Transform, initData?: TableData) => {
  if (!initData) {
    initData = copy(data);
  }
  const dec = mat.decompose();
  if (!inter || (!~inter.colBorderNdx && !~inter.rowBorderNdx)) {
    return matResponse({ data, mat, operType: getOperType()! }, initData);
  }

  const initDec = new Transform(initData.mat).decompose();
  const move = new Transform().rotate(MathUtils.degToRad(-dec.rotation)).point({
    x: dec.x - initDec.x,
    y: dec.y - initDec.y,
  });
  if (~inter.rowBorderNdx) {
    const ndxrow = inter.rowBorderNdx - 1;
    const ndx = ndxrow === -1 ? 0 : ndxrow;
    let offset = ndxrow === -1 ? -move.y : move.y;
    const minSize = getColMinSize(data.content[ndx][0]);
    const h = Math.max(minSize.h, initData.content[ndx][0].height + offset);
    offset = h - initData.content[ndx][0].height;

    data.content[ndx].forEach(
      (col, colNdx) => (col.height = initData.content[ndx][colNdx].height + offset)
    );
    data.height = initData.height + offset;

    if (ndxrow === -1) {
      const translate = new Transform()
        .rotate(MathUtils.degToRad(dec.rotation))
        .point({ x: 0, y: -offset });
      data.mat = new Transform()
        .translate(translate.x, translate.y)
        .multiply(new Transform(initData.mat)).m;
    }
  } else {
    const ndxrow = inter.colBorderNdx - 1;
    const ndx = ndxrow === -1 ? 0 : ndxrow;
    let offset = ndxrow === -1 ? -move.x : move.x;
    const minSize = getColMinSize(data.content[0][ndx]);
    const w = Math.max(minSize.w, initData.content[0][ndx].width + offset);
    offset = w - initData.content[0][ndx].width;
    data.content.forEach((row, rowNdx) => {
      row[ndx].width = initData.content[rowNdx][ndx].width + offset;
    });
    data.width = initData.width + offset;
    if (ndxrow === -1) {
      const translate = new Transform()
        .rotate(MathUtils.degToRad(dec.rotation))
        .point({ x: -offset, y: 0 });
      data.mat = new Transform()
        .translate(translate.x, translate.y)
        .multiply(new Transform(initData.mat)).m;
    }
  }
  return data;
};

let repShape: Rect | null;
const transformer = useTransformer();
const sync = (data: TableData) => {
  if (repShape) {
    repShape.width(data.width);
    repShape.height(data.height);
    const tf = new Transform(data.mat);
    setShapeTransform(repShape, tf);
    initData = copy(data);
    transformer.forceUpdate();
  }
};

let initData: TableData;
const { shape, tData, data, operateMenus, describes } = useComponentStatus<
  Group,
  TableData
>({
  emit,
  props,
  getMouseStyle,
  defaultStyle,
  // alignment: (data, mat) => {
  //   matResponse({ data, mat, increment: true });
  //   sync(data);
  //   return data;
  // },
  transformType: "custom",
  customTransform(callback, shape, data) {
    useCustomTransformer(shape, data, {
      openSnap: true,
      transformerConfig: { flipEnabled: false },
      getRepShape() {
        repShape = new Rect();
        sync(data.value);
        return {
          shape: repShape as any,
          update(data) {
            sync(data);
          },
        };
      },
      beforeHandler(data, mat) {
        return matToData(copy(data), mat, initData);
      },
      handler(data, mat) {
        matToData(data, mat, initData);
      },
      callback(data) {
        callback();
        nextTick(() => shape.value?.getNode().fire("bound-change"));
      },
    });
  },
  copyHandler(mat, data) {
    return matResponse({ data, mat, increment: true });
  },
  propertys: [
    "fill",
    "stroke",
    "fontColor",
    "strokeWidth",
    "fontSize",
    // "ref",
    "opacity",
    //  "zIndex"
    "align",
    "fontStyle",
  ],
});

watchEffect((onCleanup) => {
  shape.value = tableRef.value?.shape;
  onCleanup(() => (shape.value = undefined));
});

watch(
  () => data.value.fontSize,
  () => {
    data.value.content.forEach((row) => {
      row.forEach((col) => {
        col.fontSize = data.value.fontSize;
      });
    });
    const $shape = shape.value!.getNode();
    data.value = matToData(data.value, $shape.getTransform());

    sync(data.value);
    $shape.fire("bound-change");
  },
  { flush: "sync" }
);

watchEffect(
  () => {
    data.value.content.forEach((row) => {
      row.forEach((col) => {
        col.fontColor = data.value.fontColor;
        col.fontStyle = data.value.fontStyle;
        col.align = data.value.align;
      });
    });
  },
  { flush: "sync" }
);

let addMenu: any;
const menuShowHandler = () => {
  const config = tableRef.value!.getMouseIntersect();
  addMenu = [];
  if (!data.value.notaddRow) {
    addMenu.push({
      label: "插入行",
      handler: () => {
        const temprow = data.value.content[config.rowNdx];
        data.value.content.splice(
          config.rowNdx,
          0,
          temprow.map((item) => ({ ...item, content: "" }))
        );
        data.value.height += temprow[0].height;
        sync(data.value);
        nextTick(() => shape.value?.getNode().fire("bound-change"));
        emit("updateShape", { ...data.value });
      },
    });
  }
  const canDelRaw = data.value.content[config.rowNdx].every((item) => !item.notdel);
  if (canDelRaw) {
    addMenu.push({
      label: "删除行",
      handler: () => {
        const temprow = data.value.content[config.rowNdx];
        data.value.content.splice(config.rowNdx, 1);
        data.value.height -= temprow[0].height;
        nextTick(() => shape.value?.getNode().fire("bound-change"));
        if (data.value.content.length === 0) {
          emit("delShape");
        } else {
          sync(data.value);
          emit("updateShape", data.value);
        }
      },
    });
  }

  if (!data.value.notaddCol) {
    addMenu.push({
      label: "插入列",
      handler: () => {
        const tempCol = data.value.content[0][config.colNdx];
        for (let i = 0; i < data.value.content.length; i++) {
          const row = data.value.content[i];
          row.splice(config.colNdx, 0, { ...tempCol, content: "" });
        }
        data.value.width += tempCol.width;
        nextTick(() => shape.value?.getNode().fire("bound-change"));
        sync(data.value);
        emit("updateShape", data.value);
      },
    });
  }

  const canDelCol = data.value.content.every((items) => !items[config.colNdx].notdel);
  if (canDelCol) {
    addMenu.push({
      label: "删除列",
      handler: () => {
        const tempCol = data.value.content[0][config.colNdx];
        for (let i = 0; i < data.value.content.length; i++) {
          const row = data.value.content[i];
          row.splice(config.colNdx, 1);
        }
        data.value.width -= tempCol.width;
        nextTick(() => shape.value?.getNode().fire("bound-change"));
        if (data.value.content[0].length === 0) {
          emit("delShape");
        } else {
          sync(data.value);
          emit("updateShape", data.value);
        }
      },
    });
  }
  operateMenus.unshift(...addMenu);
};
const menuHideHandler = () => {
  for (let i = 0; i < addMenu.length; i++) {
    const ndx = operateMenus.indexOf(addMenu[i]);
    if (ndx !== -1) {
      operateMenus.splice(ndx, 1);
    }
  }
  addMenu = [];
};

const submitInputHandler = (playData: {
  val: string;
  rowNdx: number;
  colNdx: number;
}) => {
  data.value.content[playData.rowNdx][playData.colNdx].content = playData.val;
  emit("updateShape", data.value);
};
</script>
