<template>
  <Teleport :to="`#${DomMountId}`" v-if="stage">
    <transition name="pointer-fade">
      <div
        v-if="pointer"
        :style="{ transform: pointer }"
        :size="8"
        class="propertys-controller"
        ref="layout"
      >
        <ElMenu>
          <ElMenuItem v-for="menu in menus" @click="clickHandler(menu.handler)">
            <span class="menu-item">{{ menu.label }}</span>
          </ElMenuItem>
        </ElMenu>
      </div>
    </transition>
  </Teleport>
</template>

<script lang="ts" setup>
import { computed, nextTick, ref, watch } from "vue";
import { useMouseMenusFilter, useStage } from "../../hook/use-global-vars.ts";
import { useMode } from "../../hook/use-status.ts";
import { DC, EntityShape } from "@/deconstruction.js";
import { useViewerTransformConfig } from "../../hook/use-viewer.ts";
import { Transform } from "konva/lib/Util";
import { DomMountId } from "@/constant/index.ts";
import { ElMenu, ElMenuItem } from "element-plus";
import { Mode } from "@/constant/mode.ts";
import { useGlobalOnlyRightClickShape } from "@/core/hook/use-event.ts";
import { useStore } from "@/core/store/index.ts";

const props = defineProps<{
  target: DC<EntityShape> | undefined;
  data?: Record<string, any>;
  menus: Array<{ icon?: any; label?: string; handler: () => void }>;
}>();
const emit = defineEmits<{
  (e: "show" | "hide"): void;
}>();

const layout = ref<HTMLDivElement>();
const stage = useStage();
const { getFilter } = useMouseMenusFilter();
const store = useStore();
const id = computed(() => props.target?.getNode().id());
const type = computed(() => id.value && store.getType(id.value));
const menus = computed(() => {
  if (id.value && type.value) {
    return getFilter(type.value, id.value)(props.menus);
  } else {
    return props.menus;
  }
});

const show = ref(false);
const { add } = useGlobalOnlyRightClickShape();
const mode = useMode();
watch(
  () => !mode.value.has(Mode.draw) && props.target?.getNode(),
  (shape, _, onCleanup) => {
    if (!shape) return;
    const del = add(
      shape,
      async () => {
        await nextTick();
        show.value = true;
      },
      () => {
        show.value = false;
      }
    );
    onCleanup(del);
  }
);

const clickHandler = (handler: () => void) => {
  handler();
  show.value = false;
};

const move = new Transform();
const pointer = ref<string | null>(null);
const calcPointer = async () => {
  if (!show.value) {
    pointer.value = null;
    return;
  } else if (pointer.value) {
    return;
  }
  const $stage = stage.value!.getStage();
  const mousePosition = $stage.pointerPos;
  if (!mousePosition) {
    pointer.value = null;
    return;
  }
  if (!menus.value.length) {
    return;
  }
  const $shape = props.target!.getNode();
  const shapeRect = $shape.getClientRect();

  const shapeR = shapeRect.x + shapeRect.width;
  const shapeB = shapeRect.y + shapeRect.height;
  let x = Math.min(Math.max(mousePosition.x, shapeRect.x), shapeR);
  let y = Math.min(Math.max(mousePosition.y, shapeRect.y), shapeB);

  move.reset();
  move.translate(x, y);
  pointer.value = `matrix(${move.m.join(",")})`;
  await nextTick();

  const domRect = layout.value!.getBoundingClientRect();
  x = x - domRect.width / 2;
  x = Math.max(x, shapeRect.x);

  if (x + domRect.width > shapeR) {
    x = shapeR - domRect.width;
  }
  if (y + domRect.height > shapeB) {
    y = y - domRect.height;
  }

  x = Math.max(x, 10);
  y = Math.max(y, 10);
  if (x + domRect.width > $stage.width() - 10) {
    x = $stage.width() - domRect.width - 10;
  }
  if (y + domRect.height > $stage.height() - 10) {
    y = $stage.height() - domRect.height - 10;
  }

  move.reset();
  move.translate(x, y);
  pointer.value = `matrix(${move.m.join(",")})`;
};
watch(
  () => !!pointer.value,
  (show) => {
    emit(show ? "show" : "hide");
  }
);

let timeout: any;
const resetPointer = () => {
  if (!show.value) {
    pointer.value = null;
    return;
  } else if (pointer.value) {
    return;
  }
  clearTimeout(timeout);
  timeout = setTimeout(calcPointer, 16);
};

watch(show, resetPointer);
watch(useViewerTransformConfig(), () => {
  pointer.value = null;
  resetPointer();
});
</script>

<style lang="scss" scoped>
.menu-item {
  display: block;
  min-width: 60px;
  margin-left: 10px;
}
</style>

<style lang="scss">
.propertys-controller {
  pointer-events: none;
  position: absolute;
  border-radius: 4px;
  border: 1px solid #e4e7ed;
  background-color: #ffffff;
  left: 0;
  top: 0;
  overflow: hidden;
  color: #303133;
  display: flex;
  flex-direction: column;
  align-items: center;
  box-shadow: 0px 0px 12px rgba(0, 0, 0, 0.12);
  min-width: 10px;
  padding: 5px 0;
  pointer-events: all;
  font-size: 14px;
  border: 1px solid var(--el-border-color-light);
  box-shadow: var(--el-dropdown-menu-box-shadow);
  background-color: var(--el-bg-color-overlay);
  border-radius: var(--el-border-radius-base);
  --el-menu-base-level-padding: 16px;
  --el-menu-item-height: 32px;
  --el-menu-item-font-size: 14px;

  .el-menu-item {
    align-items: center;
    padding: 5px 16px 5px 6px !important;
    color: var(--el-text-color-regular);
  }
}

.pointer-fade-enter-active,
.pointer-fade-leave-active {
  transition: opacity 0.3s ease;
  .item {
    pointer-events: none;
  }
}
.pointer-fade-enter-from,
.pointer-fade-leave-to {
  opacity: 0;
}
</style>
