<script setup>
/**
 * 筛选组件
 * 在3D地图上创建可筛选的设备标记点，并提供分类筛选功能
 * 用于对地图上的不同类型设备进行显示/隐藏控制
 * 基于THREE.js实现3D标记点的渲染和管理
 */
import { inject, computed, ref, watch, onBeforeUnmount } from "vue"

/**
 * 从VgoMap获取THREE.js实例
 * THREE.js是一个JavaScript 3D库，用于3D模型的渲染、动画和交互
 * 此处用于创建和管理3D标记点对象
 */
const { THREE } = VgoMap

/**
 * 注入上级组件（Map.vue）提供的数据
 * 使用Vue的provide/inject机制获取父组件共享的数据
 */
// 从父组件注入地图实例，用于将标记点添加到地图场景
const map = inject("map")
// 从父组件注入所有多边形数据，包含仓库中所有设备的点位信息
const polygonDataAll = inject("polygonDataAll")

/**
 * 计算属性：筛选出有分类的点位
 * 将点位按shortcutsId进行分组，用于生成筛选选项
 * @returns {Object} - 按shortcutsId分组的点位对象，键为分类ID，值为该分类下的点位数组
 */
const shortcutList = computed(() => {
  // 筛选出有shortcutsId属性的点位，并按shortcutsId进行分组
  return polygonDataAll.value.filter(i => i?.shortcutsId).reduce((result, item) => {
    // 如果该分类ID对应的数组不存在，则创建一个新数组
    if(!result?.[item.shortcutsId]) {
      result[item.shortcutsId] = []
    }
    // 将当前点位添加到对应分类的数组中
    result[item.shortcutsId].push(item)
    return result
  }, {})
})

/**
 * 创建标记组，用于统一管理和销毁所有标记点
 * 使用THREE.Group可以方便地组织和管理多个3D对象
 */
const markerGroup = new THREE.Group() // 创建THREE.js组对象，用于集中管理所有标记点
map.value.scene.add(markerGroup) // 将标记组添加到地图场景中，使其可见

/**
 * 组件生命周期钩子：卸载前执行
 * 清理标记点资源，避免内存泄漏
 */
onBeforeUnmount(() => {
  // 安全检查：确保markerGroup存在
  if (markerGroup) {
    markerGroup.clear() // 清除标记组中的所有子对象
    // 安全检查：确保map和map.scene存在
    if (map.value && map.value.scene) {
      map.value.scene.remove(markerGroup) // 从地图场景中移除标记组
    }
  }
})

/**
 * 标记点状态引用
 * 存储各个分类下的标记点对象，用于后续根据用户选择控制显示/隐藏
 * 结构为：{分类ID: [标记点对象1, 标记点对象2, ...]}
 */
const markerStatus = ref({})

/**
 * 监听shortcutList变化，创建或更新标记点
 * 当shortcutList变化时，清理旧标记点并创建新标记点
 * @param {Object} value - 变化后的shortcutList值
 */
watch(shortcutList, (value) => {
  // 清理所有旧的标记点
  while(markerGroup.children.length) {
    const child = markerGroup.children[0]
    if (child && child.dispose) {
      child.dispose()
    }
    markerGroup.remove(child)
  }
  // 重置标记点状态对象
  markerStatus.value = {}

  // 创建新的标记点
  const listGroup = Object.entries(value) // 转换为[key, value]格式的数组，便于遍历
  
  // 遍历每个分类及其对应的点位列表
  listGroup.forEach(([key, list]) => {
    // 遍历该分类下的每个点位
    list.forEach((item) => {
      // 创建标记点，显示"设备类型+分类ID"
      const marker = createDivMarker('设备类型' + item.shortcutsId)
      
      // 设置标记点位置（在点位中心，Z轴方向上移10单位，使其悬浮在点位上方）
      marker.position.copy(item.center).setZ(10)
      
      // 将标记点添加到标记组
      markerGroup.add(marker)
      
      // 将标记点添加到对应分类的状态数组中，便于后续控制显示/隐藏
      if(!markerStatus.value?.[key]) {
        markerStatus.value[key] = []
      }
      markerStatus.value[key].push(marker)
      
      // 初始化时默认选中所有分类
      checkedStatus.value[key] = true
    })
  })
}, {
  immediate: true, // 立即执行一次以初始化标记点
})

/**
 * 选中状态引用
 * 存储各个设备类型分类的选中状态，用于控制对应分类标记点的显示/隐藏
 * 结构为：{分类ID: boolean}
 */
const checkedStatus = ref({})

/**
 * 监听checkedStatus变化，控制标记点的显示/隐藏
 * 当用户通过复选框改变选中状态时，更新对应分类标记点的可见性
 * @param {Object} checked - 变化后的checkedStatus值
 */
watch(checkedStatus, (checked) => {
  // 遍历所有分类的选中状态
  for(let key in checked) {
    // 安全检查：确保该分类下有标记点
    if (markerStatus.value[key] && Array.isArray(markerStatus.value[key])) {
      // 遍历该分类下的所有标记点
      markerStatus.value[key].forEach((marker) => {
        // 根据选中状态设置标记点的可见性
        marker.visible = checked[key]
      })
    }
  }
}, {
  deep: true, // 深度监听，确保能捕获到对象属性的变化
})

/**
 * 创建DOM标记点
 * 在地图上创建一个自定义DOM元素作为标记点
 * @param {string} content - 标记点显示的文本内容
 * @returns {Object} - 创建的标记点对象，可用于进一步操作（如设置位置、可见性等）
 */
function createDivMarker(content) {
  // 创建包装元素
  const wrapper = document.createElement("div")
  
  // 创建内容元素
  const contentNode = document.createElement("div")
  contentNode.className = "marker" // 设置样式类，用于自定义标记点外观
  contentNode.textContent = content // 设置显示文本

  // 将内容元素添加到包装元素中
  wrapper.appendChild(contentNode)
  
  // 获取当前楼层ID，如果获取失败则默认为'1'
  const floorId = map.status?.floor?.data?.id || '1'
  
  // 使用地图API添加DOM标记点到指定楼层
  return map.value.addDomMarker(floorId, wrapper)
}
</script>

<template>
  <!-- 筛选组件界面 -->
  <!-- 用于显示设备类型筛选选项，允许用户控制不同类型设备标记点的显示/隐藏 -->
  <ul class="filter">
    <!-- 遍历shortcutList的所有键，为每个设备类型生成筛选选项 -->
    <li v-for="key in Object.keys(shortcutList)" :key="key">
      <!-- 筛选选项标签，显示"设备类型+分类ID" -->
      <label :for="key">设备类型{{ key }}</label>
      <!-- 筛选复选框，与checkedStatus中的对应状态双向绑定 -->
      <input type="checkbox" :id="key" v-model="checkedStatus[key]">
    </li>
  </ul>
</template>

<style scoped>
/**
 * 筛选组件样式
 * 设置筛选面板的位置、层级和交互样式
 */
.filter {
  position: absolute; /* 绝对定位，固定在地图右下角 */
  right: 50px; /* 距离右侧50px */
  bottom: 20px; /* 距离底部20px */
  z-index: 10; /* 设置较高层级，确保在地图上层显示 */

  /* 为列表项、标签和输入框添加指针样式，表示可交互 */
  li, label, input {
    cursor: pointer;
  }
}

</style>

<style>
/**
 * 全局样式 - 标记点样式
 * 定义地图上设备标记点的外观样式
 */
.marker {
  background-color: #fff; /* 白色背景，提高可见度 */
  padding: 2px 10px; /* 内边距：上下2px，左右10px */
  border-radius: 20px; /* 圆角边框，使标记点看起来更柔和 */
  transform: scale(0.7); /* 缩小标记点显示尺寸，避免遮挡过多地图内容 */
}
</style>