<template>
  <div class="page">
    <div class="page-head flex items-center">
      <el-icon size="20"><ArrowLeft /></el-icon>
      <div class="head-opt flex-1 flex justify-between items-center">
        <div>
          <span>场景：</span>
          <el-tree-select
            v-model="selectedShopCode"
            :data="scenesTree"
            accordion
            :render-after-expand="false"
            :default-expand-all="true"
            :teleported="false"
            placeholder="请选择相关场景"
            @change="changeShop"
          />
        </div>
        <div class="flex">
          <el-upload
            class="avatar-uploader flex justify-center items-center"
            action="/temp/api/layout/uploadPicture"
            :show-file-list="false"
            :on-success="handleAvatarSuccess"
            :with-credentials="true"
            name="file"
            :data="{ workshopId: selectedShopCode }"
          >
            <el-button style="font-size: 12px" :icon="Refresh" :disabled="!hasBg">
              替换照片
            </el-button>
          </el-upload>

          <el-button
            @click="handleSave"
            style="margin-left: 40px"
            type="primary"
            :disabled="!selectedShopCode"
            >保存为布局
          </el-button>
        </div>
      </div>
    </div>
    <div class="paint-tool flex">
      <div class="camera-list">
        <div>
          <span class="label-text flex">相机列表：</span>
          <ElInput
            class="search-put"
            style="margin: 10px 0; width: 230px"
            placeholder="请输入搜索内容"
            v-model="searchKey"
            :suffix-icon="Search"
          />
        </div>
        <span v-if="filterShopCameraList.length == 0" class="ml-1" style="color: #3f3f3f">
          提示：请先选择相应场景和图片
        </span>
        <el-scrollbar v-else style="position: relative; height: calc(100% - 90px)">
          <div
            v-for="item in filterShopCameraList"
            :key="item.code"
            class="camera-item flex justify-start"
            :class="{
              isAdded: isAddedToMap(item.code),
              isActive: item.code === selectedCamera?.cameraId,
            }"
            @click="handleAddCamera(item.code)"
          >
            <span class="camera-id">{{ item.name }}</span>
            <el-popover
              placement="bottom-start"
              trigger="hover"
              :content="item.name"
              :teleported="false"
            >
              <template #reference>
                <span class="space-name">{{ item.workSpaceName }}</span>
              </template>
            </el-popover>
          </div>
        </el-scrollbar>
      </div>
      <div class="draw-container">
        <div style="overflow: auto">
          <canvas width="400" height="400" id="mapEditCanvas"></canvas>
        </div>
        <el-upload
          v-if="!hasBg"
          class="upload-icon flex justify-center items-center"
          action="/temp/api/layout/uploadPicture"
          :show-file-list="false"
          :on-success="handleAvatarSuccess"
          :with-credentials="true"
          name="file"
          :data="{ workshopId: selectedShopCode }"
        >
          <img src="~@/assets/images/img-upload.png" />
        </el-upload>
        <div>
          <div style="height: 20px; margin-bottom: 10px">
            <div v-if="selectedCamera">
              <SelectedCameraToolbar
                @render-map="renderMap"
                :selected-camera="selectedCamera!"
                :key="selectedPositionHash"
              />
            </div>
          </div>
        </div>
        <div style="overflow: auto">
          <canvas width="400" height="400" id="mapEditCanvas"></canvas>
        </div>
        <div style="overflow: auto; position: relative">
          <canvas
            width="400"
            height="400"
            id="mapEditCanvas"
            style="border: 1px solid #ccc"
          ></canvas>
          <ContextMenu
            :visible="menuVisible"
            :position="mousePosition"
            :set-default="handleSetDefault"
          />
          <DefaultCameraIcon :position="favPosition" />
        </div>

        <CameraPreview :json="cameraJSON" />
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
  import useMiniMap from './use-mini-map';
  import { storeToRefs } from 'pinia';
  import { ElMessage, ElInput } from 'element-plus';
  import urlJoin from 'url-join';
  import { onMounted, ref } from 'vue';
  import useCameraMap from './MapBase/useCameraMap';
  import { updateMinMapViewLayoutApi } from '@/api/scene/scene';
  import { onUnmounted, watchEffect } from 'vue';
  import { useGlobSetting } from '@/hooks/setting';
  import { computed } from 'vue';
  import ContextMenu from './MapBase/ContextMenu.vue';
  import DefaultCameraIcon from './MapBase/DefaultCameraIcon.vue';
  import { CameraImage } from './MapBase/types';
  import { createSelectedPositionHash, getFavPositionByCamera } from './MapBase/utils';
  import SelectedCameraToolbar from './components/SelectedCameraToolbar.vue';
  import CameraPreview from './MapBase/CameraPreview.vue';
  import { Search, Refresh } from '@element-plus/icons-vue';

  const miniMap = useMiniMap();
  const globSetting = useGlobSetting();
  const { scenesTree, shopCameraList, selectedShopCode, selectedShopDetail } = storeToRefs(miniMap);
  const { getScenesTree, getShowCameras, getMapLayout } = miniMap;
  const selectedPositionHash = ref('');
  const allObjects = ref<CameraImage[]>([]);

  const onSelectCamera = () => {
    menuVisible.value = false;
  };

  const handleRightClick = (e) => {
    mousePosition.value = { left: e.pointer.x, top: e.pointer.y };
    menuVisible.value = true;
    rightSelectedCamera.value = e.target;
  };

  const handleMoving = (e) => {
    const target = e.transform.target;
    selectedPositionHash.value = createSelectedPositionHash(target);
    if (target?.cameraId === defaultCamera.value?.cameraId) {
      favPosition.value = getFavPositionByCamera(target);
    }
  };

  const handleRoating = (e) => {
    const target = e.transform.target;
    selectedPositionHash.value = createSelectedPositionHash(target);
    if (target.cameraId === defaultCamera.value?.cameraId) {
      favPosition.value = getFavPositionByCamera(target);
    }
  };

  const handleSetSelectedCameara = (target: CameraImage | null) => {
    selectedCamera.value = target;
  };

  const handleObjectsAdded = () => {
    allObjects.value = map.getObjects();
  };

  const map = useCameraMap({
    onSelect: onSelectCamera,
    onRightClick: handleRightClick,
    onMoving: handleMoving,
    onRotating: handleRoating,
    setSelectedCamera: handleSetSelectedCameara,
    onObjectsAdded: handleObjectsAdded,
  });

  const selectedCamera = ref<CameraImage | null>();
  /** 右键选中的摄像机 */
  const rightSelectedCamera = ref<CameraImage | null>(null);
  const defaultCamera = ref<CameraImage | null>();
  const searchKey = ref('');
  // 是否已有背景图
  const hasBg = ref(false);

  const mousePosition = ref<{ left: number; top: number }>({ left: 0, top: 0 });
  const menuVisible = ref(false);
  const favPosition = ref<{ left: number; top: number } | null>(null);

  const handleAvatarSuccess = (e) => {
    const imgPath = e.data;
    const imgUrl = urlJoin(globSetting.imgUrl!, imgPath);
    hasBg.value = true;
    map.uploadBg(imgUrl);
  };

  const renderMap = () => {
    map.renderCamera();
    favPosition.value = getFavPositionByCamera(defaultCamera.value);
  };

  const changeShop = (code: string) => {
    getShopContent(code);
    hasBg.value = false;
  };

  const cameraJSON = ref();

  const getShopContent = (code: string) => {
    getShowCameras(code);
    getMapLayout(code).then((res) => {
      if (!res) {
        map.clear();
        defaultCamera.value = null;
        return;
      }
      cameraJSON.value = res;

      map.loadFromJSON(res).then(() => {
        console.log('loadFromJSON', res);
        if (res.defaultCameraId) {
          defaultCamera.value = map.getCameraById(res.defaultCameraId);
          console.log('defaultCamera', defaultCamera.value);
        } else {
          defaultCamera.value = map.getObjects()?.[0] as CameraImage;
        }
        renderMap();
      });
    });
  };

  onMounted(() => {
    getScenesTree({ level: 2, valueKey: 'code', labelKey: 'name', disabled: true });
    if (selectedShopCode.value) {
      getShopContent(selectedShopCode.value);
    }
    map.createMap('mapEditCanvas');
  });

  const keyupListener = (e) => {
    const keyCode = e.code;
    if (keyCode === 'Delete') {
      handleDeleteCamera();
    }
  };

  onMounted(() => {
    document.addEventListener('keyup', keyupListener);
  });

  onUnmounted(() => {
    document.removeEventListener('keyup', keyupListener);
  });

  const filterShopCameraList = computed(() => {
    const k = searchKey.value.trim();
    if (!k) return shopCameraList.value;
    return shopCameraList.value.filter((x) => x.code?.includes(k) || x.workSpaceName?.includes(k));
  });

  /** 摄像机是否已添加到底图 */
  const isAddedToMap = (cameraId: string): boolean => {
    return !!allObjects.value.find((item) => item.cameraId === cameraId);
  };

  const handleAddCamera = (cameraId: string) => {
    if (!hasBg.value) return;
    if (map.hasCamera(cameraId)) {
      ElMessage.warning({ message: '相机已添加' });
      return;
    }
    map.addCamera(cameraId).then((cameraImg) => {
      if (!defaultCamera.value?.cameraId) {
        defaultCamera.value = cameraImg;
      }
    });
  };

  const handleSave = () => {
    const json = map.toJSON();
    console.log('save json', json);
    if (!json?.backgroundImage) {
      ElMessage.error('背景图片未添加');
      return;
    }
    const layout = JSON.stringify({ ...json, defaultCameraId: defaultCamera.value?.cameraId });
    updateMinMapViewLayoutApi({ layout, targetId: String(selectedShopDetail.value?.id) }).then(
      (res) => {
        console.log('updateMinMapViewLayoutApi', res);
        ElMessage.success('保存成功');
      },
    );
  };

  const handleDeleteCamera = () => {
    if (!selectedCamera.value) return;
    /** 如果删除的是默认选中的摄像头，那么先清空默认的摄像头再 */
    if (selectedCamera.value?.cameraId === defaultCamera.value?.cameraId) {
      defaultCamera.value = map.getObjects()?.[0];
    }
    map.removeActiveCamera();
  };

  const handleSetDefault = () => {
    const cameraId = rightSelectedCamera.value?.cameraId;
    if (!cameraId) return;
    defaultCamera.value = rightSelectedCamera.value;

    /** 选择完成后隐藏 */
    menuVisible.value = false;
  };

  watchEffect(() => {
    if (!defaultCamera.value) {
      favPosition.value = null;
      return;
    }
    favPosition.value = getFavPositionByCamera(defaultCamera.value);
  });
</script>

<style scoped>
  .page {
  }
  .page-head {
    height: 54px;
    padding-left: 15px;
    background-color: #ffffff;
  }
  .head-opt {
    margin-left: 20px;
    padding-right: 15px;
    font-size: 14px;
    color: #3f3f3f;
  }

  .avatar-uploader {
    /* width: 120px; */
    /* height: 30px; */
    /* border: 1px solid #eee; */
    border-radius: 4px;
    margin-left: 30px;
  }
  .upload-icon {
    position: absolute;
    top: 0;
    right: 0;
    left: 0;
    bottom: 0;
    margin: auto;
  }

  .paint-tool {
    position: relative;
    height: calc(100vh - 138px);
    margin-top: 2px;
  }

  .camera-list {
    width: 250px;
    padding: 0 10px;
    background-color: #ffffff;
  }
  .label-text {
    font-size: 14px;
    font-weight: 600;
    margin: 10px 0 5px 10px;
  }
  .camera-item {
    height: 32px;
    font-size: 14px;
    font-weight: 400;
    color: #404040;
    line-height: 14px;
    cursor: pointer;
  }
  .camera-item-disabled {
    color: #c6c6c6;
  }
  .camera-id {
    width: 110px;
  }
  .space-name {
    width: 120px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }

  .draw-container {
    position: relative;
    width: calc(100% - 300px);
    margin: 20px;
  }

  :deep(.search-put .el-input__wrapper) {
    background-color: #f0f2f5;
  }
  :deep(.el-popper__arrow) {
    display: none;
  }
  :deep(.el-tree-node__content:hover) {
    background: #e6f4ff;
  }
  :deep(.el-button--primary) {
    --el-button-disabled-bg-color: #bfbfbf;
  }
  :deep(.el-popover) {
    width: unset !important;
    min-width: 110px;
    text-align: center;
    font-weight: 400;
  }

  .isAdded {
    color: #409eff;
  }
  .isActive {
    background-color: #eee;
  }
</style>
./MapBase/useCameraMap ./MapBase/CameraMapBak
