<template>
  <div ref="mapContainerRef" class="map-container">
    <template v-if="map">
      <slot></slot>
    </template>
  </div>
</template>
<script>
import TileLayer from 'ol/layer/Tile'
import Map from 'ol/Map'
import XYZ from 'ol/source/XYZ'
import View from 'ol/View'
import { fromLonLat } from 'ol/proj'
import Stroke from 'ol/style/Stroke'
import { MarkerIcon, MarkerType } from './markerEnum'
import Icon from 'ol/style/Icon'
import VectorSource from 'ol/source/Vector'
import VectorLayer from 'ol/layer/Vector'
import { LineString, Point } from 'ol/geom'
import { Feature } from 'ol'
import Style from 'ol/style/Style'

const mapSourceUrl = process.env.VUE_APP_MAP_BASE_URL
export default {
  props: {
    zoom: {
      type: Number,
      default: 1,
    },
    maxZoom: {
      type: Number,
      default: 16,
    },
    minZoom: {
      type: Number,
      default: 1,
    },
    center: {
      type: Object,
      default() {
        return {
          longitude: 116,
          latitude: 40,
        }
      },
    },
  },
  data() {
    return {
      map: null,
      stationList: [],
    }
  },
  mounted() {
    this.initMap()
  },
  methods: {
    // 初始化地图
    initMap() {
      const { longitude, latitude } = this.center

      this.tileLayer = new TileLayer({
        source: new XYZ({
          url: mapSourceUrl,
        }),
      })

      this.animationSource = new VectorSource({
        features: [],
      })

      this.animationLayer = new VectorLayer({
        source: this.animationSource,
      })

      const layers = [this.tileLayer, this.animationLayer]

      this.view = new View({
        projection: 'EPSG:3857', // 使用这个坐标系
        center: fromLonLat([longitude, latitude]),
        zoom: this.zoom,
        maxZoom: this.maxZoom,
        minZoom: this.minZoom,
        extent: [-20037508.34 - 3500000, -20037508.34, 20037508.34 + 3500000, 20037508.34],
      })

      this.map = new Map({
        target: this.$refs.mapContainerRef,
        layers,
        view: this.view,
        controls: [],
      })
    },

    /**
     * 以下是工具方法
     */
    // 获取地图缩放级别
    getZoom() {
      return this.map.getView().getZoom()
    },

    // 设置地图缩放级别
    setZoom(zoom) {
      if (zoom < this.minZoom) {
        zoom = this.minZoom
      }
      if (zoom > this.maxZoom) {
        zoom = this.maxZoom
      }
      this.map.getView().animate({ zoom })
    },

    getMapInstance() {
      return this.map
    },

    // 获取地图中心点
    getCenter() {
      return this.map.getView().getCenter()
    },

    // 平移到某个位置
    panTo(center, duration = 1000) {
      return this.map.getView().animate({
        center: fromLonLat(center),
        duration,
      })
    },

    // 修改地图来源
    changeSource(url) {
      this.tileLayer.setSource(
        new XYZ({
          url,
        })
      )
    },

    // 按路线运动
    animateByRoute(coordinates) {
      if (!coordinates) {
        return
      }

      if (!coordinates.length) {
        this.stopRouteAnimation()
        this.removeAnimationFeatures()
        return
      }

      this.startRouteAnimation(
        coordinates,
        {
          stroke: new Stroke({
            color: '#17f85c',
            lineDash: [6],
            width: 2,
          }),
        },
        {
          image: new Icon({
            src: MarkerIcon[MarkerType.Car],
            anchor: [0.65, 0.5],
          }),
        },
        [100, 430, 100, 450]
      )
    },
    /**
     * 沿轨迹运动
     * @param {Array<any>} route 轨迹路线
     * @param {Object} marker 运动的物体
     */
    startRouteAnimation(route, lineStyleOptions, markerStyleOptions, padding = []) {
      // 清理上次的动画
      this.stopRouteAnimation()

      // 清理动画图层
      this.removeAnimationFeatures()

      const lineString = new LineString(route.map((item) => fromLonLat(item)))
      const extent = lineString.getExtent()
      this.view.fit(extent, {
        padding,
        duration: 1000,
      })

      // 创建轨迹线
      const routeFeature = new Feature({
        geometry: lineString,
      })

      // 设置轨迹线样式，并添加到地图上
      routeFeature.setStyle(new Style(lineStyleOptions))
      this.animationSource.addFeature(routeFeature)

      // 创建标记物，并设置位置到轨迹路线的初始位置
      const markerFeature = new Feature({
        geometry: new Point(lineString.getFirstCoordinate()),
      })

      // 设置标记物样式，并添加到地图上
      markerFeature.setStyle(new Style(markerStyleOptions))
      this.animationSource.addFeature(markerFeature)

      let lastTime = Date.now(),
        distance = 0
      this.postRenderCallback = (event) => {
        const speed = 60
        const time = event.frameState.time
        const elapsedTime = time - lastTime
        distance = (distance + (speed * elapsedTime) / 1e6) % 2
        lastTime = time

        if (distance >= 1) {
          this.stopRouteAnimation()
          return
        }

        const currentCoordinate = lineString.getCoordinateAt(distance > 1 ? 2 - distance : distance)
        markerFeature.setGeometry(new Point(currentCoordinate))

        // tell OpenLayers to continue the postrender animation
        this.map.render()
      }

      this.animationLayer.on('postrender', this.postRenderCallback)
    },

    // 移除动画层所有元素
    removeAnimationFeatures() {
      this.animationSource.clear()
    },

    // 停止轨迹动画
    stopRouteAnimation() {
      if (this.postRenderCallback) {
        this.animationLayer.un('postrender', this.postRenderCallback)
        this.postRenderCallback = null
      }
    },
  },
}
</script>
<style lang="less" scoped>
.map-container {
  height: 100%;
  position: relative;
}
</style>
