    import * as T from '@turf/turf'
    import Geometry from './Geometry'
    import { Log, defaultValue, defined, isNumber } from '../../util'
    import Zondy from '../Zondy'
    import { GeometryType, IGSGeometryType, RadiusUnit } from '../enum'
    import Polygon from './Polygon'
    import Point from './Point'
    import SpatialReference from './SpatialReference'
    import { calcExtent } from './Utiles'
    import {
      getProjUnitByWkid,
      RadiusUnitToMeter,
      ProjUnitWkidMap
    } from '../../util/unit/UnitUtils'

    /**
     * 几何圆，参考示例：<a href='#Circle'>[几何圆对象]</a>
     * <br><br>[ES5引入方式]：<br/>
     * Zondy.Geometry.Circle() <br/>
     * [ES6引入方式]：<br/>
     * import { Circle } from "@mapgis/webclient-common" <br/>
     * <br/>
     * @class Circle
     * @moduleEX GeometryModule
     * @extends Geometry
     * @param {Object} options 构造参数
     * @param {Point|Number[]} [options.center] 圆心坐标
     * @param {Number} [options.radius = 100] 圆心半径
     * @param {RadiusUnit} [options.radiusUnit = 'meters'] 圆心半径单位
     * @param {Number} [options.numberOfPoints = 40] 圆转换为区插值点个数
     * @param {Number} [options.geodesic = false] 是否显示为地理圆减少失真。当圆spatialReference为4326或3857时，设置有true有效，其他坐标系默认显示为投影坐标系的标准圆。
     * @param {SpatialReference} [options.spatialReference = new Zondy.SpatialReference('EPSG:4326')] 几何点的空间参考系，默认4326，当不是4326时请指定坐标系，方便进行投影转换，参考示例：<a href='#SpatialReference'>[指定坐标系]</a>
     * @summary <h5>支持如下方法：</h5>
     * <a href='#toPolygon'>[1、导出为区]</a><br/>
     * <a href='#toString'>[2、返回字符串]</a><br/>
     * <a href='#toXMl'>[3、导出为OGC服务要求的xml字符串]</a><br/>
     * <a href='#getIGSType'>[4、返回IGS所对应的GeometryModule型]</a><br/>
     * <a href='#fromJSON'>[5、通过传入的json构造并返回一个新的几何对象]</a><br/>
     * <a href='#toJSON'>[6、导出为json对象]</a><br/>
     * [7、克隆几何对象]{@link Geometry#clone}
     *
     * @example <caption><h7 id='Circle'>创建几何对象</h7></caption>
     * // ES5引入方式
     * const { Circle } = Zondy.Geometry
     * // ES6引入方式
     * import { Circle } from "@mapgis/webclient-common"
     * new Circle({
     *   // 中心点
     *   center:[113,42],
     *   // 半径
     *   radius:20
     * })
     *
     * @example <caption><h7 id='spatialReference'>指定坐标系</h7></caption>
     * // ES5引入方式
     * const { Circle } = Zondy.Geometry
     * const { SpatialReference } = Zondy
     * // ES6引入方式
     * import { Circle, SpatialReference } from "@mapgis/webclient-common"
     * new Circle({
     *   // 3857坐标系的点
     *   // 中心点
     *   center:[12060733.232006868, 3377247.5680546067],
     *   // 半径
     *   radius:10000,
     *   // 当不是4326时请指定坐标系，方便进行投影转换
     *   spatialReference: new SpatialReference('EPSG:3857')
     * })
     */
    class Circle extends Geometry {
      constructor(options) {
        super(options)
        options = defaultValue(options, {})
        /**
         * 圆的中心点
         * @member {Number[]} Circle.prototype.center
         */
        this.center = defaultValue(options.center, undefined)
        if (!this.center) {
          throw new Error('缺少圆心坐标')
        }
        this.center = Point.toCoordinates(this.center)
        /**
         * 圆的半径
         * @member {Number} Circle.prototype.radius
         * @default 100
         */
        this.radius = defaultValue(options.radius, 100)
        if (!isNumber(this.radius)) {
          throw new Error('半径必须为数字')
        }

        /**
         * 是否显示为地理圆减少失真。当圆spatialReference为4326或3857时，设置有true有效，其他坐标系默认显示为投影坐标系的标准圆。
         * @member {Boolean} Circle.prototype.geodesic
         * @default false
         */
        this.geodesic = defaultValue(options.geodesic, false)
        /**
         * 半径单位
         * @member {RadiusUnit} Circle.prototype.radiusUnit
         */
        this.radiusUnit = defaultValue(options.radiusUnit, RadiusUnit.degrees)
        // 定义圆曲线上点的数量。
        this.numberOfPoints = defaultValue(options.numberOfPoints, 40)
        if (!isNumber(this.numberOfPoints)) {
          throw new Error('圆曲线上点的数量必须为数字')
        }
        // 表示GeometryModule型的字符串值
        this.type = GeometryType.circle
        // 计算是否为三维
        if (this.center.length === 3) {
          this.hasZ = true
        }
      }

      /**
       * 通过传入的json构造并返回一个新的几何对象
       * @param {Object} [json] JSON对象
       * @example <caption><h7 id='fromJSON'>通过传入的json构造并返回一个新的几何对象</h7></caption>
       * // ES5引入方式
       * const { Circle } = Zondy.Geometry
       * // ES6引入方式
       * import { Circle } from "@mapgis/webclient-common"
       * const json = {
       *   // 中心点
       *   center:[113, 42],
       *   // 半径
       *   radius:20
       * }
       * const circle = Circle.fromJSON(json)
       */
      static fromJSON(json) {
        json = defaultValue(json, {})
        return new Circle(json)
      }

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

      /**
       * 导出为区
       * @returns {Polygon} 返回区对象
       * @example <caption><h7 id='toPolygon'>导出为区</h7></caption>
       * // ES5引入方式
       * const { Circle } = Zondy.Geometry
       * // ES6引入方式
       * import { Circle } from "@mapgis/webclient-common"
       * const circle = new Circle({
       *   // 中心点
       *   center:[113, 42],
       *   // 半径
       *   radius:20
       * })
       * const polygon = circle.toPolygon()
       */
      toPolygon() {
        // 先确定当前参考系时地理坐标系还是投影坐标系
        const spatialReference = this.spatialReference
        const geodesic = this.geodesic
        const unitRatio = RadiusUnitToMeter[this.radiusUnit] || 1
        // 将半径转换为m
        let radius = this.radius * unitRatio
        let currentSpDesc = 'geographic'

        if (spatialReference.isWebMercator) {
          currentSpDesc = 'webMercator'
        } else if (
          spatialReference.wkid &&
          defined(ProjUnitWkidMap[spatialReference.wkid])
        ) {
          currentSpDesc = 'proj'
        } else if (spatialReference.wkt) {
          const wkt = spatialReference.wkt
          // 兼容各种版本的wkt和projJS
          const map = ['PROJS', 'proj4', 'PROJCRS', '+proj']
          const isProj = map.some((v) => wkt.indexOf(v) === 0)
          if (isProj) {
            currentSpDesc = 'proj'
          }
        }

        if (geodesic) {
          switch (currentSpDesc) {
            case 'proj': {
              Log.error('暂时仅支持墨卡托投影坐标系和地理坐标系使用geodesic属性')
              break
            }
            case 'webMercator':
            case 'geographic': {
              // 计算真实的地理圆
              // link https://stackoverflow.com/questions/37599561/drawing-a-circle-with-the-radius-in-miles-meters-with-mapbox-gl-js/39006388#39006388
              const max = 85.0511287798
              const R = 6378137
              const d = Math.PI / 180
              let coords = {
                latitude: this.center[1],
                longitude: this.center[0]
              }
              if (currentSpDesc === 'webMercator') {
                coords = {
                  longitude: (coords.longitude * 180) / Math.PI / R,
                  latitude:
                    ((2 * Math.atan(Math.exp(coords.latitude / R)) - Math.PI / 2) *
                      180) /
                    Math.PI
                }
              }
              const km = radius / 1000
              const distanceX =
                km / (111.32 * Math.cos((coords.latitude * Math.PI) / 180))
              const distanceY = km / 110.574
              const lnglatArr = []
              for (let i = 0; i < this.numberOfPoints; i += 1) {
                const theta = (i / this.numberOfPoints) * (2 * Math.PI)
                const x = distanceX * Math.cos(theta)
                const y = distanceY * Math.sin(theta)
                const lng = coords.longitude + x
                const lat = coords.latitude + y
                if (currentSpDesc === 'webMercator') {
                  const _lat = Math.max(Math.min(85.0511287798, lat), -max)
                  const sin = Math.sin(_lat * d)
                  lnglatArr.push([
                    R * lng * d,
                    (R * Math.log((1 + sin) / (1 - sin))) / 2
                  ])
                } else {
                  lnglatArr.push([lng, lat])
                }
              }
              if (lnglatArr.length <= 3) Log.error('生成圆几何点个数不能小于3')
              // 首位点闭合
              lnglatArr.push(lnglatArr[0])
              return new Polygon({
                coordinates: [lnglatArr],
                spatialReference: SpatialReference.fromJSON(this.spatialReference)
              })
            }
            default: {
              break
            }
          }
        } else {
          if (currentSpDesc === 'geographic') {
            radius /= RadiusUnitToMeter['degrees']
          }
          const coordinates = this._tranCircleToPath(
            this.center,
            radius,
            this.numberOfPoints
          )
          return new Polygon({
            coordinates,
            spatialReference: SpatialReference.fromJSON(this.spatialReference)
          })
        }
      }

      /**
       * 返回如下格式的字符串："x,y,radius",若有z，则返回"x,y,z,radius"<a id='toString'></a>
       * @returns {String} 返回几何字符串
       */
      toString() {
        if (this.hasZ) {
          return `${this.center[0]},${this.center[1]},${this.center[2]},${this.radius}`
        }
        return `${this.center[0]},${this.center[1]},${this.radius}`
      }

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

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

      /**
       * @function Circle.prototype._tranCircleToPath
       * @private
       * @description 转换圆为区
       * @returns {Number[][]} 返回转换后的坐标数组
       */
      _tranCircleToPath(center, radius, numberOfPoints) {
        const disRad = (Math.PI * 2) / numberOfPoints
        let angle = 0
        let coordinates = []
        const coords = []
        for (let i = 0; i < numberOfPoints; i++) {
          angle -= disRad // clockwise
          const point = [
            center[0] + radius * Math.cos(angle),
            center[1] + radius * Math.sin(angle)
          ]
          coords.push(point)
        }
        const last = coords[coords.length - 1]
        const first = coords[0]
        if (
          Math.abs(last[0] - first[0]) > 10e-8 ||
          Math.abs(last[1] - first[1]) > 10e-8
        ) {
          coords.push([first[0], first[1]])
        }

        coordinates.push(coords)

        if (this.hasZ) {
          coordinates = coordinates.map((ring) => {
            return ring.map((coordinate) => {
              return [coordinate[0], coordinate[1], center[2]]
            })
          })
        }

        return coordinates
      }

      /**
       * @function Circle.prototype._calcExtent
       * @private
       * @description 计算外包盒
       * @param {Boolean} hasZ 是否是三维
       * @returns {Extent} 外包盒
       */
      _calcExtent(hasZ) {
        const polygon = this.toPolygon()
        return calcExtent(polygon._cloneCoordinates(), hasZ)
      }

      /**
       * 克隆几何对象
       * @return {Geometry} 克隆后的几何对象
       */
      clone() {
        return new Circle(this.toJSON())
      }
    }

    Object.defineProperties(Circle.prototype, {
      /**
       * 外包盒
       * @member {Number} Circle.prototype.extent
       * */
      extent: {
        configurable: false,
        get() {
          return this._calcExtent(this.hasZ)
        }
      }
    })

    Zondy.Geometry.Circle = Circle
    export default Circle