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

    /**
     * 几何弧段，三个点确定一个弧段，参考示例：<a href='#Arc'>[几何圆对象]</a>
     * <br><br>[ES5引入方式]：<br/>
     * Zondy.Geometry.Arc() <br/>
     * [ES6引入方式]：<br/>
     * import { Arc } from "@mapgis/webclient-common" <br/>
     * <br/>
     * @class Arc
     * @moduleEX GeometryModule
     * @private
     * @extends Geometry
     * @param {Object} options 构造参数
     * @param {Point|Number[]} [options.point1] 弧段上的第一个点
     * @param {Point|Number[]} [options.point2] 弧段上的第二个点
     * @param {Point|Number[]} [options.point3] 弧段上的第三个点
     * @param {Number} [options.numberOfPoints = 120] 圆转换为区插值点个数
     * @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='Arc'>创建几何对象</h7></caption>
     * // ES5引入方式
     * const { Arc } = Zondy.Geometry
     * // ES6引入方式
     * import { Arc } from "@mapgis/webclient-common"
     * new Arc({
     *   // 弧段上的第一个点
     *   point1: [113.5, 30],
     *   // 弧段上的第二个点
     *   point2: [114, 30.5],
     *   // 弧段上的第三个点
     *   point3: [113.5, 31]
     * })
     *
     * @example <caption><h7 id='spatialReference'>指定坐标系</h7></caption>
     * // ES5引入方式
     * const { Arc } = Zondy.Geometry
     * const { SpatialReference } = Zondy
     * // ES6引入方式
     * import { Arc, SpatialReference } from "@mapgis/webclient-common"
     * new Arc({
     *   // 弧段上的第一个点
     *   point1: [],
     *   // 弧段上的第二个点
     *   point2: [],
     *   // 弧段上的第三个点
     *   point3: []
     *   // 当不是4326时请指定坐标系，方便进行投影转换
     *   spatialReference: new SpatialReference('EPSG:3857')
     * })
     */
    class Arc extends Geometry {
      constructor(options) {
        super(options)
        options = defaultValue(options, {})
        /**
         * 表示GeometryModule型的字符串值
         * @member {GeometryType} Arc.prototype.type
         */
        this.type = GeometryType.arc

        /**
         * 几何点的坐标数组
         * @member {Array} Arc.prototype.coordinates
         */
        this.coordinates = defaultValue(options.coordinates, [])

        /**
         * 弧段上的第一个点
         * @member {Number[]} Arc.prototype.point1
         */
        this.point1 = this.coordinates[0]
        if (!this.point1) {
          throw new Error('缺少弧段上的第一个点坐标')
        }
        this.point1 = Point.toCoordinates(this.point1)

        /**
         * 弧段上的第二个点
         * @member {Number[]} Arc.prototype.point2
         */
        this.point2 = this.coordinates[1]
        if (!this.point2) {
          throw new Error('缺少弧段上的第二个点坐标')
        }
        this.point2 = Point.toCoordinates(this.point2)

        /**
         * 弧段上的第三个点
         * @member {Number[]} Arc.prototype.point3
         */
        this.point3 = this.coordinates[2]
        if (!this.point3) {
          throw new Error('缺少弧段上的第三个点坐标')
        }
        this.point3 = Point.toCoordinates(this.point3)

        /**
         * 定义弧段上点的数量
         * @member {Number} Arc.prototype.numberOfPoints
         */
        this.numberOfPoints = defaultValue(options.numberOfPoints, 120)
        if (!isNumber(this.numberOfPoints)) {
          throw new Error('圆曲线上点的数量必须为数字')
        }

        // 计算是否为三维
        if (
          this.point1.length === 3 &&
          this.point1.length === 3 &&
          this.point1.length === 3
        ) {
          this.hasZ = true
        }

        // 获取圆心
        this._getCenter()
        // 设置第一个点
        this._pointBegan = this.point1
        // 设置第最后一个点
        this._pointEnd = this.point3
      }

      /**
       * 通过弧段上的三个点计算圆心坐标，参考资料https://www.jianshu.com/p/f99246170561和桌面逻辑
       * @private
       * */
      _getCenter() {
        const x1 = this.point1[0]
        const x2 = this.point2[0]
        const x3 = this.point3[0]
        const y1 = this.point1[1]
        const y2 = this.point2[1]
        const y3 = this.point3[1]
        const z1 = 0
        const z2 = 0
        const z3 = 0
        const a1 = y1 * z2 - y2 * z1 - y1 * z3 + y3 * z1 + y2 * z3 - y3 * z2
        const b1 = -(x1 * z2 - x2 * z1 - x1 * z3 + x3 * z1 + x2 * z3 - x3 * z2)
        const c1 = x1 * y2 - x2 * y1 - x1 * y3 + x3 * y1 + x2 * y3 - x3 * y2
        const d1 = -(
          x1 * y2 * z3 -
          x1 * y3 * z2 -
          x2 * y1 * z3 +
          x2 * y3 * z1 +
          x3 * y1 * z2 -
          x3 * y2 * z1
        )
        const a2 = 2 * (x2 - x1)
        const b2 = 2 * (y2 - y1)
        const c2 = 2 * (z2 - z1)
        const d2 = x1 * x1 + y1 * y1 + z1 * z1 - x2 * x2 - y2 * y2 - z2 * z2

        const a3 = 2 * (x3 - x1)
        const b3 = 2 * (y3 - y1)
        const c3 = 2 * (z3 - z1)
        const d3 = x1 * x1 + y1 * y1 + z1 * z1 - x3 * x3 - y3 * y3 - z3 * z3

        const cx =
          -(
            b1 * c2 * d3 -
            b1 * c3 * d2 -
            b2 * c1 * d3 +
            b2 * c3 * d1 +
            b3 * c1 * d2 -
            b3 * c2 * d1
          ) /
          (a1 * b2 * c3 -
            a1 * b3 * c2 -
            a2 * b1 * c3 +
            a2 * b3 * c1 +
            a3 * b1 * c2 -
            a3 * b2 * c1)
        const cy =
          (a1 * c2 * d3 -
            a1 * c3 * d2 -
            a2 * c1 * d3 +
            a2 * c3 * d1 +
            a3 * c1 * d2 -
            a3 * c2 * d1) /
          (a1 * b2 * c3 -
            a1 * b3 * c2 -
            a2 * b1 * c3 +
            a2 * b3 * c1 +
            a3 * b1 * c2 -
            a3 * b2 * c1)
        const cz =
          -(
            a1 * b2 * d3 -
            a1 * b3 * d2 -
            a2 * b1 * d3 +
            a2 * b3 * d1 +
            a3 * b1 * d2 -
            a3 * b2 * d1
          ) /
          (a1 * b2 * c3 -
            a1 * b3 * c2 -
            a2 * b1 * c3 +
            a2 * b3 * c1 +
            a3 * b1 * c2 -
            a3 * b2 * c1)

        if (this.hasZ) {
          this.center = [cx, cy, this.point1[2]]
        } else {
          this.center = [cx, cy]
        }
      }

      /**
       * 通过传入的json构造并返回一个新的几何对象
       * @param {Object} [json] JSON对象
       * @example <caption><h7 id='fromJSON'>通过传入的json构造并返回一个新的几何对象</h7></caption>
       * // ES5引入方式
       * const { Arc } = Zondy.Geometry
       * // ES6引入方式
       * import { Arc } from "@mapgis/webclient-common"
       * const json = {
       *   point1: [113.5, 30],
       *   point2: [114, 30.5],
       *   point3: [113.5, 31]
       * }
       * const circle = Arc.fromJSON(json)
       */
      static fromJSON(json) {
        json = defaultValue(json, {})
        return new Arc(json)
      }

      /**
       * <a id='toJSON'></a>
       * 导出为json对象
       * @return {Object} json对象
       */
      toJSON() {
        const json = super.toJSON()
        json.point1 =
          this.point1 instanceof Point ? this.point1.toJSON() : this.point1
        json.point2 =
          this.point2 instanceof Point ? this.point2.toJSON() : this.point2
        json.point3 =
          this.point3 instanceof Point ? this.point3.toJSON() : this.point3
        json.numberOfPoints = this.numberOfPoints
        json.extent = this.extent.toJSON()

        return json
      }

      /**
       * 导出为区
       * @returns {Polygon} 返回区对象
       * @example <caption><h7 id='toPolygon'>导出为区</h7></caption>
       * // ES5引入方式
       * const { Arc } = Zondy.Geometry
       * // ES6引入方式
       * import { Arc } from "@mapgis/webclient-common"
       * const circle = new Arc({
       *   // 中心点
       *   center:[113, 42],
       *   // 半径
       *   radius:20
       * })
       * const polygon = circle.toPolygon()
       */
      toPolygon() {
        const coordinates = this._tranArcToPath(
          this.center,
          this._pointBegan,
          this._pointEnd,
          this.numberOfPoints
        )
        coordinates[0].push(coordinates[0][0])
        return new Polygon({
          coordinates,
          spatialReference: SpatialReference.fromJSON(this.spatialReference)
        })
      }

      toLineString() {
        const coordinates = this._tranArcToPath(
          this.center,
          this._pointBegan,
          this._pointEnd,
          this.numberOfPoints
        )
        return new LineString({
          coordinates: coordinates[0],
          spatialReference: SpatialReference.fromJSON(this.spatialReference)
        })
      }

      /**
       * @function Arc.prototype._tranArcToPath
       * @private
       * @description 转换三点弧为区
       * @returns {Number[][]} 返回转换后的坐标数组
       */
      _tranArcToPath(center, pointBegan, pointEnd, numberOfPoints) {
        let _center = JSON.parse(JSON.stringify(center))
        let _pointBegan = JSON.parse(JSON.stringify(pointBegan))
        let _pointEnd = JSON.parse(JSON.stringify(pointEnd))

        // 如果不是经纬度坐标系，则投影到经纬度坐标系，方便求角度
        if (!this.spatialReference.isGeographic) {
          const _sr = new SpatialReference({
            wkid: 4326
          })
          _center = Projection.project(
            new Point({
              coordinates: _center,
              spatialReference: this.spatialReference
            }),
            _sr
          ).coordinates
          _pointBegan = Projection.project(
            new Point({
              coordinates: _pointBegan,
              spatialReference: this.spatialReference
            }),
            _sr
          ).coordinates
          _pointEnd = Projection.project(
            new Point({
              coordinates: _pointEnd,
              spatialReference: this.spatialReference
            }),
            _sr
          ).coordinates
        }

        // 构造turf的点
        const _centerT = T.point(_center)
        const _pointBeganT = T.point(_pointBegan)
        const _pointEndT = T.point(_pointEnd)

        // 计算圆心半径
        const line = new LineString({
          coordinates: [center, pointBegan]
        })
        const _radius = GeometryEngine.planarLength(line)

        // 设置起始角度
        let _beganAngle = 90 - T.rhumbBearing(_centerT, _pointBeganT)

        // 设置结束角度
        const _endAngle = 90 - T.rhumbBearing(_centerT, _pointEndT)

        // 色设置弧段旋转的角度
        const _arcAngle = _endAngle - _beganAngle

        // 计算步长
        const _step = (Math.PI * (_arcAngle / 180)) / numberOfPoints

        // 将角度转为弧度
        _beganAngle = Math.PI * (_beganAngle / 180)

        // 开始极端弧段
        let _coordinates = []
        // 外圈点坐标
        const _ringPoints = [pointBegan]
        for (let i = 0; i < numberOfPoints - 1; i++) {
          _beganAngle += _step
          const point = [
            center[0] + _radius * Math.cos(_beganAngle),
            center[1] + _radius * Math.sin(_beganAngle)
          ]
          if (_endAngle <= 180) {
            if (point[0] > _pointEnd[0]) {
              _ringPoints.push(point)
            }
          } else if (point[0] < _pointEnd[0]) {
            _ringPoints.push(point)
          }
        }
        _ringPoints.push(pointEnd)
        _coordinates.push(_ringPoints)

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

        return _coordinates
      }

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

        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]
        })
      }

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

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

    Zondy.Geometry.Arc = Arc
    export default Arc