<template lang="pug">
div
  div(v-if="!hasBmView", ref="view", style="width: 100%; height: 100%")
  slot
</template>

<script>
import Vue from 'vue'
import { GeometryType } from '@/base/GeometryType.js'
import {getFeatureByMaxZIndex} from '@/base/util.js'
import FullSrceen from '@/controls/FullScreen.js'
import RotateControl from '@/controls/RotateControl.js'
const _panFactor = 0.0005
export default {
  name: 'ol-map',
  props: {
    center: {
      type: Array,
      required: true
    },
    zoom: {
      type: Number,
      default: 2
    },
    minZoom: {
      type: Number,
      default: 0
    },
    maxZoom: {
      type: Number,
      default: 28
    },
    extent: {
      type: Array,
      default: function () {
        return []
      }
    },
    projection: {
      type: String,
      default: 'EPSG:4326'
    },
    rotation: {
      type: Number,
      default: 0
    },
    zoomFactor: {
      type: Number,
      default: 2
    },
    resolution: {
      type: Number
    },
    resolutions: {
      type: Array,
      default: function () {
        return []
      }
    },
    maxResolution: {
      type: Number
    },
    minResolution: {
      type: Number
    },
    updateSize: {
      type: Boolean,
      default: false
    },
    showFullSrceen: {
      type: Boolean,
      default: false
    },
    showRotate: {
      type: Boolean,
      default: false
    }
  },
  data () {
    return {
      hasBmView: false,
      olmap: null
    }
  },
  watch: {
    center (val) {
      const { olmap, center } = this
      olmap.getView().setCenter(center)
    },
    zoom (val) {
      const { olmap } = this
      olmap.getView().setZoom(val)
    },
    minZoom (val) {
      const { olmap } = this
      olmap.setMinZoom(val)
    },
    maxZoom (val) {
      const { olmap } = this
      olmap.setMaxZoom(val)
    },
    updateSize (val) {
      this.olmap.updateSize()
    },
    extent(mapExtent) {
      this.olmap.getView().fit(mapExtent, { constrainResolution: false })
    }
  },
  methods: {
    init () {
      let $el = this.$refs.view // 地图布局，地图是用容器渲染-------
      for (let $node of this.$slots.default || []) {
        if ($node.componentOptions && $node.componentOptions.tag === 'bs-view') {
          this.hasBmView = true
          $el = $node.elm
        }
      }
      const {ol, zoom, center, minZoom, maxZoom, showFullSrceen, showRotate, projection} = this
      var {resolutions, extent} = this
      if (resolutions && resolutions.length === 0) {
        resolutions = undefined
      }
      if (extent && extent.length === 0) {
        extent = undefined
      }
      var view = new ol.View({
        projection,
        center,
        zoom,
        resolutions,
        minZoom,
        maxZoom,
        extent
      })
      var controls = ol.control.defaults({ attribution: false, zoom: false })
      if (showFullSrceen) {
        controls.extend([new FullSrceen()])
      }
      if (showRotate) {
        controls.extend([new RotateControl()])
      }
      const map = new ol.Map({
        target: $el,
        controls,
        view,
        interactions: ol.interaction.defaults({doubleClickZoom: false})
      })
      this.olmap = map
      Vue.prototype.$map = map
      this.$emit('ready', {
        ol,
        map
      })
      // 地图移动结束事件
      this.olmap.on('moveend', ev => {
        // let loc = ev.coordinate
        // this.$emit('moveend', { lon: loc[0], lat: loc[1] })
      })
      // 地图开始移动事件
      this.olmap.on('movestart', ev => {
        // let loc = ev.coordinate
        // this.$emit('movestart', { lon: loc[0], lat: loc[1] })
      })
      // 鼠标拖拽事件
      this.olmap.on('pointerdrag', ev => {
        let loc = ev.coordinate
        this.$emit('mousedrag', { lon: loc[0], lat: loc[1] })
      })
      // 鼠标移动事件
      this.olmap.on('pointermove', ev => {
        let loc = ev.coordinate
        this.$emit('mousemove', { lon: loc[0], lat: loc[1], dragging: ev.dragging })
      })
      // 鼠标单击事件
      this.olmap.on('click', ev => {
        let loc = ev.coordinate
        var featurelist = []
        var center = []
        var obj = null
        this.forEachFeatureAtPixel(ev.pixel, (fea, layer) => {
          if (layer) {
            var zIndex = layer && layer.getZIndex()
            featurelist.push([fea, zIndex])
          }
        })
        var feature = getFeatureByMaxZIndex(featurelist)
        if (feature) {
          var type = feature.getGeometry().getType()
          if (type === GeometryType.POINT) {
            center = feature.getGeometry().getCoordinates()
          }
          if (type === GeometryType.POLYGON) {
            center = feature.getGeometry().getInteriorPoint().getCoordinates()
          }
          if (type === GeometryType.MULTIPOLYGON) {
            const bounds = feature.getGeometry().getExtent()
            center = [(bounds[0] + bounds[2]) / 2, (bounds[1] + bounds[3]) / 2]
          }
          if (type === GeometryType.POLYLINE) {
            center = feature.getGeometry().getLastCoordinate()
          }
          obj = {
            lon: loc[0],
            lat: loc[1],
            feature,
            attributes: feature.get('attributes'),
            type: type,
            center: center
          }
        } else {
          obj = {
            lon: loc[0],
            lat: loc[1]
          }
        }
        this.$emit('click', obj)
      })
      // 鼠标单次单击事件
      this.olmap.on('singleclick', ev => {
        let loc = ev.coordinate
        this.$emit('singleclick', { lon: loc[0], lat: loc[1] })
      })
      // 鼠标双击事件
      this.olmap.on('dblclick', ev => {
        let loc = ev.coordinate
        this.$emit('dblclick', { lon: loc[0], lat: loc[1] })
      })
      // 地图渲染事件----渲染前
      this.olmap.on('precompose', ev => {
        this.$emit('precompose')
        // console.log('地图渲染前------地图渲染事件')
      })
      // 地图渲染事件----渲染后
      this.olmap.on('postcompose', ev => {
        this.$emit('postcompose', ev)
        // console.log('地图渲染后------地图渲染事件')
      })
      // 地图事件，渲染后----
      this.olmap.on('postrender', ev => {
        this.$emit('postrender')
        // console.log('地图渲染前------地图事件')
      })
      // 地图事件，渲染后----
      this.olmap.on('change:layerGroup', ev => {
        // console.log('地图事件---图层发生变化----')
      })
      // 地图事件，渲染后----
      this.olmap.on('change:view', ev => {
        // console.log('地图视图发生改变-----')
        // console.log(ev)
      })
      // 地图缩放事件
      this.olmap.getView().on('change:resolution', ev => {
        var zoom = this.olmap.getView().getZoom()
        var center = this.olmap.getView().getCenter()
        var extent = this.olmap.getView().calculateExtent(this.olmap.getSize())
        this.$emit('zoom', { zoom, center, extent })
      })
    },
    centerAndZoom (x, y, level) {
      this.centerAt(x, y)
      this.setLevel(level)
    },
    centerAt (x, y) {
      this.olmap.getView().setCenter([x, y])
    },
    centerAtWithOffset (x, y, offX, offY) {
      var _screenP = this.toScreen(x, y)
      _screenP.x = _screenP.x + offX
      _screenP.y = _screenP.y + offY
      var _mapP = this.toMap(_screenP.x, _screenP.y)
      this.centerAt(_mapP.x, _mapP.y)
    },
    setLevel (level) {
      this.olmap.getView().setZoom(level)
    },
    moveToLeft () {
      var view = this.olmap.getView()
      var mapCenter = view.getCenter()
      // 让地图中心的x值增加，即可使得地图向左移动，增加的值根据效果可自由设定
      mapCenter[0] += _panFactor
      view.setCenter(mapCenter)
      this.olmap.render()
    },
    moveToRight () {
      var view = this.olmap.getView()
      // var extent = this.getCurrentExtent()
      // console.log('地图范围：' + extent)
      // var level = this.getLevel()
      // console.log('地图级别：' + level)
      //  var mapCenter = ol.extent.getCenter([extent.minX, extent.minY, extent.maxX, extent.maxY])
      var mapCenter = view.getCenter()
      // 让地图中心的x值减少，即可使得地图向右移动，减少的值根据效果可自由设定
      mapCenter[0] -= _panFactor
      view.setCenter(mapCenter)
      this.olmap.render()
    },
    // 向上移动地图
    moveToUp () {
      var view = this.olmap.getView()
      var mapCenter = view.getCenter()
      // 让地图中心的y值减少，即可使得地图向上移动，减少的值根据效果可自由设定
      mapCenter[1] -= _panFactor
      view.setCenter(mapCenter)
      this.olmap.render()
    },
    // 向下移动地图
    moveToDown () {
      var view = this.olmap.getView()
      var mapCenter = view.getCenter()
      // 让地图中心的y值增加，即可使得地图向下移动，增加的值根据效果可自由设定
      mapCenter[1] += _panFactor
      view.setCenter(mapCenter)
      this.olmap.render()
    },
    getCurrCenter () {
      return this.olmap.getView().getCenter()
    },
    getLevel () {
      return this.olmap.getView().getZoom()
    },
    setExtent (extent) {
      this.olmap.getView().fit([extent.minX, extent.minY, extent.maxX, extent.maxY], this.olmap.getSize())
    },
    getCurrentExtent () {
      var extent = this.olmap.getView().calculateExtent(this.olmap.getSize())
      if (!extent) {
        return null
      }
      return {
        minX: extent[0],
        minY: extent[1],
        maxX: extent[2],
        maxY: extent[3]
      }
    },
    getEventCoordinate (event) {
      return this.olmap.getEventCoordinate(event)
    },
    isInCurrentExtent (x, y) {
      const { ol } = this
      var extent = this.getCurrentExtent()
      return ol.extent.containsXY(extent, x, y)
    },
    toMap (x, y) {
      var pixel = [x, y]
      var coordinate = this.olmap.getCoordinateFromPixel(pixel)
      if (!coordinate) {
        return null
      }
      return {
        x: coordinate[0],
        y: coordinate[1]
      }
    },
    toScreen (x, y) {
      var coordinate = [x, y]
      var pixel = this.olmap.getPixelFromCoordinate(coordinate)
      if (!pixel) {
        return null
      }
      return {
        x: pixel[0],
        y: pixel[1]
      }
    },
    getView () {
      return this.olmap.getView()
    },
    getAllLayers () {
      return this.olmap.getLayers().getArray()
    },
    getAllLayersID () {
      var layerIdArr = []
      var layers = this.getAllLayers()
      layers.forEach(function (_layer) {
        var id = _layer.get('id')
        layerIdArr.push(id)
      })
      return layerIdArr
    },
    getLayer (layerId) {
      var layers = this.getAllLayers()
      var layer = null
      layers.forEach(function (_layer) {
        var id = _layer.get('id')
        if (id === layerId) {
          layer = _layer
        }
      })
      return layer
    },
    isLayerExist (layerid) {
      return this.getLayer(layerid) == null
    },
    zoomIn () {
      this.setLevel(this.getLevel() + 1)
    },
    zoomOut () {
      this.setLevel(this.getLevel() - 1 >= 0 ? (this.getLevel() - 1) : 0)
    },
    addLayer (config) {
      const { ol } = this
      var {
        id,
        name,
        isCluster = false,
        clusterDistance = 20
      } = config
      if (this.getLayer(id)) {
        return this.getLayer(id)
      }
      var resource = new ol.source.Vector()
      var source = null
      if (isCluster) {
        source = new ol.source.Cluster({
          source: resource,
          distance: clusterDistance
        })
      } else {
        source = resource
      }
      var layer = new ol.layer.Vector({
        source
      })
      layer.set('id', id)
      layer.set('name', name)
      this.olmap.addLayer(layer)
      return layer
    },
    removeLayer (id) {
      this.olmap.removeLayer(this.getLayer(id))
    },
    addControl (control) {
      this.olmap.addControl(control)
    },
    addInteraction (interaction, isActive = false) {
      this.olmap.addInteraction(interaction)
      interaction.setActive(isActive)
    },
    removeControl (control) {
      this.olmap.removeControl(control)
    },
    removeInteraction (interaction) {
      this.olmap.removeInteraction(interaction)
    },
    getInteractions () {
      return this.olmap.getInteractions()
    },
    getControls () {
      return this.olmap.getControls()
    },
    getOverlays () {
      return this.olmap.getOverlays()
    },
    addOverlay (overlay) {
      this.olmap.addOverlay(overlay)
    },
    removeOverlay (overlayer) {
      this.olmap.removeOverlay(overlayer)
    },
    getOverlayById (id) {
      return this.olmap.getOverlayById(id)
    },
    getViewport () {
      return this.olmap.getViewport()
    },
    forEachFeatureAtPixel (pixel, callback) {
      this.olmap.forEachFeatureAtPixel(pixel, function (feature, layer) {
        callback(feature, layer)
      })
    },
    getTargetElement () {
      return this.olmap.getTargetElement()
    },
    getSize () {
      return this.olmap.getSize()
    },
    hasFeatureAtPixel (pix) {
      return this.olmap.hasFeatureAtPixel(pix)
    },
    clearLayerFeatures (id) {
      this.getLayer(id).getSource().clear()
    },
    render() {
      this.olmap.render()
    }
  },
  mounted () {
    this.init()
  }
}
</script>
