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

<script>
import mapboxgl from 'mapbox-gl'
import { MapboxStyleSwitcherControl, FollowControl, CustomFullscreenControl, NoFlyControl } from '@/utils/mapboxgl_plugs'
import planeIcon from '@/assets/svg/plane.svg'

export default {
  name: 'MapBox',
  data () {
    return {
      token: 'pk.eyJ1Ijoic3pkb3QiLCJhIjoiY2xhN2pkMWFoMHJ4eTN3cXp6bmlzaHZ0NCJ9.3hH-EAUr0wQCaLvIM2lBMQ',
      map: null,
      planes: [], // 飞机对象
      lonLats: [], // 航线 所有航点
      wayLonLats: [], // 航线 不包括起点航点
      takeoffLonLats: [], // 航线 第一个航点 起点 最后一个航点
      isflow: false, // 飞机经纬度变化时是否跟随飞机
      currentStyleIndex: 0, // 当前选中的地图样式索引
      guidedMarker: null, // 指点飞行地图标记
      // 地图样式
      mapStyles: [
        // mapbox官方样式 卫星地图
        {
          name: '谷歌卫星',
          sprite: this.$store.state.settings.host + '/Public/map/sprite',
          glyphs: 'mapbox://fonts/mapbox/{fontstack}/{range}.pbf',
          version: 8,
          sources: {
            google: {
              type: 'raster',
              tileSize: 256,
              tiles: ['https://sb.im/google-maps/vt?lyrs=s&x={x}&y={y}&z={z}']
            }
          },
          layers: [{ id: 'GoogleRasterLayer', type: 'raster', source: 'google' }]
        },
        {
          name: '必应卫星',
          sprite: this.$store.state.settings.host + '/Public/map/sprite',
          glyphs: 'mapbox://fonts/mapbox/{fontstack}/{range}.pbf',
          version: 8,
          sources: {
            bing: {
              type: 'raster',
              tileSize: 256,
              tiles: [
                'https://ecn.t3.tiles.virtualearth.net/tiles/a{quadkey}.jpeg?g=1'
              ],
              attribution: '© Microsoft, Bing Maps'
            }
          },
          layers: [{ id: 'BingRasterLayer', type: 'raster', source: 'bing' }]
        },
        {
          name: 'Mapbox Streets',
          sprite: 'mapbox://sprites/mapbox/streets-v11',
          glyphs: 'mapbox://fonts/mapbox/{fontstack}/{range}.pbf',
          version: 8,
          sources: {
            mapbox: {
              type: 'raster',
              tileSize: 256,
              tiles: ['https://api.mapbox.com/styles/v1/mapbox/streets-v11/tiles/{z}/{x}/{y}?access_token=YOUR_MAPBOX_ACCESS_TOKEN']
            }
          },
          layers: [{ id: 'MapboxStreetsLayer', type: 'raster', source: 'mapbox' }]
        },
        {
          name: 'Google Maps',
          sprite: this.$store.state.settings.host + '/Public/map/sprite',
          glyphs: 'mapbox://fonts/mapbox/{fontstack}/{range}.pbf',
          version: 8,
          sources: {
            google: {
              type: 'raster',
              tileSize: 256,
              tiles: ['https://mt1.google.com/vt/lyrs=m&x={x}&y={y}&z={z}']
            }
          },
          layers: [{ id: 'GoogleMapsLayer', type: 'raster', source: 'google' }]
        },
        {
          name: 'OpenStreetMap',
          sprite: this.$store.state.settings.host + '/Public/map/sprite',
          glyphs: 'mapbox://fonts/mapbox/{fontstack}/{range}.pbf',
          version: 8,
          sources: {
            osm: {
              type: 'raster',
              tileSize: 256,
              tiles: ['https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png']
            }
          },
          layers: [{ id: 'OSMRasterLayer', type: 'raster', source: 'osm' }]
        },
        // 第三方 高德矢量
        {
          name: '高德矢量',
          sprite: this.$store.state.settings.host + '/Public/map/sprite',
          glyphs: 'mapbox://fonts/mapbox/{fontstack}/{range}.pbf',
          version: 8,
          sources: {
            gaode: {
              type: 'raster',
              tileSize: 256, // 瓦片大小 256 512像素
              tiles: [
                'http://wprd04.is.autonavi.com/appmaptile?lang=zh_cn&size=1&style=7&x={x}&y={y}&z={z}'
              ]
            }
          },
          layers: [{ id: 'GaodeVectorLayer', type: 'raster', source: 'gaode' }]
        },
        // 第三方 高德卫星
        {
          name: '高德卫星',
          sprite: this.$store.state.settings.host + '/Public/map/sprite',
          glyphs: 'mapbox://fonts/mapbox/{fontstack}/{range}.pbf',
          version: 8,
          sources: {
            gaode: {
              type: 'raster',
              tileSize: 256, // 瓦片大小 256 512像素
              tiles: [
                'https://webst02.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}'
              ]
            }
          },
          layers: [{ id: 'GaodeRasterLayer', type: 'raster', source: 'gaode' }]
        }
      ]
    }
  },
  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
    },
    enableNofly: { // 禁飞区 设置 组件
      type: Boolean,
      default: false
    }
  },
  computed: {
    defaultLonlat () {
      return this.$store.getters['app/getDefaultLonLat']
    },
    defaultZoom () {
      return this.$store.getters['app/getDefaultZoom']
    }
  },
  mounted () {
    this.init().then(() => {
      // 地图初始化之后
      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 })
      })

      // 判断地图开启了点飞功能
      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: this.map.unproject(currentPoint).lng,
              lat: this.map.unproject(currentPoint).lat
            }
            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.mapStyles[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-right')
      }
      // 全屏 并再之后 刷新地图
      if (this.enableZoom) {
        this.map.addControl(new CustomFullscreenControl(this.handleResize), 'top-right')
      }

      // 地图样式选择控件
      // 自定义地图样式列表
      if (this.enableSwitch) {
        const styleSwitcherControl = new MapboxStyleSwitcherControl(this.mapStyles, 'iconfont icon-duozhang f-s-20', this.currentStyleIndex)
        this.map.addControl(styleSwitcherControl, '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')
      }

      // 禁飞区 设置组件
      if (this.enableNofly) {
        const defaultPolygons = [
          [
            [113.0, 23.0],
            [113.1, 23.0],
            [113.1, 23.1],
            [113.0, 23.1],
            [113.0, 23.0]
          ],
          [
            [113.2, 23.2],
            [113.3, 23.2],
            [113.3, 23.3],
            [113.2, 23.3],
            [113.2, 23.2]
          ],
          [
            [113.2, 24.2],
            [113.5, 23.1],
            [113.5, 23.2],
            [113.3, 23.2],
            [113.3, 23.3],
            [113.2, 23.3],
            [113.4, 23.2]
          ]
        ]
        this.map.addControl(new NoFlyControl({
          defaultPolygons,
          onSave: (polygons) => {
            console.log('polygons', polygons)
          }
        }), 'top-left')
      }
    },
    /**
     * @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: 创建飞机轨迹 ps:原理删除之前的轨迹 重新绘制
     * @param {arr} coordinatesArray 飞机经纬高度数组
     */
    createPathWithArray (coordinatesArray) {
      // 创建一个包含纬度、经度和高度信息的 GeoJSON LineString
      const geojson = {
        type: 'Feature',
        properties: {},
        geometry: {
          type: 'LineString',
          coordinates: coordinatesArray.map((coord) => [
            coord[0],
            coord[1],
            coord[2]
          ])
        }
      }
      // 不是第一次 则改变路径图层里面得数据
      if (this.map.getLayer('path')) {
        this.map.getSource('path').setData(geojson)
      } else {
        // 第一次在地图里添加路径图层
        // 如果坐标数组不为空，创建新路径
        if (coordinatesArray.length > 0) {
          // 添加3D图层
          this.map.addLayer({
            id: 'path',
            type: 'line',
            source: {
              type: 'geojson',
              data: geojson
            },
            layout: {
              'line-cap': 'round',
              'line-join': 'round'
            },
            paint: {
              'line-color': 'purple',
              'line-width': 2
            }
          })
        }
      }
    },
    /**
     * @description: 创建一个飞机
     * @param {*} plane 飞机对象
     */
    makePlane (plane, index = 0) {
      const customIcon = document.createElement('div')
      customIcon.className = 'custom-marker' // 添加自定义样式类名
      customIcon.style.backgroundImage = `url(${planeIcon})` // 使用引入的 SVG 图标
      customIcon.style.width = '64px' // 图标宽度
      customIcon.style.height = '64px' // 图标高度
      // 创建一个marker对象
      this.planes[index] = new mapboxgl.Marker(customIcon)
        .setLngLat([plane.lon, plane.lat])
        .setPopup(
          new mapboxgl.Popup({ offset: 25 }).setHTML(
            '<h3>' + plane.name + '</h3><hr><p>macID:' + plane.macadd + '</p>'
          )
        ) // 添加弹出窗口
        .addTo(this.map)
    },
    /**
     * @description: 移除页面上的所有飞机
     */
    removePlanes () {
      this.planes.forEach((plane) => {
        plane.remove()
      })
      this.planes = []
    },
    /**
     * @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])
      }
      // 创建轨迹
      this.createPathWithArray(pathArr) // 创建轨迹
      // 镜头跟随飞机
      if (this.isflow) {
        this.map.flyTo({
          center: lonLat,
          speed: 2,
          curve: 1,
          easing (t) {
            return t
          }
        })
      }
    },
    /**
     * @description: 镜头跳转
     * @param {obj} lonLat {lon:lon,lat:lat} 经纬度
     */
    goto (lonLat) {
      this.map.flyTo({
        center: lonLat,
        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)
      }
    }
  },
  beforeDestroy () {
    if (this.map) {
      this.$store.commit('app/setDefaultLonLat', this.map.getCenter()) // 记录当前经纬度 缺省
      this.$store.commit('app/setDefaultZoom', this.map.getZoom()) // 记录当前经纬度 缺省
      this.map.remove() // 清除地图资源
    }
  },
  watch: {
  }
}

</script>

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

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

</style>
