    import * as T from '@turf/turf'
    import Layer from './baseLayer/Layer'
    import { Zondy, ElevationInfo, FeatureSet } from '../../base'
    import { GeometryType, LayerType, LoadStatus } from '../../base/enum'
    import { defaultValue, Log } from '../../util'
    import { Extent, SpatialReference } from '../../base/geometry'
    import FetchRequest from '../../service/FetchRequest'
    import { BaseRenderer } from '../renderer'

    /**
     * geojson图层，仅支持文件服务<br/>
     * 目前二维和三维上支持4326(包括4490，4214以及4610)，3857以及EPSG支持的自定义坐标系，若是想要绘制源数据为非4326坐标系几何数据，需要在初始化时指定具体坐标系<br/>
     * 参考示例：<br/>
     * <a href='#add-GeoJSONLayer'>[加载GeoJSON图层]</a>
     * <br><br>[ES5引入方式]：<br/>
     * Zondy.Layer.GraphicsLayer() <br/>
     * [ES6引入方式]：<br/>
     * import { GraphicsLayer } from "@mapgis/webclient-common"
     * @class GeoJSONLayer
     * @moduleEX LayerManagerModule
     * @extends Layer
     * @fires Layer#图层加载完毕事件
     * @fires Layer#图层销毁完毕事件
     * @fires Layer#图层更新完毕事件
     * @fires Layer#图层显隐更新完毕事件
     * @fires Layer#图层透明度更新完毕事件
     * @fires Layer#图层刷新完毕事件
     * @fires GeoJSONLayer#图层样式更新完毕事件
     * @param {Object} options 构造参数
     * @param {String} [options.url] 服务基地址，仅支持文件服务：<br/>
     * 参考示例：<br/>
     * <a href='#add-GeoJSONLayer'>[1、加载GeoJSON图层]</a><br/>
     * <a href='#add-GeoJSONLayer-custom'>[2、加载自定义坐标系的GeoJSON图层]</a><br/>
     * <a href='#removeLayer'>[3、删除GeoJSON图层]</a>
     * @param {String} [options.id] 图层id，若不填则给一个随机id
     * @param {Boolean} [options.visible = show] 图层可见性，参考示例：<a href='#visible'>[图层可见性]</a>
     * @param {Number} [options.opacity = 1] 图层透明度，0~1之间的值，0完全透明，1不透明，参考示例：<a href='#opacity'>[图层透明度]</a>
     * @param {SpatialReference} [options.spatialReference] 图层坐标系，默认为4326，若是想要绘制源数据为非4326坐标系几何数据，需要在初始化时指定具体坐标系
     * @param {BaseRenderer} [options.renderer] 渲染样式，<br>
     * 目前支持如下样式：<br/>
     * [1、单值专题图]{@link UniqueValueRenderer}<br/>
     * [2、分段专题图]{@link ClassBreakRenderer}<br/>
     * [3、统一专题图]{@link SimpleRenderer}<br/>
     * 参考示例：<br/>
     * <a href='#UniqueValueRenderer'>[1、单值专题图]</a><br/>
     * <a href='#ClassBreakRenderer'>[2、分段专题图]</a><br/>
     * <a href='#SimpleRenderer'>[3、统一专题图]</a><br/>
     * @param {Boolean} [options.labelsVisible = false] 是否开启动态注记,仅支持三维动态注记渲染
     * @param {Array<LabelClass>} [options.labelingInfo = []] 注记样式数组，可以和renderer同时启用，默认取数组的第一个样式，
     * 仅支持三维场景，参考示例：<a href='#add-labelingInfo'>[注记样式]</a><br/>
     * @param {Number} [options.minScale = 0] 最小缩放级数，仅会请求级数大于等于minScale的图片
     * @param {Number} [options.maxScale = 19] 最大缩放级数，仅会请求级数小于等于maxScale的图片
     * @param {String} [options.tokenKey = 'token'] token名
     * @param {String} [options.tokenValue] token值，只有当tokenValue存在时，才会绑定token
     * @param {Object} [options.customParameters = {}] 自定义查询参数，键值对，填写该参数后，会在数据请求的来链接后面以&key=value的形式拼接字符串
     *
     * @summary <h5>支持如下方法：</h5>
     * <a href='#load'>[1、加载图层资源]</a><br/>
     * <a href='#destroy'>[2、销毁图层]</a><br/>
     * <a href='#queryFeatures'>[3、返回所有要素]</a><br/>
     * <a href='#queryFeaturesCount'>[4、查询要素数量]</a><br/>
     * <a href='#fromJSON'>[5、通过传入的json构造并返回一个新的几何对象]</a><br/>
     * [6、导出为json对象]{@link OGCLayer#toJSON}<br/>
     * [7、克隆几何对象]{@link OGCLayer#clone}
     *
     * @example <caption><h7 id='add-GeoJSONLayer'>初始化GeoJSON图层</h7></caption>
     * // 初始化图层管理容器
     * // ES5引入方式
     * const { Map,MapView } = Zondy
     * const { GeoJSONLayer } = Zondy.Layer
     * // ES6引入方式
     * import { Map,MapView,GeoJSONLayer } from "@mapgis/webclient-common"
     * const map = new Zondy.Map();
     * // 初始化地图视图对象
     * const mapView = new MapView({
     *   // 视图id
     *   viewId: "viewer-id",
     *   // 图层管理容器
     *   map: map
     * });
     * // 创建图层
     * const geojsonLayer = new GeoJSONLayer({
     *   // 服务基地址
     *   url: 'json数据地址'
     * })
     * // 添加图层
     * map.add(geojsonLayer)
     *
     * @example <caption><h7 id='add-GeoJSONLayer-custom'>加载自定义坐标系的GeoJSON图层</h7></caption>
     * // ES5引入方式
     * const { Map,MapView,SpatialReference} = Zondy
     * const { GeoJSONLayer } = Zondy.Layer
     * // ES6引入方式
     * import { Map,MapView,SpatialReference,GeoJSONLayer } from "@mapgis/webclient-common"
     * // 初始化图层管理容器
     * const map = new Map();
     * // 初始化地图视图对象
     * const mapView = new MapView({
     *   // 视图id
     *   viewId: "viewer-id",
     *   // 图层管理容器
     *   map: map
     * });
     * // 创建图层
     * const geojsonLayer = new GeoJSONLayer({
     *   // 服务基地址
     *   url: 'json数据地址',
     *   // 设置坐标系
     *   spatialReference: new SpatialReference({
     *     wkid: '坐标系的wkid'
     *   })
     * })
     * // 添加图层
     * map.add(geojsonLayer)
     *
     * @example <caption><h7 id='removeLayer'>删除GeoJSON图层</h7></caption>
     * // 删除图层
     * map.remove(geojsonLayer)
     *
     * @example <caption><h7 id='UniqueValueRenderer'>单值专题图-区数据</h7></caption>
     * // ES5引入方式
     * const { Map,MapView,Color} = Zondy
     * const { GeoJSONLayer } = Zondy.Layer
     * const { UniqueValueRenderer } = Zondy.Renderer
     * const { SimpleLineSymbol,SimpleFillSymbol } = Zondy.Symbol
     * // ES6引入方式
     * import { Map,MapView,Color,GeoJSONLayer,UniqueValueRenderer,SimpleLineSymbol,SimpleFillSymbol } from "@mapgis/webclient-common"
     * // 初始化图层管理容器
     * const map = new Map();
     * // 初始化地图视图对象
     * const mapView = new MapView({
     *   // 视图id
     *   viewId: "viewer-id",
     *   // 图层管理容器
     *   map: map
     * });
     * // 创建图层
     * const geojsonLayer = new GeoJSONLayer({
     *   // 服务基地址
     *   url: 'json数据地址',
     *   // 渲染样式 - 单值专题图 - 区数据
     *   renderer: new UniqueValueRenderer({
     *     //字段名
     *     field: '你的字段名',
     *     // 默认符号，不在专题图指定范围内的会采用该样式，可不设置
     *     defaultSymbol: new Zondy.Symbol.SimpleFillSymbol({
     *       // 填充颜色
     *       color: Zondy.Color(1 , 1, 252),
     *       // 外边线样式
     *       outline: new SimpleLineSymbol({
     *         //线颜色
     *         color: new Color(255, 1, 0, 1),
     *         //线宽
     *         width: 1
     *       })
     *     }),
     *     //单值专题图字段样式
     *     uniqueValueInfos: [{
     *       //指定字段值
     *       value: "指定的值",
     *       //匹配到该值后的样式
     *       symbol: new SimpleFillSymbol({
     *         // 填充颜色
     *         color: Color(1, 1, 252),
     *         // 外边线样式
     *         outline: new SimpleLineSymbol({
     *           //线符号颜色
     *           color: new Color(255, 1, 0),
     *           //线宽
     *           width: 1
     *           })
     *         })
     *       },{
     *         //指定字段值
     *         value: "指定的值",
     *         //匹配到该值后的样式
     *         symbol: new SimpleFillSymbol({
     *           // 填充颜色
     *           color: new Color(211, 111, 11, 1)
     *         })
     *       }]
     *   })
     * })
     * map.add(geojsonLayer)
     *
     * @example <caption><h7 id='ClassBreakRenderer'>分段专题图-区数据</h7></caption>
     * // ES5引入方式
     * const { Map,MapView,Color} = Zondy
     * const { GeoJSONLayer } = Zondy.Layer
     * const { ClassBreakRenderer } = Zondy.Renderer
     * const { SimpleLineSymbol,SimpleFillSymbol } = Zondy.Symbol
     * // ES6引入方式
     * import { Map,MapView,Color,GeoJSONLayer,ClassBreakRenderer, SimpleLineSymbol,SimpleFillSymbol} from "@mapgis/webclient-common"
     * // 初始化图层管理容器
     * const map = new Map();
     * // 初始化地图视图对象
     * const mapView = new MapView({
     *   // 视图id
     *   viewId: "viewer-id",
     *   // 图层管理容器
     *   map: map
     * });
     * // 创建图层
     * const geojsonLayer = new GeoJSONLayer({
     *   // 服务基地址
     *   url: 'json数据地址',
     *   // 渲染样式 - 分段专题图 - 区数据
     *   renderer: new ClassBreakRenderer({
     *      //字段名
     *      field: '你的字段名',
     *      // 指定默认样式，不在专题图指定范围内的会采用该样式，可不设置
     *      defaultSymbol:  new SimpleFillSymbol({
     *        // 填充颜色
     *        color: new Color(222, 1, 252),
     *        // 线符号样式
     *        outline: new SimpleLineSymbol({
     *          //线符号颜色
     *          color: new Zondy.Color(255, 1, 0),
     *          //线宽
     *          width: 1
     *        })
     *      }),
     *      //分段专题图字段样式
     *      classBreakInfos: [{
     *        // 最大范围
     *        maxValue: "最大范围",
     *        // 最小范围
     *        minValue: "最小范围",
     *        //匹配到该值后的样式
     *        symbol:  new SimpleFillSymbol({
     *          // 填充颜色
     *          color: new Color(1, 1, 252),
     *          // 线符号样式
     *          outline: new SimpleLineSymbol({
     *            //线符号颜色
     *            color: new Color(255, 1, 0, 1),
     *            //线宽
     *            width: 1
     *          })
     *        })
     *      }]
     *    })
     * })
     * map.add(geojsonLayer)
     *
     * @example <caption><h7 id='SimpleRenderer'>统一专题图-区数据</h7></caption>
     * // ES5引入方式
     * const { GeoJSONLayer } = Zondy.Layer
     * const { SimpleRenderer } = Zondy.Renderer
     * const { SimpleFillSymbol,SimpleLineSymbol } = Zondy.Symbol
     * const { Color } = Zondy
     * // ES6引入方式
     * import { Color,GeoJSONLayer, SimpleRenderer,SimpleLineSymbol,SimpleFillSymbol} from "@mapgis/webclient-common"
     * const geojsonLayer = new GeoJSONLayer({
     *   // 服务基地址
     *   url: 'json数据地址',
     *   // 渲染样式 - 统一专题图 - 区数据
     *   renderer: new SimpleRenderer({
     *     // 设置几何的样式
     *     symbol: new SimpleFillSymbol({
     *       //线符号颜色
     *       color: randomColor(),
     *       // 外边线样式
     *       outline: new SimpleLineSymbol({
     *         // 外边线颜色
     *         color: new Color(1, 1, 252),
     *         // 外边线宽度
     *         width: 1
     *       })
     *     })
     *   })
     * })
     * map.add(geojsonLayer)
     *
     * @example <caption><h7 id='index'>设置图层顺序</h7></caption>
     * // 加载完毕后，更改图层顺序
     * map.reorder(geojsonLayer, '要移动到的index');
     *
     * @example <caption><h7 id='visible'>图层可见性</h7></caption>
     * // 创建图层
     * // ES5引入方式
     * const { GeoJSONLayer } = Zondy.Layer
     * // ES6引入方式
     * import { GeoJSONLayer } from "@mapgis/webclient-common"
     * const geojsonLayer = new GeoJSONLayer({
     *   // 服务基地址
     *   url: 'json数据地址',
     *   // 设置图层可见性
     *   visible: true
     * })
     *
     * // 图层加载完成后，设置可见性
     * geojsonLayer.visible = !geojsonLayer.visible
     *
     * @example <caption><h7 id='opacity'>图层透明度</h7></caption>
     * // 创建图层
     *  // ES5引入方式
     * const { GeoJSONLayer } = Zondy.Layer
     * // ES6引入方式
     * import { GeoJSONLayer } from "@mapgis/webclient-common"
     * const geojsonLayer = new Zondy.Layer.GeoJSONLayer({
     *   // 服务基地址
     *   url: 'json数据地址',
     *   // 设置图层透明度
     *   opacity: 1
     * })
     *
     * // 图层加载完成后，设置可见性
     * geojsonLayer.opacity = 0.5
     *
     * @example <caption><h7 id='add-labelingInfo'>启用注记</h7></caption>
     *  // ES5引入方式
     * const { LabelClass,Font } = Zondy
     * const { TextSymbol } = Zondy.Symbol
     * const { GeoJSONLayer } = Zondy.Layer
     * // ES6引入方式
     * import { LabelClass,Font,TextSymbol,GeoJSONLayer } from "@mapgis/webclient-common"
     * // 初始化LabelClass
     * const labelClass = new LabelClass({
     *   // 指定文本符号样式
     *   symbol: new TextSymbol({
     *     // 文字颜色
     *     color: new Color(252, 100, 22, 1),
     *     // 文字样式
     *     font: new Font({
     *       // 字体
     *       family: "微软雅黑",
     *       // 文字大小，单位像素
     *       size: 30,
     *       // 文字是否为斜体，正常模式
     *       style: "normal",
     *       // 文字粗细
     *       weight: "normal"
     *     })
     *   })
     * })
     * // 初始化GeoJSON图层
     * const geojsonLayer = new GeoJSONLayer({
     *   // 服务基地址，当不指定图层名称时，默认查询第一个子图层
     *   url: 'http://{ip}:{port}/igs/rest/services/{ServiceName}/FeatureServer',
     *   // 可在此处设置渲染样式
     *   renderer: {},
     *   // 启用注记
     *   labelsVisible: true,
     *   // 设置注记样式
     *   labelingInfo: [labelClass]
     * })
     * // 添加到容器中
     * map.add(geojsonLayer)
     */

    /**
     * 图层样式更新完毕事件，请注意该事件是图层更新事件(layerview-update)的子事件
     * @event GeoJSONLayer#图层样式更新完毕事件
     * @property {Object} event 事件对象
     * @property {String} [event.type = 'layerview-update'] 图层更新完毕事件
     * @property {String} [event.message = null] 更新描述
     * @property {Array<UpdateContent>} [event.updateContent = null] 更新详情对象
     * @property {Layer} [event.layer = null] 地图图层对象
     * @property {MapView} [event.layerView = null] 图层的视图对象
     * @property {Layer} [event.sourceTarget = null] 事件发起对象
     * @property {Map} [event.target = null] 事件接收对象
     * @example <caption><h5>图层样式更新完毕事件</h5></caption>
     * Layer.on('layerview-update', function (event) {
     *   // 获取更新事件对象
     *   console.log("更新完毕:", event)
     *   // 获取更新详情数组
     *   const updateContent = event.updateContent
     *   // 循环数组，根据事件名进行后续操作
     *   for (let i = 0; i < updateContent.length; i++) {
     *     // 图层样式更新完毕事件
     *     if(updateContent[i].name === 'renderer'){
     *       console.log("图层样式更新完毕事件:", event);
     *     }
     *   }
     * });
     */
    class GeoJSONLayer extends Layer {
      constructor(options) {
        super(options)
        options = defaultValue(options, {})
        this.type = LayerType.geojson
        this._spatialReference = defaultValue(
          options.spatialReference,
          new SpatialReference('EPSG:4326')
        )
        /**
         * 最小层级
         * @member {Number} GeoJSONLayer.prototype.minScale
         */
        this.minScale = defaultValue(options.minScale, 0)
        /**
         * 最大层级
         * @member {Number} GeoJSONLayer.prototype.maxScale
         */
        this.maxScale = defaultValue(options.maxScale, 19)
        /**
         * token名
         * @member {String} GeoJSONLayer.prototype.tokenKey
         */
        this.tokenKey = defaultValue(options.tokenKey, 'token')
        /**
         * token值
         * @member {String} GeoJSONLayer.prototype.tokenValue
         */
        this.tokenValue = defaultValue(options.tokenValue, undefined)
        /**
         * 渲染器
         * @member {String} GeoJSONLayer.prototype.renderer
         */
        this.renderer = this._decorate(
          'renderer',
          options.renderer,
          BaseRenderer,
          BaseRenderer.fromJSON
        )
        /**
         * 请求地址
         * @member {String} GeoJSONLayer.prototype.url
         */
        this.url = defaultValue(options.url, '')
        /**
         * 高程信息
         * @member {ElevationInfo} GeoJSONLayer.prototype.elevationInfo
         */
        this.elevationInfo = defaultValue(
          options.elevationInfo,
          new ElevationInfo()
        )
        /**
         * 自定义查询参数customParameters
         * @member {String} GeoJSONLayer.prototype.customParameters
         */
        this.customParameters = defaultValue(options.customParameters, {})
        /**
         * 几何类型
         * @member {String} GeoJSONLayer.prototype.geometryType
         */
        this.geometryType = GeometryType.geometry
        /**
         * 是否开启动态注记,仅支持三维动态注记渲染
         * @member {Boolean} GeoJSONLayer.prototype.labelsVisible
         */
        this.labelsVisible = defaultValue(options.labelsVisible, false)
        /**
         * 注记样式数组，默认取数组的第一个样式，仅支持三维动态注记渲染
         * @member {Array<LabelClass>} GeoJSONLayer.prototype.labelingInfo
         */
        this.labelingInfo = defaultValue(options.labelingInfo, [])
        // 缓存几何数据
        this._source = null
      }

      /**
       * 加载图层资源<a id='load'></a>
       * @return {Promise<GeoJSONLayer>}
       */
      load() {
        const self = this
        return this._parseGeoJSON().then((json) => {
          const data = json
          if (data) {
            // 缓存数据
            self._source = data
            // 计算范围
            const layerRange = T.bbox(data)
            const xMin = parseFloat(layerRange[0])
            const yMin = parseFloat(layerRange[1])
            const xMax = parseFloat(layerRange[2])
            const yMax = parseFloat(layerRange[3])
            self.extent = new Extent({
              xmin: xMin,
              xmax: xMax,
              ymin: yMin,
              ymax: yMax,
              spatialReference: self._spatialReference
            })
            // 获取数据类型
            this.geometryType = GeometryType.geometry
            return new Promise((resolve) => {
              self.loadStatus = LoadStatus.loaded
              self.loaded = true
              resolve(self)
            })
          } else {
            Log.error('几何信息为空', data)
          }
        })
      }

      /**
       * 销毁图层<a id='destroy'></a>
       */
      destroy() {}

      /**
       * 返回所有要素<a id='queryFeatures'></a>
       * @return {Promise<FeatureSet>}
       */
      queryFeatures() {
        const self = this
        return this._parseGeoJSON().then((res) => {
          const featureSet = new FeatureSet({
            spatialReference: self._spatialReference
          })
          featureSet.fromGeoJSON(res)
          return featureSet
        })
      }

      /**
       * 查询要素数量<a id='queryFeaturesCount'></a>
       * @return {Promise<Number>}
       */
      queryFeaturesCount() {
        const self = this
        return this._parseGeoJSON().then((res) => {
          const featureSet = new FeatureSet({
            spatialReference: self._spatialReference
          })
          featureSet.fromGeoJSON(res)
          return featureSet.features.length
        })
      }

      /**
       * @private
       * @return {Promise<Object>}
       */
      _parseGeoJSON() {
        const self = this
        return new Promise((reslove) => {
          if (typeof self.url === 'string') {
            let url = self.url
            if (self.customParameters) {
              const keys = Object.keys(self.customParameters)
              let queryStr = '?'
              keys.forEach((key) => {
                queryStr += `${key}=${self.customParameters[key]}&`
              })
              if (queryStr !== '?') {
                queryStr = queryStr.substring(queryStr.length - 1, 0)
                url += queryStr
              }
            }
            FetchRequest.commit('get', url, {}).then((res) => {
              reslove(res.json())
            })
          } else {
            reslove(self.url)
          }
        }).catch((err) => {
          Log.error(err)
        })
      }

      /**
       * 通过传入的json构造并返回一个新的几何对象<a id='fromJSON'></a>
       * @param {Object} [json] JSON对象
       * @example <caption><h7>通过传入的json构造并返回一个新的几何对象</h7></caption>
       * const json = {
       *   // 服务基地址
       *   url: 'json数据地址'
       * }
       * const geojsonLayer = new Zondy.Layer.GeoJSONLayer.fromJSON(json)
       */
      static fromJSON(json) {
        json = defaultValue(json, {})
        return new GeoJSONLayer(json)
      }
    }

    Zondy.Layer.GeoJSONLayer = GeoJSONLayer
    export default GeoJSONLayer