<template>
  <div class="konva-map">
    <div class="map-container">
      <div v-moveable:1 v-if="!isKnovaDestroy">
        <v-stage ref="stageAll" :config="stageConfig" @click="handleStageClick">
          <v-layer ref="layer">
            <v-image :config="bgConfig" v-show="bgImgUrl" />
            <v-group
              v-for="camera in cameras"
              :key="camera.id"
              :id="camera.id"
              :config="camera.groupConfig"
              @click="handleCameraClick(camera)"
              @mouseover="(e) => handleMouseOver(e)"
              @mouseleave="handleMouseLeave()"
              @dragstart="handleDragStart()"
            >
              <v-image :config="camera.config" />
              <v-image v-if="camera.isDefault" ref="defaultIcon" :config="defaultIconConfig" />
            </v-group>
            <v-transformer :config="transformerConfig" ref="transformer" />
          </v-layer>
        </v-stage>
        <div
          v-show="defaultShow"
          class="opt-container"
          :style="{ position: 'absolute', left: posX + 'px', top: posY + 'px' }"
        >
          <div class="opt-item" :class="{ disabled: disabledSet }" @click="setDefaultCamera">设为默认相机</div>
          <div class="opt-item" @click="previewCamera">预览相机</div>
          <div class="opt-item" @click="handleDeleteCamera">删除标签</div>
        </div>

        <CameraPreview
          v-if="isShow"
          :last-pos-x="posX!"
          :last-pos-y="posY!"
          :video-url="videoUrl"
          @close="closePreview"
        />

        <DefaultTip
          v-show="selectCameraId"
          :position="pos"
          :is-default="isDefaultCamera"
          :camera-info="cameraInfo"
          :style="{ position: 'absolute', left: posTipX + 'px', top: posTipY + 'px' }"
        />
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
  import { ref, onMounted, watch, computed } from 'vue';
  import { ElMessage } from 'element-plus';
  import DefaultTip from '../components/DefaultTip.vue';
  import cameraImgSrc from '@/assets/camera/cameraImg.png';
  import favoritesImgSrc from '@/assets/camera/favorites.png';
  import { TipPositionEnum, camerasGroupType, cameraInfoType } from '../type';
  import { cloneDeep } from 'lodash-es';
  import { updateMinMapViewLayoutApi } from '@/api/scene/scene';
  import CameraPreview from './CameraPreview.vue';
  import { ShopMapCamera } from '@/types/scene/type';
  import { openMessageBox } from '@/views/system-config/business-scene/components/MessageBox';

  const emit = defineEmits(['changeDefaultCamera', 'sendCameraId', 'change']);
  interface MapConfigType {
    width: number;
    height: number;
  }
  const props = defineProps<{
    filterData: ShopMapCamera[];
    cameraList: ShopMapCamera[];
    mapConfig: MapConfigType;
    isKnovaDestroy: boolean;
  }>();
  const transformerScale = computed(() => {
    if (stageConfig.value.width === 0 || stageConfig.value.height === 0) return 1;
    return Math.min(props.mapConfig.width / stageConfig.value.width, props.mapConfig.height / stageConfig.value.height);
  });
  const camImg = new Image();

  const stageConfig = ref({
    width: 0,
    height: 0,
  });

  const bgImg = new Image();
  const favoritesImg = new Image();
  favoritesImg.src = favoritesImgSrc;

  //右键点击是否出现
  const defaultShow = ref<boolean>(false);
  const posX = ref<number>();
  const posY = ref<number>();
  const posTipX = ref<number>();
  const posTipY = ref<number>();
  const pos = ref(TipPositionEnum.TOP);
  //上一次点击的相机
  const lastClickedGroupId = ref<string | null>(null);
  const lastClickedVideoUrl = ref<string | null>(null);

  const bgImgUrl = ref<string | null>('');
  const cameraInfo = ref<cameraInfoType>();
  const cameras = ref<camerasGroupType[]>([]);
  //默认相机id
  const defaultCameraId = ref('');
  const isDefaultCamera = ref(false);
  const disabledSet = ref(false);
  const videoUrl = ref<string>('');

  const cameraIconSize = { width: 52, height: 52 };

  //标签
  const layer = ref();
  const transformer = ref();
  const defaultIcon = ref();

  function addIntegrationState(data: camerasGroupType[], updateData: ShopMapCamera[]) {
    for (let i = 0; i < data.length; i++) {
      const camera = data[i];
      const matchedCamera = updateData.find((item) => item.code === camera.id);
      if (matchedCamera) {
        camera.groupConfig.visible = matchedCamera.integrationState === 0 ? true : false;
      }
    }
  }

  const bgConfig = ref({
    x: 0,
    y: 0,
    id: 'bgImg',
    width: bgImg.width,
    height: bgImg.height,
    image: bgImg,
    name: 'bgImg',
    backgroundSize: 'cover',
  });

  //待修改
  const transformerConfig = ref({
    keepRatio: true,
    rotateAnchorOffset: 30,
    enabledAnchors: ['top-left', 'top-right', 'bottom-left', 'bottom-right'],
  });

  //背景大小变换
  const resizeContainer = (width, height) => {
    stageConfig.value.width = width;
    stageConfig.value.height = height;
    // layer.value.getNode().draw();
  };

  const defaultIconConfig = ref({
    x: 18,
    y: -16,
    width: 16,
    height: 16,
    id: 'defaultIcon',
    image: favoritesImg,
  });
  //选中相机的id
  const selectCameraId = ref();
  //取消
  const handleMouseOver = (e) => {
    document.oncontextmenu = () => {
      return false;
    };

    const group = e.target.parent;
    selectCameraId.value = group.id();
    let defaultNode;
    const stage = transformer.value.getNode().getStage();
    const groups = stage.find('.group');
    const selectedCameraAttrs = groups.find((item) => item.attrs.id === selectCameraId.value).attrs;
    cameraInfo.value = {
      x: selectedCameraAttrs.x,
      y: selectedCameraAttrs.y,
      sizeX:
        selectCameraId.value === defaultCameraId.value
          ? cameraIconSize.width + defaultIconConfig.value.width
          : cameraIconSize.width,
      sizeY: cameraIconSize.height,
      scaleX: selectedCameraAttrs.scaleX,
      scaleY: selectedCameraAttrs.scaleY,
      rotation: selectedCameraAttrs.rotation,
    };
    if (selectCameraId.value === defaultCameraId.value) {
      isDefaultCamera.value = true;
      defaultNode = stage.findOne('#defaultIcon');
    } else {
      defaultNode = stage.findOne(`#${selectCameraId.value}`);
    }
    const tipPosition = defaultNode.absolutePosition();
    posTipX.value = Number(tipPosition?.x.toFixed(2)) || 0;
    posTipY.value = Number(tipPosition?.y.toFixed(2)) || 0;
    const angle = group.rotation() >= 0 ? group.rotation() : group.rotation() + 360;
    if (angle >= 30) {
      if (angle <= 150) {
        pos.value = TipPositionEnum.RIGHT;
        posTipX.value += 26;
        posTipY.value -= 17;
      } else if (angle <= 210) {
        pos.value = TipPositionEnum.BOTTOM;
        posTipY.value += 26;
        posTipX.value -= 50;
      } else {
        pos.value = TipPositionEnum.LEFT;
        posTipX.value -= 121;
        posTipY.value -= 25;
      }
    } else {
      posTipY.value -= 80;
      posTipX.value -= 43;
    }
  };
  // 还原浏览器默认鼠标事件
  const handleMouseLeave = () => {
    document.oncontextmenu = () => {
      return true;
    };
    isDefaultCamera.value = false;
    selectCameraId.value = null;
  };

  const handleDragStart = () => {
    isDefaultCamera.value = false;
    emit('change', true);
  };

  const handleStageClick = (e: any) => {
    defaultShow.value = false;
    disabledSet.value = false;
    document.oncontextmenu = () => {
      return false;
    };
    const parent = e.target.parent;
    //判断是否点击相机组
    if (parent.hasName('group')) {
      lastClickedGroupId.value = parent.id();
      emit('change', true);
      // 判断是否为右键点击
      if (e.evt.button === 2) {
        lastClickedGroupId.value = parent.id();
        const clickedVideoUrl = props.filterData.find((item) => item.code === lastClickedGroupId.value);

        const videoUrl = clickedVideoUrl?.pushStreamDTO?.videoUrls?.pushstreamIp;
        if (videoUrl) {
          lastClickedVideoUrl.value = videoUrl;
        } else {
          ElMessage.error('视频地址不存在');
          return;
        }
        isShow.value = false;
        posX.value = e.evt.offsetX + 20;
        posY.value = e.evt.offsetY;
        disabledSet.value = defaultCameraId.value === parent.id();
        defaultShow.value = true;
      }
    } else {
      lastClickedGroupId.value = null;
      //取消transformer选择
      const transformerNode = transformer.value.getNode();
      transformerNode.nodes([]);
    }
  };

  const handleCameraClick = (camera) => {
    isDefaultCamera.value = false;
    const transformerNode = transformer.value.getNode();
    const stage = transformerNode.getStage();
    const cameraNode = stage.findOne('#' + camera.id);
    // 将变换器附加到点击的相机
    transformerNode.nodes([cameraNode]);
    transformerNode.moveToTop();
    emit('change', true);
  };
  /**
   * @description 根据相机Icon的大小动态配置相机Icon的拖拽边界
   * @param configWidth 相机Icon的宽度
   * @param configHeight 相机Icon的高度
   * @author chauncey
   */
  function dymamicdragBoundFunc() {
    return function (pos: { x: number; y: number }) {
      // 如果没有发生缩放，则返回的scaleX为undefined，需要设置为默认缩放为1
      const scaleX = this.attrs.scaleX || 1;
      const scaleY = this.attrs.scaleY || 1;
      const rotation = ((this.attrs.rotation || 0) * Math.PI) / 180; // 转换为弧度
      const width = cameraIconSize.width;
      const height = cameraIconSize.height;

      // 计算旋转和缩放后的四个角点相对于左上角的偏移
      const points = [
        { x: 0, y: 0 }, // 左上
        { x: width * scaleX, y: 0 }, // 右上
        { x: width * scaleX, y: height * scaleY }, // 右下
        { x: 0, y: height * scaleY }, // 左下
      ].map((p) => {
        // 先应用旋转变换
        const rotatedX = p.x * Math.cos(rotation) - p.y * Math.sin(rotation);
        const rotatedY = p.x * Math.sin(rotation) + p.y * Math.cos(rotation);
        return {
          x: rotatedX,
          y: rotatedY,
        };
      });

      // 计算变换后的边界框
      const minX = Math.min(...points.map((p) => p.x));
      const maxX = Math.max(...points.map((p) => p.x));
      const minY = Math.min(...points.map((p) => p.y));
      const maxY = Math.max(...points.map((p) => p.y));

      // 计算边界框的尺寸
      const boxWidth = maxX - minX;
      const boxHeight = maxY - minY;

      // 计算相对于变换原点的偏移
      const offsetX = -minX;
      const offsetY = -minY;

      // 限制位置，确保完全在画布内
      const restrictedX = Math.max(0 + offsetX, Math.min(bgConfig.value.width - boxWidth + offsetX, pos.x));
      const restrictedY = Math.max(0 + offsetY, Math.min(bgConfig.value.height - boxHeight + offsetY, pos.y));

      return {
        x: restrictedX,
        y: restrictedY,
      };
    };
  }
  /**
   * @description 根据index动态配置相机出现位置
   * @param index
   * @author chauncey
   */

  function dymamicCameraConfig(index: number) {
    const maxColumn = Math.floor((bgConfig.value.height - 16) / cameraIconSize.height);
    const column = Math.floor(index / maxColumn);
    const row = index % maxColumn;
    const x = column * cameraIconSize.width;
    const y = row * cameraIconSize.height + 16;
    const dragBoundFunc = dymamicdragBoundFunc();
    return {
      x,
      y,
      draggable: true,
      name: 'group',
      dragBoundFunc,
    };
  }

  //添加相机
  const addCamera = (id: string, index: number) => {
    const existingCamera = cameras.value.find((camera) => camera.id === id);
    if (existingCamera) return;
    const config = {
      width: cameraIconSize.width,
      height: cameraIconSize.height,
      image: camImg,
      name: 'image',
      id: id,
    };
    const groupConfig = dymamicCameraConfig(index);
    const cameraDetail = {
      id,
      groupConfig,
      config,
      isDefault: false,
    };
    cameras.value.push(cameraDetail);
    //当只有一个相机时，自动设置默认相机
    if (cameras.value.length === 1) {
      cameras.value[0].isDefault = true;
      defaultCameraId.value = id;
    }
    emit('change', true);
  };

  //设置默认相机
  const setDefaultCamera = () => {
    //选中的相机id号
    defaultCameraId.value = lastClickedGroupId.value!;
    cameras.value.forEach((item) => {
      if (item.id === defaultCameraId.value) {
        item.isDefault = true;
      } else {
        item.isDefault = false;
      }
    });
    const transformerNode = transformer.value.getNode();
    const stage = transformerNode.getStage();
    transformerNode.nodes([]);
    const cameraNode = stage.findOne('#' + defaultCameraId.value);
    // 将变换器附加到点击的相机
    transformerNode.nodes([cameraNode]);
    defaultShow.value = false;
    emit('change', true);
  };

  const isShow = ref<boolean>(false);

  const previewCamera = () => {
    videoUrl.value = lastClickedVideoUrl.value!;
    if (!videoUrl.value) {
      ElMessage.warning('视频流不存在!');
      return;
    }
    isShow.value = true;
    defaultShow.value = false;
  };

  const closePreview = () => {
    isShow.value = false;
    defaultShow.value = false;
  };

  watch(
    lastClickedGroupId,
    () => {
      emit('changeDefaultCamera', lastClickedGroupId.value);
    },
    { immediate: true },
  );

  watch(
    () => cameras.value,
    () => {
      emit('sendCameraId', cameras.value);
    },
    { immediate: true, deep: true },
  );

  watch(
    () => props.filterData,
    () => {
      addIntegrationState(cameras.value, props.filterData);
    },
    { immediate: true, deep: true },
  );

  const clearBg = () => {
    bgImg.src = null as any as string;
    emit('change', true);
    layer.value.getNode().draw();
  };

  //添加背景
  const addBg = (imgBg) => {
    return new Promise((resolve) => {
      bgImgUrl.value = imgBg;
      bgImg.src = imgBg;
      bgImg.onload = () => {
        bgConfig.value.width = bgImg.width;
        bgConfig.value.height = bgImg.height;
        resizeContainer(bgImg.width, bgImg.height);
        resolve(null);
      };
    });
  };

  //根据id找到对应的相机
  const findCamera = (cameraId: string) => {
    const camera = cameras.value.find((item) => item.id === cameraId);
    return camera;
  };

  //保存布局
  const saveLayout = () => {
    const stage = transformer.value.getNode().getStage();
    const groups = stage.find('.group');
    const tempList = cloneDeep(cameras.value);
    const camerasLists = tempList.map((item, index) => {
      item.groupConfig.x = groups[index].attrs.x;
      item.groupConfig.y = groups[index].attrs.y;
      item.groupConfig.rotation = groups[index].attrs.rotation || 0;
      item.groupConfig.scaleX = groups[index].attrs.scaleX || 1;
      item.groupConfig.scaleY = groups[index].attrs.scaleY || 1;
      return item;
    });
    const layout = {
      stageConfig: stageConfig.value,
      bgConfig: bgConfig.value,
      bgImgUrl: bgImgUrl.value,
      defaultCameraId: defaultCameraId.value,
      cameraList: camerasLists,
    };
    emit('change', false);
    return JSON.stringify(layout);
  };
  const deleteCameraFn = () => {
    const index = cameras.value.findIndex((item) => item.id === lastClickedGroupId.value);
    index >= 0 && cameras.value.splice(index, 1);
    lastClickedGroupId.value = '';
    //取消transformer
    const transformerNode = transformer.value.getNode();
    transformerNode.nodes([]);
    if (!defaultShow.value) return;
    defaultShow.value = false;
  };
  const handleDeleteCamera = () => {
    if (lastClickedGroupId.value === defaultCameraId.value) {
      openMessageBox('警告', '此相机为默认相机，您确认要删除此相机?', deleteCameraFn);
    } else {
      deleteCameraFn();
      ElMessage.success('删除成功！');
    }
    emit('change', true);
  };

  //重置
  const resetMap = () => {
    bgImgUrl.value = null;
    clearBg();
    cameras.value = [];
    lastClickedGroupId.value = null;
    lastClickedGroupId.value = null;
    videoUrl.value = '';
    isShow.value = false;
    defaultCameraId.value = '';
  };

  /** 导入布局json */
  const createMap = (layout, selectId) => {
    addBg(layout.bgImgUrl).then((_res) => {
      const unExitList = [] as any[];
      stageConfig.value = layout.stageConfig;
      defaultCameraId.value = layout.defaultCameraId;
      layout.cameraList = layout.cameraList
        ?.map((item) => {
          item.config.image = camImg;
          const width = item.config.width;
          const height = item.config.height;
          const ratio = width / height;
          /** 老版本的相机icon比例在1.4左右，新icon已经更新，比例是1:1，为了避免新icon渲染出来变形，需要调整icon大小
           * 如果图标的比例在该范围，那么会将它设置为新图标的icon大小 */
          if (1.3 < ratio && ratio < 1.5) {
            item.config.width = cameraIconSize.width;
            item.config.height = cameraIconSize.height;
          }

          return item;
        })
        ?.filter((cam) => {
          if (props.cameraList.findIndex((x) => x.code === cam.id) >= 0) {
            return true;
          } else {
            unExitList.push(cam.code);
            return false;
          }
        });
      if (unExitList.length > 0) {
        ElMessage.warning('部分相机不存在，已为您删除!');

        const layoutNew = cloneDeep(layout);
        updateMinMapViewLayoutApi({
          layout: JSON.stringify({ ...layoutNew, isUploadBg: true }),
          targetId: String(selectId),
          viewType: 2,
        });
      }
      cameras.value = layout.cameraList.map((camera) => {
        return {
          ...camera,
          groupConfig: {
            ...camera.groupConfig,
            dragBoundFunc: dymamicdragBoundFunc(),
          },
        };
      });
    });
  };

  defineExpose({
    addBg,
    createMap,
    handleCameraClick,
    findCamera,
    resetMap,
    addCamera,
    resizeContainer,
    saveLayout,
  });
  onMounted(() => {
    camImg.src = cameraImgSrc;
  });
</script>

<style scoped lang="scss">
  .konva-map {
    background: #0009;
    scale: v-bind(transformerScale);
  }

  .map-container {
    width: 100%;
    height: 100%;
    overflow: hidden;
  }

  .opt-container {
    width: 160px;
    padding: 10px;
    border-radius: 5px;
    background-color: #ffffff;
    box-shadow: 5px 5px 5px #a3a5a5;
  }

  .opt-item {
    height: 30px;
    font-size: 14px;
    color: #404040;
    display: flex;
    justify-content: flex-start;
    align-items: center;
    padding-left: 8px;
    border-radius: 3px;
    cursor: pointer;

    &:hover {
      background-color: #f1f2f5;
    }
  }

  .disabled {
    background-color: #f1f2f5;
    color: #bcbdc0;
    cursor: not-allowed;
  }
</style>
