<template>
  <div id="map">
    <slot name="content"></slot>
  </div>
</template>

<script>
import mapboxgl from 'mapbox-gl'
import { MapboxStyleSwitcherControl, FollowControl, CustomFullscreenControl, NoFlyControl, RestrictflyControl, SaveToFileControl, PolygonToggleControl } from '@/utils/mapboxgl_plugs'
import planeIcon from '@/assets/svg/plane.svg'
import planeRunIcon from '@/assets/svg/plane_run.svg'
import unlineIcon from '@/assets/svg/plane_unline.svg'
import civilIcon from '@/assets/svg/civil.svg'

export default {
  name: 'MapBox',
  data () {
    return {
      token: 'pk.eyJ1Ijoic3pkb3QiLCJhIjoiY2xhN2pkMWFoMHJ4eTN3cXp6bmlzaHZ0NCJ9.3hH-EAUr0wQCaLvIM2lBMQ',
      map: null,
      ADSBMarkers: [], // ADSB民航飞机对象
      planes: [], // 飞机对象
      lonLats: [], // 航线 所有航点
      wayLonLats: [], // 航线 不包括起点航点
      takeoffLonLats: [], // 航线 第一个航点 起点 最后一个航点
      isflow: false, // 飞机经纬度变化时是否跟随飞机
      currentStyleIndex: 0, // 当前选中的地图样式索引
      guidedMarker: null, // 指点飞行地图标记
      styleSwitcherControlRef: null, // 地图样式切换控件引用
      fullscreenControlRef: null // 全屏控件引用（enableZoom）
    }
  },
  props: {
    enableGuided: { // 是否开启 飞机点飞功能
      type: Boolean,
      default: false
    },
    enableFollow: { // 开启跟随组件
      type: Boolean,
      default: false
    },
    enableSwitch: { // 开启 地图选择
      type: Boolean,
      default: true
    },
    enableZoom: { // 开启 地图放大组件
      type: Boolean,
      default: true
    },
    enblueTranslate: { // 开启 地图移动旋转 组件
      type: Boolean,
      default: true
    },
    enblueScale: { // 开启 地图 距离比例 组件
      type: Boolean,
      default: false
    },
    enableSaveToFile: { // 开启 保存数据到文件 组件
      type: Boolean,
      default: false
    },
    getData: { // 从父级 获取数据
      type: Function,
      default: null
    },
    enableNofly: { // 禁飞区 设置 组件
      type: Boolean,
      default: false
    },
    enableRestrictfly: { // 限飞区 设置 组件
      type: Boolean,
      default: false
    },
    enableShowNofly: { // 显示 禁飞区 限飞区 组件
      type: Boolean,
      default: false
    }
  },
  computed: {
    defaultLonlat () {
      return this.$store.getters['app/getDefaultLonLat']
    },
    defaultZoom () {
      return this.$store.getters['app/getDefaultZoom']
    },
    mapStyleList () {
      return this.$store.state.mapStyleList
    }
  },
  watch: {
    /** 动态开关全屏控件（enableZoom） */
    enableZoom (val) {
      if (!this.map) return
      if (val) {
        if (!this.fullscreenControlRef) {
          const control = new CustomFullscreenControl(this.handleResize)
          this.map.addControl(control, 'top-right')
          this.fullscreenControlRef = control
        }
      } else {
        if (this.fullscreenControlRef) {
          try { this.map.removeControl(this.fullscreenControlRef) } catch (e) { /* noop */ }
          this.fullscreenControlRef = null
        }
      }
    },
    /** 动态开关地图样式切换控件 */
    enableSwitch (val) {
      if (!this.map) return
      if (val) {
        if (!this.styleSwitcherControlRef) {
          const control = new MapboxStyleSwitcherControl(this.mapStyleList, 'iconfont icon-duozhang f-s-20', this.currentStyleIndex)
          this.map.addControl(control, 'top-right')
          this.styleSwitcherControlRef = control
        }
      } else {
        if (this.styleSwitcherControlRef) {
          try { this.map.removeControl(this.styleSwitcherControlRef) } catch (e) { /* noop */ }
          this.styleSwitcherControlRef = null
        }
      }
    }
  },
  async mounted () {
    // 等待 异步 加载 后端地图源 地址
    if (this.$store.state.mapStyleList.length === 0) {
      await this.$store.dispatch('fetchMapStyleList', this.$store.state.settings.host)
    }
    // 等待 异步 地图初始化
    await this.init()

    // 地图初始化之后
    this.map.on('load', () => {
      /* 更新样式，添加自定义 sprite */

      // 星空背景 大气层
      this.map.setFog({
        range: [1.0, 8.0],
        color: 'white',
        'horizon-blend': 0.01
      })
      // // 添加等高线图层
      // this.map.addSource('contours', {
      //   type: 'vector',
      //   url: 'mapbox://mapbox.mapbox-terrain-v2'
      // })
      // this.map.addLayer({
      //   id: 'contours',
      //   type: 'line',
      //   source: 'contours',
      //   'source-layer': 'contour',
      //   layout: {
      //     'line-join': 'round',
      //     'line-cap': 'round'
      //   },
      //   paint: {
      //     'line-color': '#ff69b4',// 设置等高线颜色
      //     'line-width': 1 // 设置等高线宽度
      //   }
      // })
      // // 3D地图
      // this.map.addSource('mapbox-dem', {
      //   type: 'raster-dem',
      //   url: 'mapbox://mapbox.mapbox-terrain-dem-v1',
      //   tileSize: 512,
      //   maxzoom: 14
      // })
      // this.map.setTerrain({ source: 'mapbox-dem', exaggeration: 1.5 })

      // 外部组件回调 触发地图加载完成事件(on 'load') 传到外部组件调用 以确保执行时地图已经加载完成
      this.$emit('map-ready')

      // 判断地图开启了点飞功能
      if (this.enableGuided) {
        // 长按事件 传longPress到组件外部调用
        let pressTimer = null
        let isLongPress = false // 标记是否为长按
        let startPoint = null // 记录按下时的位置
        // pc操作
        this.map.on('mousedown', (event) => { // 按下鼠标左键
          startPoint = { x: event.point.x, y: event.point.y } // 记录起始位置
          isLongPress = false // 重置标记
          pressTimer = setTimeout(() => {
            isLongPress = true // 设置为长按状态
          }, 200) // 设置长按延迟时间
        })
        this.map.on('mouseup', (event) => { // 提起鼠标左键
          clearTimeout(pressTimer) // 清除定时器
          const currentPoint = { x: event.point.x, y: event.point.y }
          const dx = currentPoint.x - startPoint.x
          const dy = currentPoint.y - startPoint.y
          if (Math.sqrt(dx * dx + dy * dy) <= 5 && isLongPress) { // 没有发生拖拽地图
            const lonLat = {
              lon: Number(this.map.unproject(currentPoint).lng.toFixed(7)),
              lat: Number(this.map.unproject(currentPoint).lat.toFixed(7))
            }
            this.$emit('longPress', lonLat) // 将经纬度信息传递到组件外部
            this.makeGuidedMarker(lonLat) // 创建标记
          }
        })
        // 触摸屏操作
        this.map.on('touchstart', (event) => { // 按下鼠标左键
          clearTimeout(pressTimer) // 清除定时器
          startPoint = { x: event.point.x, y: event.point.y } // 记录起始位置
          isLongPress = false // 重置标记
          pressTimer = setTimeout(() => {
            isLongPress = true // 设置为长按状态
          }, 200) // 设置长按延迟时间
        })
        this.map.on('touchend', (event) => { // 提起鼠标左键
          const currentPoint = { x: event.point.x, y: event.point.y }
          const dx = currentPoint.x - startPoint.x
          const dy = currentPoint.y - startPoint.y
          if (Math.sqrt(dx * dx + dy * dy) <= 5 && isLongPress) { // 没有发生拖拽地图
            const lonLat = {
              lon: this.map.unproject(currentPoint).lng,
              lat: this.map.unproject(currentPoint).lat
            }
            this.$emit('longPress', lonLat) // 将经纬度信息传递到组件外部
            this.makeGuidedMarker(lonLat) // 创建标记
          }
        })
      }
    })
  },
  methods: {
    /**
     * @description: 地图初始化
     */
    async init () {
      // token
      mapboxgl.accessToken = this.token
      // 实例化map
      this.map = new mapboxgl.Map({
        container: 'map',
        style: this.mapStyleList[0],
        center: this.defaultLonlat,
        zoom: this.defaultZoom,
        pitch: 0,
        bearing: 0,
        projection: 'globe'
      })
      /* 地图控件 */
      // 移动旋转指南针
      if (this.enblueTranslate) {
        this.map.addControl(new mapboxgl.NavigationControl(), 'top-right')
      }

      // 地图比例
      if (this.enblueScale) {
        this.map.addControl(new mapboxgl.ScaleControl(), 'bottom-left')
      }
      // 全屏 并再之后 刷新地图
      if (this.enableZoom) {
        this.fullscreenControlRef = new CustomFullscreenControl(this.handleResize)
        this.map.addControl(this.fullscreenControlRef, 'top-right')
      }

      // 飞机跟随
      if (this.enableFollow) {
        this.map.addControl(new FollowControl({
          defaultIconClass: 'iconfont icon-weidingwei f-s-20', // 默认图标
          activeIconClass: 'iconfont icon-dingwei f-s-20 brandFontColor', // 激活时图标
          onClick: (isActive) => {
            this.isflow = isActive // 根据控件的状态更新 isflow
            if (this.isflow) {
              this.goto(this.planes[0].getLngLat()) // 点击按钮首先直接 跳转到当前飞机对象最后的经纬度
            }
          }
        }), 'top-right')
      }

      // 保存数据到文件 控件 用于:1.保存历史飞行航线轨迹
      if (this.enableSaveToFile && typeof this.getData === 'function') {
        this.map.addControl(new SaveToFileControl({
          Data: this.getData,
          filename: `飞行数据_${Date.now()}.json`,
          title: '保存当前数据',
          icon: '💾'
        }), 'top-left')
      }

      // 禁飞区 限飞区 设置组件
      if (this.enableNofly) {
        this.map.addControl(new NoFlyControl({
          defaultPolygons: this.$store.state.noflyData[0], // 禁飞
          shopId: this.$store.state.user.shop_id,
          onSave: (data) => {
            console.log('保存成功:', data)
          }
        }), 'top-left')
      }

      // 禁飞区 限飞区 设置组件
      if (this.enableRestrictfly) {
        this.map.addControl(new RestrictflyControl({
          defaultPolygons: this.$store.state.noflyData[1], // 限飞区
          defaultHeights: this.$store.state.noflyData[2], // 限飞区对应高度
          shopId: this.$store.state.user.shop_id,
          onSave: (data) => {
            console.log('保存成功:', data)
          }
        }), 'top-left')
      }

      // 显示禁飞区 限飞区控件
      if (this.enableShowNofly) {
        const noflyPolygons = this.$store.state.noflyData[0] || []
        const restrictflyPolygons = this.$store.state.noflyData[1] || []
        const restrictflyHeights = this.$store.state.noflyData[2] || []

        this.map.addControl(new PolygonToggleControl({
          defaultIconClass: 'iconfont icon-jinfeiqu_weidianji f-s-20 seatFontColor',
          activeIconClass: 'iconfont icon-jinfeiqu f-s-20 brandFontColor',

          onToggle: (isActive) => {
            if (isActive) {
              // 绘制禁飞区
              noflyPolygons.forEach((coords, i) => {
                this.drawPolygonWithLabel(coords, `nofly-polygon-${i}`, '#F33', '禁飞区')
              })
              // 绘制限飞区，添加高度标签
              restrictflyPolygons.forEach((coords, i) => {
                const height = restrictflyHeights[i] || 0
                const label = `限飞高度${height}米`
                this.drawPolygonWithLabel(coords, `restrict-polygon-${i}`, '#F83', label)
              })
            } else {
              // 清除图层
              const layerIds = []
              const sourceIds = []

              noflyPolygons.forEach((_, i) => {
                layerIds.push(`nofly-polygon-${i}`, `nofly-polygon-${i}-outline`, `nofly-polygon-${i}-label-layer`)
                sourceIds.push(`nofly-polygon-${i}`, `nofly-polygon-${i}-label`)
              })

              restrictflyPolygons.forEach((_, i) => {
                layerIds.push(`restrict-polygon-${i}`, `restrict-polygon-${i}-outline`, `restrict-polygon-${i}-label-layer`)
                sourceIds.push(`restrict-polygon-${i}`, `restrict-polygon-${i}-label`)
              })

              this.clearMapElements(layerIds, sourceIds)
            }
          }
        }), 'top-right')
      }

      // 地图样式选择控件
      // 自定义地图样式列表
      if (this.enableSwitch) {
        this.styleSwitcherControlRef = new MapboxStyleSwitcherControl(this.mapStyleList, 'iconfont icon-duozhang f-s-20', this.currentStyleIndex)
        this.map.addControl(this.styleSwitcherControlRef, 'top-right')
      }
    },
    /**
     * @description 通用地图图层和数据源清除方法
     * @param {Array} layerIds - 要清除的图层ID数组
     * @param {Array} sourceIds - 要清除的数据源ID数组
     */
    clearMapElements (layerIds = [], sourceIds = []) {
      // 先移除图层（必须先删图层，再删数据源）
      layerIds.forEach(id => {
        if (this.map.getLayer(id)) {
          this.map.removeLayer(id)
        }
      })

      // 再移除数据源
      sourceIds.forEach(id => {
        if (this.map.getSource(id)) {
          this.map.removeSource(id)
        }
      })
    },
    /**
     * @description: 清除地图上的航线
     */
    clearRoute () {
      // 判断地图上是否已经存在 航线信息 存在先将其清除
      if (this.map.getSource('way_route')) {
        this.map.removeLayer('takeoff_layer')
        this.map.removeSource('takeoff_route')
        this.map.removeLayer('way_route_layer')
        this.map.removeLayer('way_route_layer1')
        this.map.removeSource('way_route')
        this.map.removeLayer('home_point_layer')
        this.map.removeSource('home_point')
        this.map.removeLayer('takeoff_point_layer')
        this.map.removeSource('takeoff_point')
        for (let i = 2; i < this.lonLats.length - 1; i++) {
          this.map.removeLayer('way_point_layer' + i)
          this.map.removeSource('way_point' + i)
        }
        this.lonLats = []
        this.wayLonLats = []
        this.takeoffLonLats = []
      }
    },
    /**
     * @description: 绘画航线
     * @param {*} routeObj 航点文件对象
     */
    makeRoute (routeObj) {
      // 初始化清空航线
      this.clearRoute()
      // 获取所有航点 及 非起点所有航点
      routeObj.questAss.tasks.forEach((element, index) => {
        const lonLat = [element.y, element.x, 100]
        this.lonLats.push(lonLat)
        if (index > 1 && index < routeObj.questAss.taskcount - 1) {
          this.wayLonLats.push(lonLat)
        }
      })
      // 跳转home点位置
      this.goto(this.lonLats[0])
      // 起飞 降落虚线
      if (routeObj.questAss.tasks[this.lonLats.length - 1].command === 20) {
        this.takeoffLonLats.push(this.lonLats[2])
        this.takeoffLonLats.push(this.lonLats[1])
        this.takeoffLonLats.push(this.lonLats[this.lonLats.length - 2])
      } else if (
        routeObj.questAss.tasks[this.lonLats.length - 1].command === 21
      ) {
        this.takeoffLonLats.push(this.lonLats[2])
        this.takeoffLonLats.push(this.lonLats[1])
      }

      // 起飞降落虚线
      this.map.addSource('takeoff_route', {
        type: 'geojson',
        data: {
          type: 'Feature',
          properties: {},
          geometry: {
            type: 'LineString',
            coordinates: this.takeoffLonLats
          }
        }
      })
      this.map.addLayer({
        id: 'takeoff_layer',
        type: 'line',
        source: 'takeoff_route',
        layout: {
          'line-join': 'round', // 线连接处的样式
          'line-cap': 'round' // 线结束处的样式
        },
        paint: {
          'line-color': '#fff', // 线的颜色
          'line-width': 2, // 线的宽度
          'line-opacity': 1.0, // 线的透明度
          'line-dasharray': [3, 2] // 虚线
        }
      })
      // 航线 叠加描边线
      this.map.addSource('way_route', {
        type: 'geojson',
        data: {
          type: 'Feature',
          properties: {},
          geometry: {
            type: 'LineString',
            coordinates: this.wayLonLats
          }
        }
      })
      this.map.addLayer({
        id: 'way_route_layer1',
        type: 'line',
        source: 'way_route',
        layout: {
          'line-join': 'round', // 线连接处的样式
          'line-cap': 'round' // 线结束处的样式
        },
        paint: {
          'line-color': '#fff', // 线的颜色
          'line-width': 4, // 线的宽度
          'line-opacity': 1.0 // 线的透明度
        }
      })
      this.map.addLayer({
        id: 'way_route_layer',
        type: 'line',
        source: 'way_route',
        layout: {
          'line-join': 'round', // 线连接处的样式
          'line-cap': 'round' // 线结束处的样式
        },
        paint: {
          'line-color': '#f00', // 线的颜色
          'line-width': 2, // 线的宽度
          'line-opacity': 1.0 // 线的透明度
        }
      })
      // 航点 图标 PS:home点 起飞点 航点 送餐点等
      this.lonLats.forEach((item, index) => {
        // home点
        if (index === 0) {
          // 第一点home点
          this.map.addSource('home_point', {
            type: 'geojson',
            data: {
              type: 'FeatureCollection',
              features: [
                {
                  type: 'Feature',
                  geometry: {
                    type: 'Point',
                    coordinates: item
                  },
                  properties: {
                    'marker-symbol': 'homePoint' // home点图标
                  }
                }
              ]
            }
          })
          this.map.addLayer({
            id: 'home_point_layer',
            type: 'symbol',
            source: 'home_point',
            layout: {
              'icon-image': '{marker-symbol}',
              'icon-size': 1.5, // 图标的大小
              'icon-allow-overlap': true
            }
          })
        } else if (index === 1) {
          // 起飞点
          let takeoffPoint
          // 起点图标设定 根据最后一个航点为RETURN_TO_LAUNCH 20 返回起点降落 或者 最后一个航点为LAND 21 指定点降落
          if (routeObj.questAss.tasks[this.lonLats.length - 1].command === 20) {
            takeoffPoint = 'takeoffLandPoint'
          } else if (
            routeObj.questAss.tasks[this.lonLats.length - 1].command === 21
          ) {
            takeoffPoint = 'takeoffPoint'
          }
          this.map.addSource('takeoff_point', {
            type: 'geojson',
            data: {
              type: 'FeatureCollection',
              features: [
                {
                  type: 'Feature',
                  geometry: {
                    type: 'Point',
                    coordinates: item
                  },
                  properties: {
                    'marker-symbol': takeoffPoint // 起飞点图标
                  }
                }
              ]
            }
          })
          this.map.addLayer({
            id: 'takeoff_point_layer',
            type: 'symbol',
            source: 'takeoff_point',
            layout: {
              'icon-image': '{marker-symbol}',
              'icon-size': 1.5, // 图标的大小
              'icon-allow-overlap': true
            }
          })
        } else {
          // waypoint点
          if (index !== this.lonLats.length - 1) {
            // 最后一个点不显示 要不和最后一个点结合 要不和起点结合
            let wayPoint = 'wayPoint'
            if (index === this.lonLats.length - 2) {
              // 降落点 如果是LAND 21 和最后一个waypoint点结合
              if (
                routeObj.questAss.tasks[this.lonLats.length - 1].command === 21
              ) {
                wayPoint = 'wayLandPoint'
              }
            }
            if (routeObj.questAss.tasks[index].command === 94) {
              // command字段为94既然 投放放勾航点 放勾图标
              wayPoint = 'hookPoint'
            }
            this.map.addSource('way_point' + index, {
              type: 'geojson',
              data: {
                type: 'FeatureCollection',
                features: [
                  {
                    type: 'Feature',
                    geometry: {
                      type: 'Point',
                      coordinates: item
                    },
                    properties: {
                      'marker-symbol': wayPoint
                    }
                  }
                ]
              }
            })
            this.map.addLayer({
              id: 'way_point_layer' + index,
              type: 'symbol',
              source: 'way_point' + index,
              layout: {
                'icon-image': '{marker-symbol}',
                'icon-size': 1.5, // 图标的大小
                'icon-allow-overlap': true
              }
            })
          }
        }
      })
    },
    /**
     * @abstract 创建指点飞行标记
     * @param {Array} lonLat - 经纬度数组，格式为 [longitude, latitude]
     */
    makeGuidedMarker (lonLat) {
      // 删除之前的所有标记
      this.delGuidedMarker()

      // 创建一个标记对象
      this.guidedMarker = new mapboxgl.Marker({
        draggable: false// 关闭拖拽
      })
        .setLngLat(lonLat)
        .setPopup(
          new mapboxgl.Popup({ offset: 32 }).setHTML(
            '<h3>指点</h3>'
          )
        ) // 添加弹出窗口
        .addTo(this.map)
    },
    /**
     * @abstract 删除指点飞行标记
     */
    delGuidedMarker () {
      if (this.guidedMarker) {
        this.guidedMarker.remove()
        this.guidedMarker = null // 清除当前标记
      }
    },
    /**
 * @description 清除历史轨迹所有轨迹线和轨迹点的图层与数据源
 * @param {Array<string>} lineLayerIds - 轨迹线图层ID数组
 * @param {Array<string>} pointLayerIds - 轨迹点图层ID数组
 */
    clearHistoryPaths (lineLayerIds = [], pointLayerIds = []) {
      if (!this.map) return

      // 清除轨迹线图层及对应数据源
      lineLayerIds.forEach(id => {
        if (this.map.getLayer(id)) {
          this.map.removeLayer(id)
        }
        if (this.map.getSource(id)) {
          this.map.removeSource(id)
        }
      })

      // 清除轨迹点图层及对应数据源
      pointLayerIds.forEach(id => {
        if (this.map.getLayer(id)) {
          this.map.removeLayer(id)
        }
        if (this.map.getSource(id)) {
          this.map.removeSource(id)
        }
      })
    },

    /**
 * @description 根据索引绘制一条历史轨迹的轨迹线和起点圆点
 * @param {Array<Array<number>>} pathArray - GPS坐标数组，格式为 [[lon, lat, ...], ...]
 * @param {number} index - 用于生成图层ID的索引，确保每条轨迹ID唯一
 */
    drawHistoryPathByIndex (pathArray, index) {
      if (!this.map) return
      if (!pathArray || pathArray.length === 0) return

      const zoomThreshold = 12 // 缩放等级阈值，决定显示轨迹线还是起点圆点
      const pointRadius = 6 // 起点圆点半径

      // 轨迹线GeoJSON对象
      const lineGeojson = {
        type: 'Feature',
        geometry: {
          type: 'LineString',
          coordinates: pathArray
        }
      }

      // 起点圆点GeoJSON对象，取轨迹的第一个坐标点
      const pointGeojson = {
        type: 'Feature',
        geometry: {
          type: 'Point',
          coordinates: pathArray[0]
        }
      }

      // 图层ID，保证唯一
      const lineLayerId = `path${index}`
      const pointLayerId = `path${index}-point`

      // 绘制轨迹线
      const drawLine = () => {
        if (this.map.getSource(lineLayerId)) return
        this.map.addSource(lineLayerId, {
          type: 'geojson',
          data: lineGeojson
        })
        this.map.addLayer({
          id: lineLayerId,
          type: 'line',
          source: lineLayerId,
          layout: {
            'line-join': 'round',
            'line-cap': 'round'
          },
          paint: {
            'line-color': '#3388ff',
            'line-width': 3
          }
        })
      }

      // 绘制起点圆点
      const drawPoint = () => {
        if (this.map.getSource(pointLayerId)) return
        this.map.addSource(pointLayerId, {
          type: 'geojson',
          data: pointGeojson
        })
        this.map.addLayer({
          id: pointLayerId,
          type: 'circle',
          source: pointLayerId,
          paint: {
            'circle-radius': pointRadius,
            'circle-color': '#3388ff',
            'circle-stroke-color': '#fff',
            'circle-stroke-width': 2
          }
        })
      }

      // 根据缩放等级切换显示轨迹线还是起点圆点
      const updateDisplay = () => {
        const zoom = this.map.getZoom()

        // 起点圆点一直显示，先确保点图层存在
        if (!this.map.getSource(pointLayerId)) {
          drawPoint()
        }

        // 轨迹线根据缩放决定显示或隐藏
        if (zoom >= zoomThreshold) {
          // 放大，显示轨迹线
          if (!this.map.getSource(lineLayerId)) {
            drawLine()
          }
        } else {
          // 缩小时隐藏轨迹线
          if (this.map.getLayer(lineLayerId)) {
            this.map.removeLayer(lineLayerId)
          }
          if (this.map.getSource(lineLayerId)) {
            this.map.removeSource(lineLayerId)
          }
        }
      }

      // 首次渲染轨迹显示
      updateDisplay()

      // 监听缩放事件，动态切换显示
      this.map.on('zoom', () => {
        updateDisplay()
      })
    },
    /**
     * @description: 创建飞机轨迹 ps:原理删除之前的轨迹 重新绘制 用于实时轨迹
     * @param {arr} coordinatesArray 飞机经纬高度数组
     */
    createPathWithArray (coordinatesArray, pathId = 'path') {
      const geojson = {
        type: 'Feature',
        properties: {},
        geometry: {
          type: 'LineString',
          coordinates: coordinatesArray.map((coord) => [
            coord[0],
            coord[1],
            coord[2]
          ])
        }
      }

      // 如果已有图层，更新数据
      if (this.map.getLayer(pathId)) {
        this.map.getSource(pathId).setData(geojson)
      } else {
        if (coordinatesArray.length > 0) {
          // 添加新的 source 和 layer
          this.map.addSource(pathId, {
            type: 'geojson',
            data: geojson
          })
          this.map.addLayer({
            id: pathId,
            type: 'line',
            source: pathId,
            layout: {
              'line-cap': 'round',
              'line-join': 'round'
            },
            paint: {
              'line-color': 'purple',
              'line-width': 2
            }
          })
        }
      }
    },
    /**
     * @description:在 Mapbox 地图上绘制一个带轮廓线的多边形区域，并可选添加标签文字。
     *
     * @param {Array<Array<number>>} coords - 多边形的经纬度坐标数组，格式为 [[lng, lat], [lng, lat], ...]
     * @param {string} id - 图层和数据源的唯一标识符前缀，用于创建 source 和 layer 的 ID
     * @param {string} fillColor - 多边形的填充颜色（如 '#F00' 表示红色）
     * @param {string} labelText - 要显示的标签文字（传空则不显示）
     */
    drawPolygonWithLabel (coords, id, fillColor, labelText) {
      // 添加 GeoJSON 数据源（多边形）
      this.map.addSource(id, {
        type: 'geojson',
        data: {
          type: 'Feature',
          geometry: {
            type: 'Polygon',
            coordinates: [coords]
          }
        }
      })

      // 添加填充图层（多边形区域）
      this.map.addLayer({
        id: id,
        type: 'fill',
        source: id,
        paint: {
          'fill-color': fillColor,
          'fill-opacity': 0.3
        }
      })

      // 添加轮廓图层（边框线）
      this.map.addLayer({
        id: `${id}-outline`,
        type: 'line',
        source: id,
        paint: {
          'line-color': fillColor,
          'line-width': 2
        }
      })

      // 如果传入了标签文字，则添加文字图层
      if (labelText) {
        // 简单方式计算中心点（可替换为更复杂的质心算法）
        const center = coords.reduce((acc, cur) => {
          acc[0] += cur[0]
          acc[1] += cur[1]
          return acc
        }, [0, 0]).map(v => v / coords.length)

        // 添加文字专用数据源
        this.map.addSource(`${id}-label`, {
          type: 'geojson',
          data: {
            type: 'Feature',
            geometry: {
              type: 'Point',
              coordinates: center
            },
            properties: {
              label: labelText
            }
          }
        })

        // 添加文字图层
        this.map.addLayer({
          id: `${id}-label-layer`,
          type: 'symbol',
          source: `${id}-label`,
          layout: {
            'text-field': ['get', 'label'],
            'text-size': 14,
            'text-font': ['Open Sans Bold', 'Arial Unicode MS Bold'],
            'text-offset': [0, 0.6],
            'text-anchor': 'top'
          },
          paint: {
            'text-color': fillColor,
            'text-halo-color': '#fff',
            'text-halo-width': 1.5
          }
        })
      }
    },
    /**
     * @description: 创建一个飞机
     * @param {*} plane 飞机对象
     */
    makePlane (plane, index = 0) {
      const customIcon = document.createElement('div')
      customIcon.className = 'custom-marker'
      // 根据在线状态与是否有货显示不同图标，空值安全处理
      // 约定: online === false 才判定为离线；其他(包括 undefined/null) 默认为在线
      const isOnline = !((plane && plane.planeState && plane.planeState.online) === false)
      const hasGoods = !!(plane && plane.planeState && plane.planeState.hasGoods)
      const iconToUse = !isOnline ? unlineIcon : (hasGoods ? planeRunIcon : planeIcon)
      // 使用 important 确保覆盖样式表中的背景图设置
      customIcon.style.setProperty('background-image', `url(${iconToUse})`, 'important')
      customIcon.style.setProperty('background-repeat', 'no-repeat', 'important')
      customIcon.style.setProperty('background-size', 'contain', 'important')
      customIcon.style.width = '64px'
      customIcon.style.height = '64px'

      const marker = new mapboxgl.Marker(customIcon)
        .setLngLat([plane.lon, plane.lat])
        .addTo(this.map)

      // 创建popup，但不绑定到marker，手动控制显示
      const popup = new mapboxgl.Popup({
        offset: 25,
        closeButton: true,
        closeOnClick: true
      }).setHTML(`<h3>${plane.name}</h3>`)
      marker.setPopup(popup)

      // 点击图标，弹出对应popup（每次点击都打开对应popup）
      customIcon.addEventListener('click', (e) => {
        e.stopPropagation() // 阻止地图的click事件影响
        popup.setLngLat([plane.lon, plane.lat]).addTo(this.map)
      })

      this.planes[index] = marker // 存储marker到公用数组中
    },
    /**
     * @description: 移除页面上的所有飞机
     */
    removePlanes () {
      this.planes.forEach((plane) => {
        plane.remove()
      })
      this.planes = []
    },
    /**
     * @description: 创建 ADSB 飞机图标并添加到地
     * @param {Array} adsbList - ADSB 飞机数据列表
     */
    makeADSBPlanes (adsbList = []) {
      // 清除旧图标
      if (this.ADSBMarkers && this.ADSBMarkers.length) {
        this.ADSBMarkers.forEach(marker => marker.remove())
      }
      this.ADSBMarkers = []

      adsbList.forEach((plane) => {
        // 创建图标容器
        const iconWrapper = document.createElement('div')
        iconWrapper.className = 'adsb-wrapper'
        iconWrapper.style.width = '36px'
        iconWrapper.style.height = '36px'

        // 创建真正的飞机图标元素（子元素旋转！父级无法控制transiton）
        const planeIcon = document.createElement('div')
        planeIcon.className = 'adsb-icon'
        planeIcon.style.backgroundImage = `url(${civilIcon})`
        planeIcon.style.width = '100%'
        planeIcon.style.height = '100%'
        planeIcon.style.backgroundSize = 'contain'
        planeIcon.style.backgroundRepeat = 'no-repeat'
        planeIcon.style.transform = `rotate(${plane.heading}deg)`
        planeIcon.style.transition = 'transform 0.3s linear'

        // 加入子元素
        iconWrapper.appendChild(planeIcon)

        const popupContent = `
      <h3>ADSB 飞机</h3>
      <hr>
      <p>ICAO: ${plane.icao}</p>
      <p>高度: ${plane.alt} m</p>
      <p>航向: ${plane.heading}°</p>
    `

        const marker = new mapboxgl.Marker(iconWrapper)
          .setLngLat([plane.lon, plane.lat])
          .setPopup(new mapboxgl.Popup({ offset: 25 }).setHTML(popupContent))
          .addTo(this.map)

        this.ADSBMarkers.push(marker)
      })
    },
    /**
     * @description: 实时更新经纬度
     * @param {obj} lonLat lon经度 lat纬度
     * @param {*} index 飞机序号
     * @param {*} pathArr 飞机轨迹 痕迹坐标数组
     */
    setPlaneLonLat (lonLat, index, pathArr) {
      // 设置新的经纬度
      const plane = this.planes[index]
      if (plane != null) {
        plane.setLngLat([lonLat.lon, lonLat.lat])
      }
      // 创建轨迹，路径 ID 每架飞机独有
      this.createPathWithArray(pathArr, `path-${index}`)
      // 镜头跟随飞机
      if (this.isflow) {
        this.map.flyTo({
          center: lonLat,
          speed: 2,
          curve: 1,
          easing (t) {
            return t
          }
        })
      }
    },
    /**
     * @description 实时更新地图上指定飞机图标的弹窗内容（Popup）
     * @param {Object} stateObj 飞机状态对象，包含飞机的状态信息
     * @param {number} index 飞机对象索引 ps:用这个索引 取本组件组件共有变量planes对象组 指定对象
     */
    updatePopupContent (stateObj, index = 0) {
      const plane = this.planes[index]
      if (!plane) return

      // 选出最后一条位置数据
      const lastPos = stateObj.position?.[stateObj.position.length - 1] ?? []

      // 心跳图标样式
      const heartClass = stateObj.online
        ? 'heart-icon heart-online'
        : 'heart-icon heart-offline'

      // 拼接弹窗内容 HTML
      const popupContent = `
        <style>
          @keyframes heartbeat {
            0%, 100% { transform: scale(1); }
            50% { transform: scale(1.3); }
          }
          .heart-icon {
            display: inline-block;
            width: 12px;
            height: 12px;
            margin-left: 5px;
            border-radius: 50%;
          }
          .heart-online {
            background-color: green;
            animation: heartbeat 1s infinite;
          }
          .heart-offline {
            background-color: gray;
          }
        </style>

        <h3>${stateObj.name}</h3>
        <hr>
        <p><strong>心跳:</strong> <span class="${heartClass}"></span></p>
        <p><strong>解锁状态:</strong> 
          <span style="color:${stateObj.isUnlock ? 'green' : ''}">
            ${stateObj.isUnlock ? '已解锁' : '未解锁'}
          </span>
        </p>
        <p><strong>模式:</strong> ${stateObj.getPlaneMode ?? '未知'}</p>
        <p><strong>定位状态:</strong> ${stateObj.fixType ?? '--'}</p>
        <p><strong>卫星颗数:</strong> ${stateObj.satCount ?? '--'}</p>
        <p><strong>电压:</strong> ${stateObj.voltagBattery ?? '--'} V</p>
        <p><strong>电流:</strong> ${stateObj.currentBattery ?? '--'} A</p>
        <p><strong>高度:</strong> ${lastPos[2] ?? '--'} 米</p>
        <p><strong>经度:</strong> ${lastPos[0] ?? '--'}°</p>
        <p><strong>纬度:</strong> ${lastPos[1] ?? '--'}°</p>
        <p><strong>对地速度:</strong> ${stateObj.groundSpeed ?? '--'} 米/秒</p>
      `

      const popup = plane.getPopup()
      if (popup) {
        popup.setHTML(popupContent)
      }
    },
    /**
     * @description 实时根据在线状态与载重更新指定飞机的图标
     * @param {Object} stateObj 飞机状态对象（需包含 online, loadweight）
     * @param {number} index 对应的飞机索引
     */
    updatePlaneIcon (stateObj, index = 0) {
      const marker = this.planes[index]
      if (!marker) return
      const el = marker.getElement()
      if (!el) return
      // 约定: online === false 才判定为离线；其他(包括 undefined/null) 默认为在线
      const isOnline = !((stateObj && stateObj.online) === false)
      const hasGoods = !!(stateObj && stateObj.hasGoods)
      const iconToUse = !isOnline ? unlineIcon : (hasGoods ? planeRunIcon : planeIcon)
      // 使用 important 确保覆盖样式表中的背景图设置
      el.style.setProperty('background-image', `url(${iconToUse})`, 'important')
      el.style.setProperty('background-repeat', 'no-repeat', 'important')
      el.style.setProperty('background-size', 'contain', 'important')
      // 调试信息，观察状态与最终图标选择
      try {
        el.dataset.icon = !isOnline ? 'unline' : (hasGoods ? 'run' : 'normal')
        console.debug('[updatePlaneIcon]', { index, online: stateObj && stateObj.online, hasGoods, chosen: el.dataset.icon })
      } catch (e) { /* noop */ }
    },
    /**
     * @description: 镜头跳转
     * @param {obj} lonLat {lon:lon,lat:lat} 经纬度
     * @param {Number} zoom 地图放大率
     */
    goto (lonLat, zoom = 12) {
      this.map.flyTo({
        center: lonLat,
        zoom: zoom,
        speed: 2,
        curve: 1,
        easing (t) {
          return t
        }
      })
    },
    /**
     * @description: 屏幕横移 纵移
     * @param {*} x 正数向左移动 负数向右移动
     * @param {*} y 正数向上移动 负数向下移动
     */
    mapXOffset (x, y) {
      this.map.panBy([x, y], {
        duration: 333 // 过渡持续时间，以毫秒为单位
      })
    },
    /**
     * @description:重置地图大小
     */
    handleResize () {
      if (this.map) {
        setTimeout(() => {
          this.map.resize()
        }, 200)
      }
    },
    /**
     * @description: 点飞 - 绘制引导线与中心点 ps: 该方法用于在地图上绘制从中心点到点击点的引导线，以及每架飞机到终点的拓扑偏移线。
     * @param {Object} centerPoint - 中心点经纬度对象 {lng: number, lat: number}
     * @param {Object} endPoint - 点击点经纬度对象 {lng: number, lat: number}
     * @param {Array} planeCoords - 飞机坐标数组 [[lng, lat], ...]
     */
    drawGuidedLines ({ centerPoint, endPoint, planeCoords }) {
      const sourceId = 'guided-lines-source'
      const lineLayerId = 'guided-lines-layer'
      const pointLayerId = 'guided-center-point-layer'

      // 清除旧图层和数据源
      if (this.map.getLayer(lineLayerId)) this.map.removeLayer(lineLayerId)
      if (this.map.getLayer(pointLayerId)) this.map.removeLayer(pointLayerId)
      if (this.map.getSource(sourceId)) this.map.removeSource(sourceId)

      const deltaLng = endPoint.lng - centerPoint.lng
      const deltaLat = endPoint.lat - centerPoint.lat

      const features = []

      // 🔵 每架飞机 → 拓扑偏移终点 引导线
      planeCoords.forEach(([lng, lat]) => {
        const targetLng = lng + deltaLng
        const targetLat = lat + deltaLat

        features.push({
          type: 'Feature',
          geometry: {
            type: 'LineString',
            coordinates: [[lng, lat], [targetLng, targetLat]]
          },
          properties: {
            color: 'blue',
            type: 'line'
          }
        })
      })

      // 🔴 中心点 → 点击点 引导线
      features.push({
        type: 'Feature',
        geometry: {
          type: 'LineString',
          coordinates: [[centerPoint.lng, centerPoint.lat], [endPoint.lng, endPoint.lat]]
        },
        properties: {
          color: 'red',
          type: 'line'
        }
      })

      // 🔴 中心点标记
      features.push({
        type: 'Feature',
        geometry: {
          type: 'Point',
          coordinates: [centerPoint.lng, centerPoint.lat]
        },
        properties: {
          color: 'red',
          type: 'center-point'
        }
      })

      // 添加 GeoJSON 源
      this.map.addSource(sourceId, {
        type: 'geojson',
        data: {
          type: 'FeatureCollection',
          features
        }
      })

      // 添加线图层
      this.map.addLayer({
        id: lineLayerId,
        type: 'line',
        source: sourceId,
        filter: ['==', ['get', 'type'], 'line'],
        layout: {
          'line-cap': 'round',
          'line-join': 'round'
        },
        paint: {
          'line-color': ['get', 'color'],
          'line-width': 2,
          'line-dasharray': [2, 2]
        }
      })

      // 添加中心点图层（圆点）
      this.map.addLayer({
        id: pointLayerId,
        type: 'circle',
        source: sourceId,
        filter: ['==', ['get', 'type'], 'center-point'],
        paint: {
          'circle-radius': 6,
          'circle-color': ['get', 'color'],
          'circle-stroke-color': '#fff',
          'circle-stroke-width': 2
        }
      })
    }

  },
  beforeDestroy () {
    if (this.map) {
      this.$store.commit('app/setDefaultLonLat', this.map.getCenter())
      this.$store.commit('app/setDefaultZoom', this.map.getZoom())
    }
  }
}

</script>

<style lang="scss" scoped>
#map {
  width: 100%;
  height: 100%;
  overflow: hidden;
}

.custom-marker {
  background-size: cover;
  cursor: pointer;
}

.adsb-wrapper {
  display: flex;
  justify-content: center;
  align-items: center;
}

.adsb-icon {
  will-change: transform;
}

/*飞机popup 心跳图标样式*/
@keyframes heartbeat {

  0%,
  100% {
    transform: scale(1);
  }

  50% {
    transform: scale(1.3);
  }
}

.heart-icon {
  display: inline-block;
  width: 16px;
  height: 16px;
  margin-left: 5px;
  border-radius: 50%;
}

.heart-online {
  background-color: green;
  animation: heartbeat 1s infinite;
}

.heart-offline {
  background-color: gray;
}
</style>
