<template>
  <div class="stage-wrapper" ref="stageWrapperRef">
    <div
      class="stage"
      ref="stageRef"
      :style="getStyles.stageStyle"
    >
      <div ref="tipRef" class="tip-txt" :style="getStyles.tipStyle">请在画布内放置组件，超出的内容无法显示</div>
      <div
        ref="canvasRef"
        id="canvasContainer"
        ondragover="return false"
        :style="getStyles.canvasStyle"
        @drop="handleDrop"
      >
        <ComponentWrapper 
          v-for="item in projectStore.elements"
          v-show="item.visible"
          :component-data="item"
          :key="item.key"
          :style="{zIndex: item.zIndex}"
        />
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted, onBeforeUnmount, computed, nextTick, watch, defineExpose } from "vue";
import type { Ref } from "vue";
import { useStageStore } from "@/store/modules/stage";
import { useProjectStore } from "@/store/modules/project";
import { useScroll } from "@vueuse/core";
import ComponentWrapper from "./ComponentWrapper.vue";
import { useAcionStore } from "@/store/modules/action";
import { uuid } from "@/utils";

const stageWrapperRef: Ref<HTMLElement | null> = ref(null);
const stageRef: Ref<HTMLElement | null> = ref(null);
const canvasRef: Ref<HTMLElement | null> = ref(null);
const stageStore = useStageStore();
const projectStore = useProjectStore();
const actionStore = useAcionStore();

const STAGE_SCALE = 3;
const getStyles = computed(() => {
  const { width = 1280, height = 720 } = projectStore.projectInfo || {};
  // 视窗宽高
  const clientWidth = stageWrapperRef.value!?.clientWidth || 0;
  const clientHeight = stageWrapperRef.value?.clientHeight || 0;
  // 可滚动距离 至少2倍窗口大小
  const scrollWidth = width * STAGE_SCALE > clientWidth *2 ? width * STAGE_SCALE : clientWidth *2;
  const scrollHeight = height * STAGE_SCALE > clientHeight *2 ? height * STAGE_SCALE : clientHeight *2;

  // 计算居中偏移量
  const canvasOffsetX = -(width - (width * stageStore.scale)) / 2 + (clientWidth - width * stageStore.scale) / 2;
  const canvasOffsetY = -(height - (height * stageStore.scale)) / 2 + (clientHeight - height * stageStore.scale) / 2;
  // 距离左边 = (可滚动距离 - 视窗宽度) / 2 + 缩放实际偏移量
  const canvasLeft = (scrollWidth - clientWidth) / 2 + canvasOffsetX;
  // 距离顶边 = (可滚动距离 - 视窗高度) / 2 + 缩放实际偏移量
  const canvasTop = (scrollHeight - clientHeight) / 2 + canvasOffsetY;

  const tipOffsetX = (clientWidth - (width * stageStore.scale)) / 2;
  const tipOffsetY = (clientHeight - (height * stageStore.scale)) / 2 - 20;
  const tipLeft = (scrollWidth - clientWidth) / 2 + tipOffsetX;
  const tipTop = (scrollHeight - clientHeight) / 2 + tipOffsetY;

  stageStore.setSize(width, height);
  stageStore.setOriginPoint(tipLeft, tipTop + 20);
  stageStore.setViewportSize(clientWidth, clientHeight);
  stageStore.setStageWaraaperSize(scrollWidth, scrollHeight);

  const { background } = projectStore.currentPage || {};
  const canvasBackground: Record<string, string | undefined> = {};
  if(background?.type === 'color') {
    canvasBackground['background-color'] = background.color;
  } else if(background?.type === 'image') {
    canvasBackground['background-image'] = `url(${background.image})`;
    // todo 背景填充方式
  }

  return {
    // 舞台样式
    stageStyle: {
      width: `${width * STAGE_SCALE}px`,
      height: `${height * STAGE_SCALE}px`,
    },
    // 画布样式
    canvasStyle: {
      width: `${width}px`,
      height: `${height}px`,
      'transform-origin': '50% 50%',
      transform: `scale(${stageStore.scale})`,
      left: `${canvasLeft}px`,
      top: `${canvasTop}px`,
      border: '1px solid #ddd',
      ...canvasBackground
    },
    // 提示样式
    tipStyle: {
      left: `${tipLeft}px`,
      top: `${tipTop}px`
    }
  }
});

useScroll(stageWrapperRef, {
  throttle: 10,
  onScroll: () => {
    const scrollTop = stageWrapperRef.value!.scrollTop;
    const scrollLeft = stageWrapperRef.value!.scrollLeft;
    stageStore.setScroll(scrollLeft, scrollTop);
  }
});

/* 设置缩放倍数 */
const initScale = () => {
  // 4为滚动条宽度 40为左右上下间隔20px
  const windowWidth = stageWrapperRef.value!.clientWidth - 4 - 40;
  const windowHeight = stageWrapperRef.value!.clientHeight - 4 - 40;

  const { width = 1280, height = 720 } = projectStore.projectInfo || {};
  let scale;
  let maxScale;
  if (windowHeight > windowWidth) {
    scale = (windowWidth / width).toFixed(2) as unknown as number;
    maxScale = (windowHeight / height).toFixed(2) as unknown as number;
  } else {
    scale = (windowHeight / height).toFixed(2) as unknown as number;
    maxScale = (windowWidth / width).toFixed(2) as unknown as number;
  }

  const result = scale > maxScale ? maxScale : scale;
  stageStore.setScale(result > 0.1 ? result : 0.1);
};

/* 设置舞台位置-默认居中 */
const initStagePosition = async () => {
  await nextTick();
  const scrollWidth = stageWrapperRef.value!.scrollWidth;
  const scrollHeight = stageWrapperRef.value!.scrollHeight;
  const clientWidth = stageWrapperRef.value!.clientWidth;
  const clientHeight = stageWrapperRef.value!.clientHeight;

  const centerX = (scrollWidth - clientWidth) / 2;
  const centerY = (scrollHeight - clientHeight) / 2;
  stageStore.setCenterPoint(centerX, centerY);
  stageWrapperRef.value!.scrollTo(centerX, centerY);
};
// 拖拽组件结束 添加组件到指定位置
const handleDrop = (e: DragEvent) => {
  e.preventDefault();
  const { offsetX, offsetY } = e;

  if(projectStore.addCompData) {
    const compData = {
      key: uuid(),
      name: projectStore.addCompData.name,
      componentType: projectStore.addCompData.componentType,
      container: {
        props: {
          x: offsetX,
          y: offsetY,
        }
      }
    };
    projectStore.addElement(compData);
    actionStore.addRecord(); // 记录操作
  }
};

/* 适应大小设置 */
watch(
  () => stageStore.scale,
  (val) => {
    if(!val) {
      initScale();
      initStagePosition();  
    }
  }
);
watch(
  () => [
    projectStore.projectInfo.width,
    projectStore.projectInfo.height
  ],
  () => {
    initScale();
    initStagePosition();  
  }
);

defineExpose({
  getPosition: () => canvasRef.value?.getBoundingClientRect(),
});

onMounted(() => {
  initStagePosition();
  initScale();
  window.addEventListener("resize", initScale);
  window.addEventListener("resize", initStagePosition);
});
onBeforeUnmount(() => {
  window.removeEventListener("resize", initScale);
  window.removeEventListener("resize", initStagePosition);
});
</script>

<style lang="less" scoped>
.stage-wrapper {
  height: calc(100% - 20px);
  width: calc(100% - 20px);
  position: absolute;
  top: 20px;
  left: 20px;
  overflow: auto;
}
.stage {
  position: relative;
}
#canvasContainer {
  position: absolute;
}
.tip-txt {
  position: absolute;
  font-size: 12px;
  line-height: 20px;
  color: #999;
}
</style>
