    import * as T from '@turf/turf'
    import Geometry from './Geometry'
    import { defaultValue, isNumber } from '../../util'
    import Zondy from '../Zondy'
    import { GeometryType, IGSGeometryType } from '../enum'
    import Polygon from './Polygon'
    import Point from './Point'
    import Extent from './Extent'
    import SpatialReference from './SpatialReference'
    import Log from '../../util/Log'

    /**
     * 多区几何
     * <br><br>[ES5引入方式]：<br/>
     * Zondy.Geometry.MultiPolygon() <br/>
     * [ES6引入方式]：<br/>
     * import { MultiPolygon } from "@mapgis/webclient-common" <br/>
     * <br/>
     * @class MultiPolygon
     * @moduleEX BaseModule
     * @extends Geometry
     * @param {Object} options 构造参数
     * @param {Array} [options.coordinates = []] 几何点的坐标数组，参考示例：<a href='#MultiPolygon'>[多区几何对象]</a>
     * @param {SpatialReference} [options.spatialReference = new Zondy.SpatialReference('EPSG:4326')] 几何点的空间参考系，默认4326，当不是4326时请指定坐标系，方便进行投影转换，参考示例：<a href='#SpatialReference'>[指定坐标系]</a>
     * @summary <h5>支持如下方法：</h5>
     * <a href='#addPolygon'>[1、在多区末尾添加区]</a><br/>
     * <a href='#removePolygon'>[2、根据区几何的下标删除的区对象]</a><br/>
     * <a href='#toXMl'>[3、导出为OGC服务要求的xml字符串]</a><br/>
     * <a href='#contains'>[4、检查输入点是否在多边形内]</a><br/>
     * <a href='#toString'>[5、返回字符串]</a><br/>
     * <a href='#toOldIGSGeometry'>[6、返回igs1.0的几何对象]</a><br/>
     * <a href='#getIGSType'>[7、返回IGS所对应的GeometryModule型]</a><br/>
     * <a href='#fromJSON'>[8、通过传入的json构造并返回一个新的几何对象]</a><br/>
     * <a href='#toJSON'>[9、导出为json对象]</a><br/>
     * [10、克隆几何对象]{@link Geometry#clone}
     *
     * @example <caption><h7 id='MultiPolygon'>创建几何对象</h7></caption>
     * // ES5引入方式
     * const { MultiPolygon } = Zondy.Geometry
     * // ES6引入方式
     * import { MultiPolygon } from "@mapgis/webclient-common"
     * new MultiPolygon({
     *   coordinates:  [
     *     // 第一个区
     *     [
     *       // 外圈
     *       [
     *         // 区要首尾相连
     *         [102.0, 2.0],
     *         [103.0, 2.0],
     *         [103.0, 3.0],
     *         [102.0, 3.0],
     *         [102.0, 2.0]
     *       ],
     *       // 第一个内圈
     *       [],
     *       // 第二个内圈
     *       [],
     *       ...
     *     ],
     *     // 第二个区
     *     [
     *       // 外圈
     *       [
     *         [100.0, 0.0],
     *         [101.0, 0.0],
     *         [101.0, 1.0],
     *         [100.0, 1.0],
     *         [100.0, 0.0]
     *       ],
     *       // 第一个内圈
     *       [
     *         [100.2, 0.2],
     *         [100.2, 0.8],
     *         [100.8, 0.8],
     *         [100.8, 0.2],
     *         [100.2, 0.2]
     *       ]
     *     ]
     *   ]
     * })
     *
     * @example <caption><h7 id='spatialReference'>指定坐标系</h7></caption>
     * // ES5引入方式
     * const { MultiPolygon } = Zondy.Geometry
     * const { SpatialReference } = Zondy.SpatialReference
     * // ES6引入方式
     * import { MultiPolygon, SpatialReference } from "@mapgis/webclient-common"
     * new MultiPolygon({
     *   coordinates:  [
     *     // 第一个区
     *     [
     *       // 外圈
     *       [
     *         // 区要首尾相连
     *         [12060733.232006868, 3377247.5680546067],
     *         [12929863.44711455, 3377247.5680546067],
     *         [12929863.44711455, 3934286.575385226],
     *         [12060733.232006868, 3934286.575385226],
     *         [12060733.232006868, 3377247.5680546067]
     *       ]
     *     ],
     *     // 第二个区
     *     [
     *       // 外圈
     *       [
     *         [12060733.232006868, 3377247.5680546067],
     *         [12929863.44711455, 3377247.5680546067],
     *         [12929863.44711455, 3934286.575385226],
     *         [12060733.232006868, 3934286.575385226],
     *         [12060733.232006868, 3377247.5680546067]
     *       ]
     *     ]
     *   ],
     *   // 当不是4326时请指定坐标系，方便进行投影转换
     *   spatialReference: new SpatialReference('EPSG:3857')
     * })
     */
    class MultiPolygon extends Geometry {
      constructor(options) {
        super(options)
        options = defaultValue(options, {})
        /**
         * 几何点的坐标
         * @member {Array} MultiPolygon.prototype.coordinates
         */
        this.coordinates = defaultValue(options.coordinates, [])
        this.isSelfIntersecting = defaultValue(options.isSelfIntersecting, false)
        this.type = GeometryType.multiPolygon

        if (!Array.isArray(this.coordinates)) {
          throw new Error('坐标必须为数组')
        }

        for (const polygon of this.coordinates) {
          for (const ring of polygon) {
            if (ring.length < 4) {
              throw new Error('每个环至少有四个点')
            }

            if (ring[ring.length - 1].length !== ring[0].length) {
              throw new Error('首尾点维度不相等')
            }

            for (let j = 0; j < ring[ring.length - 1].length; j++) {
              // Check if first point of Polygon contains two numbers
              if (ring[ring.length - 1][j] !== ring[0][j]) {
                throw new Error('第一个位置和最后一个位置不相等')
              }
            }
          }
        }

        if (this.coordinates[0][0][0].length === 3) {
          this.hasZ = true
        }
      }

      /**
       * 在多区末尾添加区
       * @param {Polygon} polygon 要添加区几何对象
       * @param {String} [mode = 'add'] 添加模式，add:正常添加，deduplication:去除重复点，即当新加的区几何和现有的一个或多个几何相交时，会合并几何对象
       * @return {MultiPolygon} 表示给定范围的多边形实例
       * @example <caption><h7 id='addPolygon'>在多区末尾添加区</h7></caption>
       * // ES5引入方式
       * const { MultiPolygon, Polygon } = Zondy.Geometry
       * // ES6引入方式
       * import { MultiPolygon, Polygon } from "@mapgis/webclient-common"
       * const multiPolygon = new MultiPolygon({
       *   coordinates:  [
       *     // 第一个区
       *     [
       *       // 外圈
       *       [
       *         // 区要首尾相连
       *         [102.0, 2.0],
       *         [103.0, 2.0],
       *         [103.0, 3.0],
       *         [102.0, 3.0],
       *         [102.0, 2.0]
       *       ]
       *     ]
       *   ]
       * })
       * const polygon = new Polygon({
       *   coordinates:[
       *     // 外圈
       *     [
       *       [100.0, 0.0],
       *       [101.0, 0.0],
       *       [101.0, 1.0],
       *       [100.0, 1.0],
       *       [100.0, 0.0]
       *     ]
       *   ]
       * })
       * multiPolygon.addPolygon(polygon)
       */
      addPolygon(polygon, mode) {
        mode = defaultValue(mode, 'add')

        if (!(polygon instanceof Polygon)) {
          Log.error('polygon类型不是Polygon对象类型！')
        }

        // 添加模式
        if (mode === 'add') {
          this.coordinates.push(polygon._cloneCoordinates())
        }
        // 去重添加模式
        else {
          // 获取所有与polygon相交的几何
          const intersectPolygons = []
          for (let i = 0; i < this.coordinates.length; i++) {
            const _polygon = T.polygon(this.coordinates[i])
            const _polygon2 = T.polygon(polygon.coordinates)
            if (!T.booleanDisjoint(_polygon, _polygon2)) {
              intersectPolygons.push(_polygon)
              this.coordinates[i] = undefined
            }
          }
          // 删除以相交的几何
          for (let i = 0; i < this.coordinates.length; i++) {
            if (!this.coordinates[i]) {
              this.coordinates.splice(i, 1)
            }
          }
          let unionGeometry = T.polygon(polygon.coordinates)
          // 几何求并
          for (let i = 0; i < intersectPolygons.length; i++) {
            unionGeometry = T.union(unionGeometry, intersectPolygons[i]).geometry
          }
          // 将相交几何放到末尾
          this.coordinates.push(unionGeometry.coordinates)
        }
        return this
      }

      /**
       * 根据区几何的下标删除的区对象
       * @param {Number} polygonIndex
       * @return {Polygon | null} 返回删除的区对象
       * @example <caption><h7 id='removePolygon'>根据区几何的下标删除的区对象</h7></caption>
       * // ES5引入方式
       * const { MultiPolygon } = Zondy.Geometry
       * // ES6引入方式
       * import { MultiPolygon } from "@mapgis/webclient-common"
       * const multiPolygon = new MultiPolygon({
       *   coordinates:  [
       *     // 第一个区
       *     [
       *       // 外圈
       *       [
       *         // 区要首尾相连
       *         [102.0, 2.0],
       *         [103.0, 2.0],
       *         [103.0, 3.0],
       *         [102.0, 3.0],
       *         [102.0, 2.0]
       *       ]
       *     ]
       *   ]
       * })
       * multiPolygon.removePolygon(0)
       */
      removePolygon(polygonIndex) {
        if (!this._isValidate(polygonIndex)) return null
        const coords = JSON.parse(
          JSON.stringify(this._cloneCoordinates()[polygonIndex])
        )
        this.coordinates.splice(polygonIndex, 1)
        return new Polygon({
          coordinates: coords
        })
      }

      /**
       * 导出为OGC服务要求的xml字符串<a id='toXMl'></a>
       * @return {String} 字符串
       */
      toXMl() {
        return ''
      }

      /**
       * 检查输入点是否在多边形内。多边形线上的一个点被认为是内部
       * @param {Point} point 用于测试是否包含在测试多边形中的点
       * @return {Boolean} 如果该点位于多边形内，则返回true
       * @example <caption><h7 id='contains'>检查输入点是否在多边形内</h7></caption>
       * // ES5引入方式
       * const { MultiPolygon, Point } = Zondy.Geometry
       * // ES6引入方式
       * import { MultiPolygon, Point } from "@mapgis/webclient-common"
       * const multiPolygon = new MultiPolygon({
       *   coordinates:  [
       *     // 第一个区
       *     [
       *       // 外圈
       *       [
       *         // 区要首尾相连
       *         [102.0, 2.0],
       *         [103.0, 2.0],
       *         [103.0, 3.0],
       *         [102.0, 3.0],
       *         [102.0, 2.0]
       *       ]
       *     ]
       *   ]
       * })
       * const point = new Point({
       *   coordinates: [100.0, 0.0]
       * })
       * const isIn = multiPolygon.contains(point)
       */
      contains(point) {
        if (!point) throw new Error('传入参数错误')
        const mutiPolygon = T.multiPolygon(this._cloneCoordinates())
        const p = T.point(Point.toCoordinates(point))
        return T.booleanPointInPolygon(p, mutiPolygon)
      }

      /**
       * 通过传入的json构造并返回一个新的几何对象
       * @param {Object} [json] JSON对象
       * @example <caption><h7 id='fromJSON'>通过传入的json构造并返回一个新的几何对象</h7></caption>
       * // ES5引入方式
       * const { MultiPolygon } = Zondy.Geometry
       * // ES6引入方式
       * import { MultiPolygon } from "@mapgis/webclient-common"
       * const json = {
       *   coordinates:  [
       *     // 第一个区
       *     [
       *       // 外圈
       *       [
       *         // 区要首尾相连
       *         [102.0, 2.0],
       *         [103.0, 2.0],
       *         [103.0, 3.0],
       *         [102.0, 3.0],
       *         [102.0, 2.0]
       *       ]
       *     ]
       *   ]
       * }
       * const multiPolygon = new MultiPolygon.fromJSON(json)
       */
      static fromJSON(json) {
        json = defaultValue(json, {})
        return new MultiPolygon(json)
      }

      /**
       * <a id='toJSON'></a>
       * 导出为json对象
       * @return {Object} json对象
       */
      toJSON() {
        const json = super.toJSON()
        json.coordinates = JSON.parse(JSON.stringify(this.coordinates))
        json.extent = this.extent.toJSON()
        return json
      }

      /**
       * 返回如下格式的字符串："x0,y0,x1,y1,x2,y2,x0,y0|x3,y3,x4,y4,x5,y5,x3,y3;x6,y6,x7,y7,x8,y8,x6,y6|x9,y9,x10,y10,x11,y11,x9,y9"<a id='toString'></a>
       * 多边形内每个的多边形由|号分割，多个多边形由;号分割
       * @returns string
       */
      toString() {
        let _str = ''
        // 开始循环多边形
        for (let i = 0; i < this.coordinates.length; i++) {
          // 取得内部多边行中的一个
          const _singlePolygon = this.coordinates[i]
          // 开始循环其内部的圈
          for (let j = 0; j < _singlePolygon.length; j++) {
            // 获取多边形内圈，第一个为多边形本身
            const _innerPolygon = _singlePolygon[j]
            // 开始循环获取内部点
            for (let k = 0; k < _innerPolygon.length; k++) {
              // 多边形里的点
              const _point = _innerPolygon[k]
              // 有可能是xyz，因此for循环
              for (let m = 0; m < _point.length; m++) {
                // 加,号
                _str += `${_point[m]},`
              }
            }
            // 删除最后一个,号
            _str = _str.substring(0, _str.length - 1)
            // 加上|号
            _str += '|'
          }
          // 删除最后一个|号
          _str = _str.substring(0, _str.length - 1)
          // 加上;号
          _str += ';'
        }
        // 删除最后一个;号
        _str = _str.substring(0, _str.length - 1)
        return _str
      }

      /**
       * 返回igs1.0的几何对象<a id='toOldIGSGeometry'></a>
       * @returns Object igs1.0的几何对象
       */
      toOldIGSGeometry() {
        const RegGeom = []
        let reg = {}
        let ring = {}
        for (let i = 0; i < this.coordinates.length; i++) {
          const polygon = this.coordinates[i]
          reg = {
            Rings: [],
            GID: i
          }
          for (let j = 0; j < polygon.length; j++) {
            ring = {
              Arcs: []
            }
            const Dots = []
            for (let k = 0; k < polygon[j].length; k++) {
              const ringCos = polygon[j][k]
              Dots.push({
                x: ringCos[0],
                y: ringCos[1]
              })
            }
            ring.Arcs.push({
              Dots,
              ArcID: j
            })
            reg.Rings.push(ring)
          }
          RegGeom.push(reg)
        }
        return {
          RegGeom
        }
      }

      /**
       * @private
       * @description 拷贝几何信息
       * @returns {Array}
       */
      _cloneCoordinates() {
        return JSON.parse(JSON.stringify(this.coordinates))
      }

      /**
       * @private
       * @description 判断输入是否合法
       * @param {Number} polygonIndex 路径下标
       * @returns {Boolean} 输入是否合法
       */
      _isValidate(polygonIndex) {
        const len = this.coordinates.length
        if (!isNumber(polygonIndex) || polygonIndex < 0 || polygonIndex >= len) {
          return false
        }
        return true
      }

      /**
       * @private
       * @description 计算外包盒
       * @param {Array} coordinates 坐标点
       * @param {Boolean} hasZ 是否是三维
       * @returns {Extent}
       */
      _calcExtent(coordinates, hasZ) {
        const line = T.multiPolygon(coordinates)
        const bbox = T.bbox(line)

        if (!hasZ) {
          return new Extent({
            xmin: bbox[0],
            ymin: bbox[1],
            xmax: bbox[2],
            ymax: bbox[3]
          })
        }
        return new Extent({
          xmin: bbox[0],
          ymin: bbox[1],
          zmin: bbox[2],
          xmax: bbox[3],
          ymax: bbox[4],
          zmax: bbox[5]
        })
      }

      /**
       * 返回IGS所对应的GeometryModule型<a id='getIGSType'></a>
       * @returns string GeometryModule型
       */
      getIGSType() {
        return IGSGeometryType.multiPolygon
      }

      /**
       * 多区对象转区对象数组
       * @return {Array<Polygon>} 区对象数组
       * */
      toPolygonArray() {
        const polygonArray = []
        for (let i = 0; i < this.coordinates.length; i++) {
          polygonArray.push(
            new Polygon({
              coordinates: this.coordinates[i],
              spatialReference: SpatialReference.fromJSON(this.spatialReference)
            })
          )
        }
        return polygonArray
      }

      /**
       * 克隆几何对象
       * @return {Geometry} 克隆后的几何对象
       */
      clone() {
        return new MultiPolygon(this.toJSON())
      }
    }
    Object.defineProperties(MultiPolygon.prototype, {
      /**
       * 外包盒
       * @member {Number} MultiPolygon.prototype.extent
       * */
      extent: {
        configurable: false,
        get() {
          return this._calcExtent(this.coordinates, this.hasZ)
        }
      }
    })

    Zondy.Geometry.MultiPolygon = MultiPolygon
    export default MultiPolygon