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

  <template v-for="(raw, rawNdx) in data.content">
    <template v-for="(_, colNdx) in raw">
      <TextDom
        v-if="tableRef?.texts[rawNdx] && tableRef?.texts[rawNdx][colNdx]?.getNode()"
        :shape="tableRef.texts[rawNdx][colNdx].getNode()!"
        @submit="(val) => submitInputHandler(val, rawNdx, colNdx)"
        @show="showInputHandler"
        @hide="quitInputHandler"
      />
    </template>
  </template>
</template>

<script lang="ts" setup>
import TempTable from "./temp-table.vue";
import TextDom from "../share/text-area.vue";
import { TableData, getMouseStyle, defaultStyle, TableCollData } from "./index.ts";
import { PropertyUpdate, Operate } from "../../propertys";
import { useComponentStatus } from "@/core/hook/use-component.ts";
import { Transform } from "konva/lib/Util";
import { MathUtils } from "three";
import {
  useCustomTransformer,
  useGetTransformerOperType,
} from "@/core/hook/use-transformer.ts";
import { copy, getResizeCorsur } from "@/utils/shared.ts";
import { computed, ref, watch, watchEffect } from "vue";
import { Group } from "konva/lib/Group";
import { DC } from "@/deconstruction.js";
import { Minus, Plus } from "@element-plus/icons-vue";
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 { numEq, 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
  ) => {
    rawBorderNdx: number;
    colBorderNdx: number;
    rawNdx: number;
    colNdx: number;
  };
};
const tableRef = ref<TableRef>();
const status = useMouseShapeStatus(computed(() => tableRef.value?.shape));
let inter: Pick<
  ReturnType<TableRef["getMouseIntersect"]>,
  "rawBorderNdx" | "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.rawBorderNdx)) {
            onCleanup(
              cursor.push(getResizeCorsur(!~inter.rawBorderNdx, $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.rawBorderNdx)) {
      const pop = cursor.push(
        getResizeCorsur(!~inter.rawBorderNdx, shape.value?.getNode().rotation())
      );
      const isActive = shapesStatus.actives.includes($shape);
      if (isActive) {
        shapesStatus.actives = shapesStatus.actives.filter((s) => s !== $shape);
      }
      onCleanup(() => {
        inter = null;
        pop();
        // if (isActive) {
        //   shapesStatus.actives = [...shapesStatus.actives, $shape];
        // }
      });
    }
  },
  { flush: "post" }
);

const getColMinSize = (col: TableCollData) => {
  const minw = (col.padding || 0) * 2 + (col.fontSize || 12) + 4;
  const minh = (col.padding || 0) * 2 + (col.fontSize || 12) + 4;
  return { w: minw, h: minh };
};

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.rawBorderNdx)) {
    const oldData = copy(data);
    data.height = dec.scaleY * initData.height;
    data.width = dec.scaleX * initData.width;

    let w = 0;
    let h = 0;
    data.content.forEach((raw, rndx) => {
      raw.forEach((col, cndx) => {
        const initCol = initData.content[rndx][cndx];
        const minSize = getColMinSize(initCol);
        col.width = Math.max(minSize.w, data.width * (initCol.width / initData.width));
        col.height = Math.max(
          minSize.h,
          data.height * (initCol.height / initData.height)
        );
        if (rndx === 0) {
          w += col.width;
        }
        if (cndx === 0) {
          h += col.height;
        }
      });
    });
    const eqW = numEq(w, data.width);
    const eqH = numEq(h, data.height);

    if (!eqW || !eqH) {
      const type = getOperType();
      if (type) {
        Object.assign(data, oldData);
      } else {
        data.width = w;
        data.height = h;
        const initDec = new Transform(initData.mat).decompose();
        data.mat = new Transform()
          .translate(eqW ? dec.x : initDec.x, eqH ? dec.y : initDec.y)
          .rotate(MathUtils.degToRad(dec.rotation)).m;
      }
    } else {
      data.mat = new Transform()
        .translate(dec.x, dec.y)
        .rotate(MathUtils.degToRad(dec.rotation)).m;
    }
    return data;
  }
  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.rawBorderNdx) {
    const ndxRaw = inter.rawBorderNdx - 1;
    const ndx = ndxRaw === -1 ? 0 : ndxRaw;
    let offset = ndxRaw === -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 (ndxRaw === -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 ndxRaw = inter.colBorderNdx - 1;
    const ndx = ndxRaw === -1 ? 0 : ndxRaw;
    let offset = ndxRaw === -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 (ndxRaw === -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 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);
  }
};

let initData: TableData;
const { shape, tData, data, operateMenus, describes } = useComponentStatus<
  Group,
  TableData
>({
  emit,
  props,
  getMouseStyle,
  defaultStyle,
  alignment: (data, mat) => {
    const tf = shape.value!.getNode().getTransform();
    mat.multiply(tf);
    matToData(data, mat);
    sync(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,
        };
      },
      beforeHandler(data, mat) {
        return matToData(copy(data), mat, initData);
      },
      handler(data, mat) {
        matToData(data, mat, initData);
        // sync(data);
      },
      callback(data) {
        callback();
        sync(data);
      },
    });
  },
  copyHandler(mat, data) {
    const tf = shape.value!.getNode().getTransform();
    mat.multiply(tf);
    return matToData({ ...data }, mat);
  },
  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((raw) => {
      raw.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((raw) => {
      raw.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 = [
    {
      icon: Plus,
      label: "插入行",
      handler: () => {
        const tempRaw = data.value.content[config.rawNdx];
        data.value.content.splice(
          config.rawNdx,
          0,
          tempRaw.map((item) => ({ ...item, content: "" }))
        );
        data.value.height += tempRaw[0].height;
        sync(data.value);
        emit("updateShape", { ...data.value });
      },
    },
    {
      icon: Minus,
      label: "删除行",
      handler: () => {
        const tempRaw = data.value.content[config.rawNdx];
        data.value.content.splice(config.rawNdx, 1);
        data.value.height -= tempRaw[0].height;
        if (data.value.content.length === 0) {
          emit("delShape");
        } else {
          sync(data.value);
          emit("updateShape", data.value);
        }
      },
    },
    {
      icon: Plus,
      label: "插入列",
      handler: () => {
        const tempCol = data.value.content[0][config.colNdx];
        for (let i = 0; i < data.value.content.length; i++) {
          const raw = data.value.content[i];
          raw.splice(config.colNdx, 0, { ...tempCol, content: "" });
        }
        data.value.width += tempCol.width;
        sync(data.value);
        emit("updateShape", data.value);
      },
    },
    {
      icon: Minus,
      label: "删除列",
      handler: () => {
        const tempCol = data.value.content[0][config.colNdx];
        for (let i = 0; i < data.value.content.length; i++) {
          const raw = data.value.content[i];
          raw.splice(config.colNdx, 1);
        }
        data.value.width -= tempCol.width;
        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 showText = ref(false);
const showInputHandler = () => {
  showText.value = true;
  const ndx = shapesStatus.actives.indexOf(shape.value!.getNode());
  if (~ndx) {
    shapesStatus.actives = shapesStatus.actives.filter(
      (v) => v !== shape.value!.getNode()
    );
  }
};
const quitInputHandler = () => {
  showText.value = false;
};
const submitInputHandler = (val: string, rawNdx: number, colNdx: number) => {
  quitInputHandler();
  data.value.content[rawNdx][colNdx].content = val;
  emit("updateShape", data.value);
};
</script>
