<template>
  <div ref="mapContainer" class="map-container"></div>
  <div v-if="!isTopLevel" class="map-reset-btn">
    <Button :loading="mapLoading" type="primary" @click="revertStack">返回上级</Button>
  </div>
  <div v-if="cadOpened" class="map-cad-btn">
    <a-select
      v-model:value="fileId"
      :options="fileOptions"
      :fieldNames="{ label: 'fileName', value: 'wjId' }"
      :style="{ minWidth: '200px' }"
      @change="toggleCADMap(true)"
    ></a-select>
  </div>
</template>

<script lang="ts" setup>
  import 'vjmap/dist/vjmap.min.css';
  import { ref, onMounted, onUnmounted, computed, createApp } from 'vue';
  import { Button } from 'ant-design-vue';
  import { useAppStore } from '/@/store/modules/app';
  // import { useRoute } from 'vue-router';
  import vjmap from 'vjmap';
  // import type vjmap3d from 'vjmap3d';
  import { getMarkerCollection, getShanxiGeoJSON } from '/@/api/sys/map';
  import { useMineDepartmentStore } from '/@/store/modules/mine';
  import { get, last } from 'lodash-es';
  // import { env } from '/@/views/system/cadFile/env';
  import { StatusColorEnum } from '/@/enums/jeecgEnum';
  import { generateSimplePopup } from './hooks/popup';
  import { getGoafList, getMineFileList } from '/@/views/system/cadFile/cad.api';
  import LeafPopup from './components/LeafPopup.vue';
  import { watchTriggerable } from '@vueuse/core';
  import { findPath, listToTree } from '/@/utils/helper/treeHelper';
  // import { useGlobSetting } from '/@/hooks/setting';
  import { useMap2D } from '/@/views/system/cadFile/app';

  const props = defineProps<{
    silence: boolean;
  }>();

  const { initMap2d, renderGoafMarkers } = useMap2D();
  const appStore = useAppStore();
  const mineStore = useMineDepartmentStore();
  const mapContainer = ref<HTMLElement>();
  const mapLoading = ref(false);

  // 设置地图中当前的参数以供其他页面使用
  appStore.setSimpleMapParams({ deptId: mineStore.getRootId, isLeaf: mineStore.getRoot?.isLeaf });

  // let svc = new vjmap.Service(env.serviceUrl, env.accessToken);
  // let app: vjmap3d.App;
  let map: vjmap.Map | null;
  // 标记单位集合，用来为后续计算历史记录提供支持
  let markerCollection = [];
  // CAD是否可见
  let cadOpened: boolean;

  const GEO_LAYER_ID = 'geojson-layer';
  const GEO_BORDER_ID = 'geoline-layer';
  const CIRCLE_LAYER_ID = 'circle-layer';
  const SYMBOL_LAYER_ID = 'symbol-layer';
  const DEFAULT_NODE = {
    id: mineStore.getRootId!,
    name: '根节点',
    parentId: null,
    longitude: 109.425873,
    latitude: 35.83317,
    zoom: 6,
    isLeaf: false,
  };
  const DEFAULT_TREE_CONFIG = {
    id: 'id',
    pid: 'parentId',
  };

  // 用于存储历史节点的栈，实现返回上一级功能
  const historyStack = ref([DEFAULT_NODE]);
  const isTopLevel = computed(() => get(historyStack.value, 'length', 1) === 1);
  const fileOptions = ref<any[]>([]);
  const fileId = ref();

  /** 根据GeoJSON文件绘制省市边界和填充色到地图上，其将作为一个新图层 */
  function initMapGeoJSON(map: vjmap.Map, geojson) {
    map.addSource('geojson-data', {
      type: 'geojson',
      data: geojson,
      generateId: true,
    });
    map.addFillLayer(GEO_LAYER_ID, 'geojson-data', {
      fillColor: ['get', 'fillColor'],
      fillOpacity: ['case', ['to-boolean', ['feature-state', 'hover']], 0.6, 0.2],
      fillOutlineColor: '#ff9100', // 边线颜色，没错，确实没有边线宽度这个选项，所以有了下面的LineLayer做边线
      filter: ['in', last(historyStack.value)!.id, ['get', 'deptIds']],
    });
    map.addLineLayer(GEO_BORDER_ID, 'geojson-data', {
      lineColor: '#ff9100',
      lineWidth: 2,
      filter: ['in', last(historyStack.value)!.id, ['get', 'deptIds']],
    });
    // map.hoverPointer([GEO_LAYER_ID]);
    map.hoverFeatureState(GEO_LAYER_ID);
  }

  /** 根据标记点信息绘制两个图层，一个圆标点图层和一个文本标点图层，添加标记点注记及点击事件处理逻辑 */
  function initMapMarker(map: vjmap.Map, markers) {
    // 绘制背景圆圈
    const circles = new vjmap.Circle({
      layerId: CIRCLE_LAYER_ID,
      data: markers,
      // 根据hover不同的状态设置不同的颜色值
      circleColor: ['case', ['to-boolean', ['feature-state', 'hover']], StatusColorEnum.gray, ['get', 'fillColor']],
      // 根据hover不同的状态设置不同的宽度值
      circleStrokeWidth: 1,
      circleRadius: ['case', ['to-boolean', ['get', 'isLeaf']], 8, 16],
      // 根据用户所属部门进行初步筛选
      filter: ['==', last(historyStack.value)!.id, ['get', 'parentId']],
    });
    circles.addTo(map);
    // 设置 hover 鼠标指针样式和状态更新
    circles.hoverPointer();
    circles.hoverFeatureState();
    circles.hoverPopup(
      (f) => {
        // if (!f.properties.isLeaf) {
        return generateSimplePopup(f.properties);
        // }
      },
      {
        maxWidth: '300px',
      }
    );

    const symbols = new vjmap.Symbol({
      layerId: SYMBOL_LAYER_ID,
      data: markers,
      textField: ['case', ['to-boolean', ['get', 'isLeaf']], '', ['get', 'count']],
      textFont: ['Noto Sans Italic'],
      textSize: 16,
      textAllowOverlap: true,
      textColor: '#ffffff',
      filter: ['==', last(historyStack.value)!.id, ['get', 'parentId']],
    });
    symbols.addTo(map);

    circles.clickLayer((e) => {
      if (e.defaultPrevented) return;
      const node = get(e, 'features[0].properties', DEFAULT_NODE);
      // 用户从地图点到矿节点需要显示详情框，不需要走set-watch的路线重新初始化地图
      if (node.isLeaf) {
        leafNodeClickHandler(node);
      } else {
        mineStore.setDepartById(node.id);
      }
      e.preventDefault(); // 阻止之后的事件执行
    });
  }

  /** 初始化地图对象，仅负责初始化地图及其瓦片底图，待map加载完成后返回 */
  async function initMap(HTMLElement) {
    const map = new vjmap.Map({
      container: HTMLElement,
      // 这个zoom配合了getGeoJSON，要改两边都要改
      maxZoom: 20,
      minZoom: 1,
      center: [DEFAULT_NODE.longitude, DEFAULT_NODE.latitude],
      zoom: DEFAULT_NODE.zoom,
      trackResize: false,
      // style: svc.rasterStyle('https://webrd01.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=7&x={x}&y={y}&z={z}'),
      style: `${window.location.origin}/js/shanxi.map.json`,
    });
    await map.onLoad();

    return map;
  }

  /** 标记点点击事件处理，如果不是叶节点那么聚焦到下一级 */
  function markerClickHandler() {
    if (!map) return;
    const node: any = last(historyStack.value);
    if (!node) return;
    if (node.isLeaf) return;

    appStore.setSimpleMapParams({ deptId: node.id, isLeaf: false });

    // 标点点击后，如果是叶节点需要显示详情框，点击标题后进CAD图
    map.setFilter(CIRCLE_LAYER_ID, ['==', node.id, ['get', 'parentId']]);
    map.setFilter(SYMBOL_LAYER_ID, ['==', node.id, ['get', 'parentId']]);
    map.setFilter(GEO_LAYER_ID, ['in', node.id, ['get', 'deptIds']]);
    map.setFilter(GEO_BORDER_ID, ['in', node.id, ['get', 'deptIds']]);

    map.flyTo({
      center: [node.longitude, node.latitude],
      zoom: node.zoom,
    });
  }

  /** 叶节点标记点击事件处理，叶节点需要显示该节点的详情框 */
  function leafNodeClickHandler(node: any) {
    if (!map) return;
    if (!node) return;
    if (!node.isLeaf) return;

    // 避免依赖isLeaf的首页进入矿端模式
    appStore.setSimpleMapParams({ deptId: node.id, isLeaf: false });
    // 这里推出去一个是因为点击到矿端之后不需要再下钻，那么历史栈应该保持在上一层以确保返回上一级可用
    // 待到用户确实点击了详情框标题后在推一项历史可保证历史栈的正确
    // historyStack.value.pop();

    const popup = new vjmap.Popup({ maxWidth: '1000' });
    const app = createApp(LeafPopup, {
      node,
      callback() {
        // 切换显示CAD图
        mineStore.setDepartById(node.id);
      },
    });
    const el = document.createElement('div');
    app.mount(el);

    popup.setLngLat([node.longitude, node.latitude]).setDOMContent(el).addTo(map);
    map.flyTo({
      center: [node.longitude, node.latitude + 0.2],
      zoom: node.zoom,
    });
  }

  /** 返回上一级操作，如果存在CAD底图，则切换至地图，否则回退历史记录栈 */
  async function revertStack() {
    if (isTopLevel.value) return;
    historyStack.value.pop();

    // 用户点击返回后绝对不可能停留在CAD底图上，因此这里不区分
    const id = last(historyStack.value)!.id;
    if (id === mineStore.getDepartId) {
      trigger();
    } else {
      mineStore.setDepartById(id);
    }
  }

  /** 切换CAD底图和瓦片地图底图的显示，通过重新初始化进行切换，避免出现动画异常和多个DOM节点 */
  async function toggleCADMap(visible: boolean) {
    // CAD底图不显示的时候无需再次初始化地图，显示时需要根据data来切换不同图纸
    if (cadOpened === visible && visible === false) return;

    cadOpened = visible;
    mapLoading.value = true;
    map?.destory();
    map = null;

    try {
      let markers;

      // 当不是静默状态时，需要标记点位信息来计算历史栈
      if (!props.silence) {
        const [records, mks] = await getMarkerCollection({
          deptId: mineStore.getRootId,
          pageSize: 9999,
        });
        markerCollection = listToTree(records, DEFAULT_TREE_CONFIG);
        markers = mks;
      }

      if (cadOpened) {
        const [__, res] = await Promise.all([
          initMap2d(mapContainer.value!, { wjId: fileId.value, style: { backcolor: 0xe6f3ff } }),
          props.silence
            ? Promise.resolve([])
            : getGoafList({
                mineCode: mineStore.getDepartId,
                deptId: mineStore.getDepartId,
              }),
        ]);

        renderGoafMarkers(res); // 渲染标记
      } else {
        // const hide = message.loading('地图加载中...', 0);
        const [m, res] = await Promise.all([initMap(mapContainer.value!), getShanxiGeoJSON()]);
        // hide();
        map = m;

        initMapGeoJSON(map, res);
        initMapMarker(map, markers);
      }
    } catch (e) {
      console.error('地图初始化错误', e);
    } finally {
      mapLoading.value = false;
    }
  }

  // 监听全局矿井ID的方法，同时也是负责初始化地图的方法
  // 两种方式触发watch，一种是点击全局矿井选择器
  // 如果全局矿井选择了非矿端，需要显示地图并根据全局矿井ID设置好历史记录
  // 如果是选择了矿端的页面，需要设置历史记录并显示矿端CAD图纸
  // 另一种方式是点击地图上的标记点，普通标点下钻到下一级，矿端标点不走watch的逻辑
  const { trigger } = watchTriggerable(
    () => mineStore.getDepartId,
    async () => {
      if (!mapContainer.value) return;

      // 如果是静默模式就简单展示默认地图/图纸即可
      if (props.silence) return toggleCADMap(mineStore.getRoot?.isLeaf || false);

      // 先判断显示什么图纸，若是矿端显示CAD图纸
      if (mineStore.getDepart?.isLeaf) {
        appStore.setSimpleMapParams({ deptId: mineStore.getDepartId, isLeaf: true });

        // 矿端需要先初始化图纸下拉框，下拉框触发一次change事件调用toggleCADMap，
        // 如果当前选项下没有已选中的地图，那么意味着需要新获取选项，否则无需发请求
        const { records } = await getMineFileList({ deptId: mineStore.getDepartId, pageSize: 999 });

        if (records.length) {
          fileOptions.value = records;
        } else {
          fileOptions.value = [{ fileName: '默认系统图', wjId: 'c96e7423dab1' }];
        }
        fileId.value = get(fileOptions.value, '[0].wjId');
      }

      await toggleCADMap(mineStore.getDepart?.isLeaf || false);

      // 不论是不是矿端，历史记录都是一样的，用户点击返回上一级都能正常返回
      historyStack.value = findPath(markerCollection, (n) => n.id === mineStore.getDepartId) as any[];

      // 此外如果是非矿端那么模拟触发地图点击事件
      markerClickHandler();
    }
  );

  onMounted(() => {
    trigger();
  });

  onUnmounted(() => {
    map?.destory();
    map = null;
  });
</script>

<style lang="less">
  .map-container {
    flex: 1;
    height: 100%;
    width: 100%;
    position: fixed;
    top: 0;
    left: 0;
    z-index: @simple-map-z-index;
    // filter: grayscale(80%) invert(0%) sepia(10%) hue-rotate(150deg) saturate(200%) brightness(100%) contrast(100%);

    canvas {
      /* width: 1920px !important;
      height: 948px !important; */
    }
  }

  .cad-container {
    display: none;
    z-index: @cad-map-z-index;
    background: @map-bg !important;
  }

  // .map-container canvas {
  .light-blue-theme {
    filter: invert(0.1) hue-rotate(190deg) saturate(2);
    // filter: sepia(0.3) hue-rotate(180deg) saturate(1.8);
  }

  .vjmapgis-popup {
    color: @white;
    // &-content-wrapper {
    //   background-color: @map-popup-bg;
    // }

    &-close-button {
      font-size: 30px;
    }

    &-anchor-bottom &-tip {
      border-top-color: @map-popup-bg;
    }

    &-content {
      // margin: 13px 20px;
      // min-width: 300px;
      background-color: @map-popup-bg;
      backdrop-filter: blur(4px);
      box-shadow: 0 0 10px #00000088;
      // &__popup {
      // }
    }
  }
  .map-reset-btn {
    position: absolute;
    right: 30%; /* 距离右边 20px */
    z-index: 2; /* 确保在地图控件之上 */
    top: @header-height;
    // padding: 10px 15px;
    margin: 10px 0;
  }
  .map-cad-btn {
    position: absolute;
    left: 30%; /* 距离右边 20px */
    z-index: 2; /* 确保在地图控件之上 */
    top: @header-height;
    // padding: 10px 15px;
    margin: 10px 0;
  }
</style>
