<template>
  <v-line
    ref="line"
    :config="{
      id: id,
      strokeWidth: width,
      opacity: opacity || 0,
      stroke: data.stroke,
      zIndex: zIndex,
      points: flatPositions(points),
      hitStrokeWidth: hitWidth,
    }"
  />

  <v-circle
    v-if="!disablePoint"
    :config="{ ...pointStyle, ...center, opacity: isHover || isPointHover ? 1 : 0 }"
    ref="point"
  />
</template>

<script lang="ts" setup>
import { copy, flatPositions, mergeFuns } from "@/utils/shared";
import { computed, onUnmounted, ref, watch } from "vue";
import { DC } from "@/deconstruction";
import { Line } from "konva/lib/shapes/Line";
import { useShapeDrag } from "@/core/hook/use-transformer";
import { useShapeIsHover, useShapeClick } from "@/core/hook/use-mouse-status";
import { useCursor } from "@/core/hook/use-global-vars";
import { lineCenter, Pos } from "@/utils/math";
import {
  useCustomSnapInfos,
  useGlobalSnapInfos,
  useSnap,
  useSnapResultInfo,
} from "@/core/hook/use-snap";
import { ComponentSnapInfo } from "..";
import { generateSnapInfos } from "../util";
import { getMouseColors } from "@/utils/colors";
import { themeColor } from "@/constant";
import { Circle } from "konva/lib/shapes/Circle";
import {
  useFixedScale,
  useViewer,
  useViewerInvertTransformConfig,
} from "@/core/hook/use-viewer";
import { useMode } from "@/core/hook/use-status";
import { Mode } from "@/constant/mode";
import { LineDataLine } from "../line";

type LData = Pick<LineDataLine, "strokeWidth" | "stroke"> & { fixed?: boolean };

const scale = useFixedScale();
const props = defineProps<{
  data: LData;
  points: Pos[];
  id: string;
  ndx: number;
  closed?: boolean;
  disablePoint?: boolean;
  opacity?: number;
  zIndex?: number;
}>();
const emit = defineEmits<{
  (e: "update:line", data: Pos[]): void;
  (e: "dragend"): void;
  (e: "dragstart"): void;
  (e: "addPoint", pos: Pos): void;
}>();

const line = ref<DC<Line>>();
const offset = useShapeDrag(line);
const viewer = useViewer();
const [isHover] = useShapeIsHover(line);
const mode = useMode();
const drawing = computed(() => mode.include(Mode.draw));
const cursor = useCursor();
watch([isHover, drawing], ([hover, drawing], _, onCleanup) => {
  if (hover && !drawing) {
    onCleanup(cursor.push("./icons/m_move.png"));
  }
});
const points = computed(() => {
  return [props.points[props.ndx], props.points[(props.ndx + 1) % props.points.length]];
});

const infos = useCustomSnapInfos();
const addedInfos = [] as ComponentSnapInfo[];
const clearInfos = () => {
  addedInfos.forEach(infos.remove);
};
const dragStartHandler = () => {
  viewer.disabled.value = true;
  clearInfos();
  const ndx = props.ndx;
  const geos = [
    props.points.slice(Number(ndx === props.points.length - 1), ndx),
    props.points.slice(ndx + 2, props.points.length),
  ];
  if (ndx > 0 && ndx < props.points.length - 2) {
    geos.push([props.points[ndx - 1], props.points[ndx + 2]]);
  }
  geos.forEach((geo) => {
    const snapInfos = generateSnapInfos(geo, true, true, true);
    snapInfos.forEach((item) => {
      infos.add(item);
      addedInfos.push(item);
    });
  });
};
const snapInfos = useGlobalSnapInfos();
const refSnapInfos = computed(() => {
  if (!props.id) {
    return snapInfos.value;
  } else {
    return snapInfos.value.filter((p) => !("id" in p) || p.id !== props.id);
  }
});
const snap = useSnap(refSnapInfos);

let init: Pos[];
let onUmHooks: (() => void)[] = [];
const resultInfo = useSnapResultInfo();
watch(offset, (offset, oldOffsert) => {
  snap.clear();
  if (!oldOffsert) {
    emit("dragstart");
    init = copy(points.value);
    dragStartHandler();
    onUmHooks.push(() => {
      clearInfos();
      emit("dragend");
      viewer.disabled.value = false;
      resultInfo.clear();
    });
  }
  if (offset) {
    const current = init.map((p) => ({
      x: p.x + offset.x,
      y: p.y + offset.y,
    }));
    const refSnapInfos = generateSnapInfos(current, true, true);
    const transform = snap.move(refSnapInfos);
    emit("update:line", transform ? current.map((p) => transform.point(p)) : current);
  } else {
    mergeFuns(onUmHooks)();
    onUmHooks.length = 0;
  }
});

onUnmounted(() => mergeFuns(onUmHooks)());

const point = ref<DC<Circle>>();
const [isPointHover] = useShapeIsHover(point);
let addCursorPop: () => void;
watch(isPointHover, (hover, _, onCleanup) => {
  if (hover) {
    let pop: (() => void) | null = cursor.push("./icons/m_add.png");
    addCursorPop = () => {
      pop && pop();
      pop = null;
    };
    onCleanup(addCursorPop);
  }
});
useShapeClick(point, () => {
  emit("addPoint", { ...center.value });
  addCursorPop();
});
const center = computed(() => lineCenter(points.value));
const width = computed(() =>
  props.data.fixed ? props.data.strokeWidth * scale.value : props.data.strokeWidth
);
const hitWidth = computed(() => {
  const hitWidth = Math.max(props.data.strokeWidth || 0, 10);
  return props.data.fixed ? hitWidth * scale.value : hitWidth;
});
const pointStyle = computed(() => {
  const color = getMouseColors(props.data.stroke || themeColor);
  const size = (width.value || 1) + 6 || 5;
  return {
    radius: size / 2,
    fill: "#fff",
    strokeWidth: size / 4,
    stroke: color.pub,
  };
});

defineExpose({
  get shape() {
    return line.value;
  },
});
</script>
