    // eslint-disable-next-line import/no-extraneous-dependencies
    import proj4 from 'proj4'
    import {defaultValue, Projection, EPSG, Config} from '@mapgis/webclient-common'
    import { Log, defined } from '@mapgis/webclient-common/util'
    import {ServiceConfigRequestInterceptors, ServiceConfigToken} from '@mapgis/webclient-common/service/Utils';

    /**
     * 从SpatialReference中获取TilingScheme对象给cesium使用
     * @param {SpatialReference} spatialReference 坐标系对象
     * @return {Object} TilingScheme对象
     * */
    function getTilingSchemeBySpatialReference(
      spatialReference,
      extent = undefined,
      tileInfo = undefined
    ) {
      // 若spatialReference中的wkid未定义，则默认返回Web墨卡托瓦片平铺规则
      if (!defined(spatialReference.wkid)) {
        return new Cesium.WebMercatorTilingScheme()
      }

      // 若wkid定义，则根据wkid从EPSG中获取wkt
      const targetSpatialReference = EPSG.filter(function (item) {
        return item.id === spatialReference.wkid
      })

      // Array.prototype.filter方法会将符合条件的值通过数组返回
      // 若数组的长度为0，则说明没有匹配到wkt值，此时默认返回Web墨卡托瓦片平铺规则
      if (targetSpatialReference.length === 0) {
        Log.error('根据该空间参考系的wkid未匹配到wkt值')
        return new Cesium.WebMercatorTilingScheme()
      }

      const wkt = targetSpatialReference[0].strProject
      const wkid = spatialReference.wkid
      let tilingScheme = undefined

      switch (spatialReference.wkid) {
        case 4326:
        case 4490:
        case 4610:
        case 4214:
          // 若tileInfo定义但lods未定义，默认返回经纬度瓦片平铺规则，否则返回自定义瓦片平铺规则
          if (defined(tileInfo) && !defined(tileInfo.lods)) {
            tilingScheme = new Cesium.GeographicTilingScheme()
          } else {
            // 若extent未定义，则设置默认的extent值，默认为EPSG:4326的投影范围
            if (!defined(extent)) {
              extent = {
                xmin: -180,
                ymin: -90,
                xmax: 180,
                ymax: 90
              }
            }
            tilingScheme = new Cesium.CustomTilingScheme({
              wkt,
              wkid,
              proj4,
              tileInfo,
              numberOfLevelZeroTilesX: defined(tileInfo) ? 2 : 1,
              rectangleSouthwest: new Cesium.Cartesian2(extent.xmin, extent.ymin),
              rectangleNortheast: new Cesium.Cartesian2(extent.xmax, extent.ymax)
            })
          }
          break
        default:
          // 若tileInfo定义但lods未定义，默认返回Web墨卡托瓦片平铺规则，否则返回自定义瓦片平铺规则
          if (defined(tileInfo) && !defined(tileInfo.lods)) {
            tilingScheme = new Cesium.WebMercatorTilingScheme()
          } else {
            // 若extent未定义，则设置默认的extent值，默认为EPSG:3857的投影范围
            if (!defined(extent)) {
              extent = {
                xmin: -20037508.34,
                ymin: -20037508.34,
                xmax: 20037508.34,
                ymax: 20037508.34
              }
            }
            tilingScheme = new Cesium.CustomTilingScheme({
              wkt,
              wkid,
              proj4,
              tileInfo,
              rectangleSouthwest: new Cesium.Cartesian2(extent.xmin, extent.ymin),
              rectangleNortheast: new Cesium.Cartesian2(extent.xmax, extent.ymax)
            })
          }
      }

      return tilingScheme
    }

    /**
     * 获取图层在cesium中要添加的顺序
     * @param {Object} layer 基础图层对象
     * @param {Object} innerView cesium的view对象
     * */
    function getCesiumLayerIndex(layer, innerView) {
      // 首先获取当前imageryLayers中图层数量
      let index = innerView.imageryLayers._layers.length
      // 找到第一个比当前图层顺序大且存在_realIndex属性的图层，并返回其序号，作为当前图层要添加的位置
      for (let i = 0; i < innerView.imageryLayers._layers.length; i++) {
        // layer._realIndex + 1是因为cesium第0个是底图，因此顺序从1开始
        if (
          innerView.imageryLayers._layers[i].hasOwnProperty('_realIndex') &&
          layer._realIndex + 1 < innerView.imageryLayers._layers[i]._realIndex
        ) {
          index = i
          break
        }
      }
      // 如果没有找到，则添加到最后
      return index
    }

    /**
     * 从layer中获取extent对象，如果不是4326坐标系，则进行投影
     * @param {Object} layer 基本图层对象
     * @return {Extent} 地图范围
     * */
    function getExtent(layer) {
      let extent = layer.extent
      if (layer.spatialReference.wkid === 3857) {
        const min = Projection.projectMercatorToLonLat({
          x: extent.xmin,
          y: extent.ymin
        })
        const max = Projection.projectMercatorToLonLat({
          x: extent.xmax,
          y: extent.ymax
        })
        extent = JSON.parse(JSON.stringify(extent))
        extent.xmin = min.longitude < -180 ? -180 : min.longitude
        extent.ymin = min.latitude < -90 ? -90 : min.latitude
        extent.xmax = max.longitude > 180 ? 180 : max.longitude
        extent.ymax = max.latitude > 90 ? 90 : max.latitude
      }
      return extent
    }

    /**
     * 获取m3d构造参数
     * @param {Object} options 构造参数
     * @return {Object} m3d构造参数
     * */
    function getM3DOptions(options) {
      const m3dOptions = {}
      m3dOptions.url = defaultValue(options.url, undefined)
      m3dOptions.id = defaultValue(options.id, undefined)
      m3dOptions.show = defaultValue(options.show, true)
      m3dOptions.autoReset = defaultValue(options.autoReset, true)
      m3dOptions.layerIndex = defaultValue(options.layerIndex, 0)
      m3dOptions.layerRenderIndex = defaultValue(options.layerRenderIndex, 0)
      return m3dOptions
    }

    /**
     * 获取地形参数
     * @param {Object} options 地形参数
     * @return {Object} 地形参数
     * */
    function getTerrainOptions(options) {
      const terrainOptions = {}
      const range = options.range
      const terrainRange = {
        xMin: range.xmin,
        yMin: range.ymin,
        xMax: range.xmax,
        yMax: range.ymax
      }
      const range3D = options.range3D
      const terrainRange3D = {
        xMin: range3D.xmin,
        yMin: range3D.ymin,
        xMax: range3D.xmax,
        yMax: range3D.ymax,
        zMin: range3D.zmin,
        zMax: range3D.zmax
      }
      terrainOptions.scale = defaultValue(options.elevationScale, undefined)
      terrainOptions.range = terrainRange
      terrainOptions.range3D = terrainRange3D
      terrainOptions.requestVertexNormals = defaultValue(
        options.requestVertexNormals,
        false
      )
      terrainOptions.sceneIndex = 0
      terrainOptions.layerIndex = defaultValue(options.layerIndex, 0)
      terrainOptions.layerRenderIndex = defaultValue(options.layerRenderIndex, 0)
      terrainOptions.show = defaultValue(options.show, true)
      terrainOptions.autoReset = defaultValue(options.autoReset, true)
      return terrainOptions
    }

    /**
     * 添加地形图层
     * @param {View} innerView Cesium的view对象
     * @param {Object} options 构造参数
     * */
    function addTerrainLayer(innerView, options) {
      options = defaultValue(options, {})
      // const autoReset = defaultValue(options.autoReset, true)
      const sceneIndex = options.sceneIndex
      const layerIndex = options.layerIndex
      const layerRenderIndex = options.layerRenderIndex
      const requestVertexNormals = defaultValue(options.requestVertexNormals, false)
      let terrainUrl
      if (options.url.indexOf('/SceneServer') > -1) {
        terrainUrl = `${options.url}/layers/${layerIndex}/terrains?level={z}&row={x}&col={y}&xDensity=65&yDensity=65&hasNormals=${requestVertexNormals}`
      } else {
        terrainUrl = `${options.url}/GetTerrain?sceneIndex=${sceneIndex}&layerIndex=${layerRenderIndex}&Level={z}&Row={x}&Col={y}&xdensity=65&ydensity=65&webGL=true&hasNormals=${requestVertexNormals}`
      }
      const terrainLayer = new Cesium.MapGISTerrainProvider({
        url: terrainUrl,
        range: options.range,
        scale: options.scale,
        requestVertexNormals,
        terrainColorTblInfo: options.terrainColorTblInfo,
        range3D: options.range3D,
        waterHeight: options.waterHeight
      })
      innerView.scene.terrainProvider = terrainLayer
      terrainLayer.type = 'terrain'
      return terrainLayer
    }

    /**
     * 添加M3D图层
     * @param {Object} mapGISM3DSetOptions M3D构造参数
     * @param {View} innerView Cesium的view对象
     * @param {SceneView} sceneView plugin的SceneView对象
     * @param {Function} callback 图层加载成功的回调函数
     * @param {Function} errorCallback 图层加载失败的回调函数
     * */
    function addM3DLayer(
      mapGISM3DSetOptions,
      innerView,
      sceneView,
      callback,
      errorCallback
    ) {
      // 获取M3D构造参数
      mapGISM3DSetOptions = defaultValue(mapGISM3DSetOptions, {})
      // 如果有headers，则设置headers
      if (mapGISM3DSetOptions.headers) {
        mapGISM3DSetOptions.url = new Cesium.Resource({
          url: mapGISM3DSetOptions.url,
          headers: mapGISM3DSetOptions.headers
        })
      }

      // 保存cesium的scene对象
      mapGISM3DSetOptions._scene = innerView.scene
      // 添加图层
      const m3dLayer = innerView.scene.primitives.add(
        new Cesium.MapGISM3DSet(mapGISM3DSetOptions)
      )
      // 添加图层到图层管理容器中
      sceneView._layersMap.put(mapGISM3DSetOptions.id, m3dLayer)
      // 开始请求图层数据
      m3dLayer.readyPromise
        .then(function (tileSet) {
          tileSet.id = mapGISM3DSetOptions.id
          tileSet.type = 'm3d'
          // 执行加载完成回调，发送图层添加事件
          if (callback && typeof callback === 'function') {
            callback(tileSet)
          }
        })
        .otherwise(function (result) {
          // 加载失败回调
          if (errorCallback && typeof errorCallback === 'function') {
            errorCallback(result)
          }
        })
    }

    /**
     * 根据M3D的外包盒进行跳转
     * @param {Object} boundingSphere M3D的外包球
     * @param {Object} options 跳转参数
     * @param {View} innerView Cesium的View对象
     * */
    function zoomToM3DLayer(boundingSphere, options, innerView) {
      options = defaultValue(options, {})
      const duration = defaultValue(options.duration, 0)
      const orientation = defaultValue(
        options.orientation,
        new Cesium.HeadingPitchRange(0.0, -0.5, boundingSphere.radius * 2.5)
      )
      innerView.scene.camera.flyToBoundingSphere(boundingSphere, {
        duration,
        offset: orientation
      })
      innerView.scene.camera.lookAtTransform(Cesium.Matrix4.IDENTITY)
    }

    /**
     * 添加注记图层
     * @param {String} url     服务地址
     * @param {Object} options 可选参数
     * @param {Object} layerInfo 从IGS查询到的图层的信息，如最大最小比例尺，避让等
     * @param {View} innerView Cesium的view对象
     */
    function appendLabelLayer(url, options, layerInfo, innerView) {
      options = defaultValue(options, {})

      options.url = url

      let dataType = undefined
      if (url.indexOf('igs/rest/mrfs') !== -1) {
        dataType = 'MapGIS2D'
      }
      if (url.indexOf('igs/rest/g3d') !== -1) {
        dataType = 'MapGIS3D'
      }

      let layer = {}
      switch (dataType) {
        case 'MapGIS2D':
          layer = new Cesium.MapGISLabelLayer(
            url,
            innerView.scene,
            options,
            layerInfo
          )
          break
        case 'MapGIS3D':
          options.is3d = true
          layer = new Cesium.MapGISLabelLayer(
            url,
            innerView.scene,
            options,
            layerInfo
          )
          break
        default:
          break
      }
      layer.type = 'label'
      return layer
    }

    function formatServiceConfigToUrl(url) {
      const tokenStr = `${ServiceConfigToken(Config)}`
      const _url = ServiceConfigRequestInterceptors(
        Config,
        `${url}?${tokenStr}`
      )
      return _url
    }

    export {
      getTilingSchemeBySpatialReference,
      getCesiumLayerIndex,
      getExtent,
      getM3DOptions,
      getTerrainOptions,
      addTerrainLayer,
      addM3DLayer,
      zoomToM3DLayer,
      appendLabelLayer,
      formatServiceConfigToUrl
    }