    import {defaultValue, isNumber, toJSON} from '../../util'
    import Zondy from '../Zondy'
    import Geometry from './Geometry'
    import { GeometryType, IGSGeometryType } from '../enum'
    import Extent from './Extent'

    /**
     * 点几何对象
     * <br><br>[ES5引入方式]：<br/>
     * Zondy.Geometry.Point() <br/>
     * [ES6引入方式]：<br/>
     * import { Point } from "@mapgis/webclient-common" <br/>
     * <br/>
     * @class Point
     * @moduleEX GeometryModule
     * @extends Geometry
     * @param {Object} options 构造参数
     * @param {Array} [options.coordinates = []] 几何点的坐标数组，支持任意单位，参考示例：<a href='#Point'>[点几何对象]</a>
     * @param {SpatialReference} [options.spatialReference = new Zondy.SpatialReference('EPSG:4326')] 几何点的空间参考系，默认4326，当不是4326时请指定坐标系，方便进行投影转换，参考示例：<a href='#SpatialReference'>[指定坐标系]</a>
     *
     * @summary <h5>支持如下方法：</h5>
     * <a href='#toCoordinates'>[1、获取坐标数组]</a><br/>
     * <a href='#copy'>[2、将另一个点对象的所有值复制到当前点对象]</a><br/>
     * <a href='#distance'>[3、计算与另一个点的距离]</a><br/>
     * <a href='#equals'>[4、判断两个点是否相等]</a><br/>
     * <a href='#normalize'>[5、归一化计算]</a><br/>
     * <a href='#toString'>[6、返回字符串]</a><br/>
     * <a href='#getIGSType'>[7、返回IGS所对应的GeometryModule型]</a><br/>
     * <a href='#toOldIGSGeometry'>[8、返回igs1.0的几何对象]</a>
     * <a href='#fromJSON'>[9、通过传入的json构造并返回一个新的几何对象]</a><br/>
     * <a href='#toJSON'>[10、导出为json对象]</a><br/>
     * [11、克隆几何对象]{@link Geometry#clone}
     *
     * @example <caption><h7 id='Point'>创建几何对象</h7></caption>
     * // ES5引入方式
     * const { Point } = Zondy.Geometry
     * // ES6引入方式
     * import { Point } from "@mapgis/webclient-common"
     * new Point({
     *   coordinates: [100.0, 0.0]
     * })
     *
     * @example <caption><h7 id='SpatialReference'>指定坐标系</h7></caption>
     * // ES5引入方式
     * const { Point } = Zondy.Geometry
     * const { SpatialReference } = Zondy
     * // ES6引入方式
     * import { Point, SpatialReference } from "@mapgis/webclient-common"
     * new Point({
     *   // 现在为3857坐标系
     *   coordinates: [12929863.44711455, 3377247.5680546067],
     *   // 当不是4326时请指定坐标系，方便进行投影转换
     *   spatialReference: new SpatialReference('EPSG:3857')
     * })
     */

    class Point extends Geometry {
      constructor(options) {
        super(options)
        // 构造几何对象
        options = defaultValue(options, {})
        /**
         * 几何点的坐标数组
         * @member {Array} Point.prototype.coordinates
         */
        this.coordinates = defaultValue(options.coordinates, [])
        /**
         * 几何类型
         * @member {GeometryType} Point.prototype.type
         */
        this.type = GeometryType.point

        // 判断坐标是否合理
        if (!Array.isArray(this.coordinates)) {
          throw new Error('坐标必须为数组')
        }
        if (this.coordinates.length < 2 || this.coordinates.length > 3) {
          throw new Error('坐标数组的长度必须在合理范围内(2或者3)')
        }
        if (!isNumber(this.coordinates[0]) || !isNumber(this.coordinates[1])) {
          throw new Error('坐标数组成员必须为数字')
        }

        // 计算hasZ的值
        if (this.coordinates.length === 3) {
          this.hasZ = true
        } else {
          this.hasZ = false
        }
      }

      /**
       * @static
       * 获取坐标数组
       * @param {Point | Number[]} point 输入坐标信息
       * @return {Number[]} 坐标数组
       * @example <caption><h7 id='toCoordinates'>获取坐标数组</h7></caption>
       * // ES5引入方式
       * const { Point } = Zondy.Geometry
       * // ES6引入方式
       * import { Point } from "@mapgis/webclient-common"
       * const point = new Point({
       *   coordinates: [100.0, 0.0]
       * })
       * const coordinates = Point.toCoordinates(point)
       */
      static toCoordinates(point) {
        if (!point) throw new Error('输入参数不合法.')
        let coordinates = JSON.parse(JSON.stringify(point))
        if (point instanceof Point) {
          coordinates = point._cloneCoordinates()
        }
        return coordinates
      }

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

      /**
       * 将另一个点对象的所有值复制到当前点对象
       * @param {Point} point 被复制的点对象
       * @return {Point} 修改后的Point
       * @example <caption><h7 id='copy'>将另一个点对象的所有值复制到当前点对象</h7></caption>
       * // ES5引入方式
       * const { Point } = Zondy.Geometry
       * // ES6引入方式
       * import { Point } from "@mapgis/webclient-common"
       * const point1 = new Point({
       *   coordinates: [100.0, 0.0]
       * })
       * const point2 = new Point({
       *   coordinates: [200.0, 10.0]
       * })
       * point1.copy(point2)
       */
      copy(point) {
        // 输入检测
        Point.fromJSON(point.toJSON())
        return this
      }

      /**
       * 计算与另一个点的距离
       * @param {Point|Number[]} point 用于计算距离的点
       * @return {Number} 距离
       * @example <caption><h7 id='distance'>计算与另一个点的距离</h7></caption>
       * // ES5引入方式
       * const { Point } = Zondy.Geometry
       * // ES6引入方式
       * import { Point } from "@mapgis/webclient-common"
       * const point1 = new Point({
       *   coordinates: [100.0, 0.0]
       * })
       * const point2 = new Point({
       *   coordinates: [200.0, 10.0]
       * })
       * const distance = point1.distance(point2)
       */
      distance(point) {
        if (!point) throw new Error('输入参数不合法.')
        // 判断传入point参数的合理性
        const coordinates = Point.toCoordinates(point)
        const width = Math.abs(this.coordinates[0] - coordinates[0])
        const height = Math.abs(this.coordinates[1] - coordinates[1])
        let zlength = 0
        if (this.hasZ && coordinates.length >= 3) {
          zlength = Math.abs(this.coordinates[2] - point.coordinates[[2]])
        }
        return Math.sqrt(
          Math.pow(width, 2) + Math.pow(height, 2) + Math.pow(zlength, 2)
        )
      }

      /**
       * 判断两个点是否相等
       * @param {Point | Array<Number>} [point=null] 点对象或坐标数组
       * @return {Boolean} 两个点是否相等
       * @example <caption><h7 id='equals'>判断两个点是否相等</h7></caption>
       * // ES5引入方式
       * const { Point } = Zondy.Geometry
       * // ES6引入方式
       * import { Point } from "@mapgis/webclient-common"
       * const point1 = new Point({
       *   coordinates: [100.0, 0.0]
       * })
       * const point2 = new Point({
       *   coordinates: [200.0, 10.0]
       * })
       * const equal = point1.equals(point2)
       */
      equals(point) {
        if (point instanceof Point) {
          // 二维点
          if (!this.hasZ) {
            if (
              !!Math.abs(this.coordinates[0] - point.coordinates[0]) <= 10e-8 &&
              !!Math.abs(this.coordinates[1] - point.coordinates[1]) <= 10e-8
            ) {
              return true
            }
            return false
          }
          // 三维点
          else {
            if (
              !!Math.abs(this.coordinates[0] - point.coordinates[0]) <= 10e-8 &&
              !!Math.abs(this.coordinates[1] - point.coordinates[1]) <= 10e-8 &&
              !!Math.abs(this.coordinates[2] - point.coordinates[2]) <= 10e-8
            ) {
              return true
            }
            return false
          }
        }
        // 点数组
        else if (point instanceof Array) {
          // 二维点
          if (point.length === 2) {
            if (
              !!Math.abs(this.coordinates[0] - point[0]) <= 10e-8 &&
              !!Math.abs(this.coordinates[1] - point[1]) <= 10e-8
            ) {
              return true
            }
            return false
          }
          // 三维点
          else if (point.length === 3) {
            if (
              !!Math.abs(this.coordinates[0] - point[0]) <= 10e-8 &&
              !!Math.abs(this.coordinates[1] - point[1]) <= 10e-8 &&
              !!Math.abs(this.coordinates[2] - point[2]) <= 10e-8
            ) {
              return true
            }
            return false
          }
          // 点数量不对
          else {
            throw new Error('坐标数组的数量有误!')
          }
        } else {
          throw new Error('必须为点对象或坐标数组才能比较是否相等!')
        }
      }

      /**
       * 归一化计算
       * @returns {Point} 归一化后的点对象
       * @example <caption><h7 id='normalize'>归一化计算</h7></caption>
       * // ES5引入方式
       * const { Point } = Zondy.Geometry
       * // ES6引入方式
       * import { Point } from "@mapgis/webclient-common"
       * const point1 = new Point({
       *   coordinates: [100.0, 0.0]
       * })
       * const normalize = point1.normalize()
       */
      normalize() {
        let point
        // 开根号计算
        if (this.hasZ) {
          point = new Point({
            coordinates: [0, 0, 0]
          })
        } else {
          point = new Point({
            coordinates: [0, 0]
          })
        }
        const dis = this.distance(point)
        const coordinates = this._cloneCoordinates()
        coordinates[0] /= dis
        coordinates[1] /= dis
        if (this.hasZ) {
          coordinates[2] /= dis
        }
        this.coordinates = coordinates
        return this
      }

      /**
       * 返回如下格式的字符串："x,y",如果有z，则返回"x,y,z"
       * @returns {String} 字符串
       * @example <caption><h7 id='toString'>返回字符串</h7></caption>
       * // ES5引入方式
       * const { Point } = Zondy.Geometry
       * // ES6引入方式
       * import { Point } from "@mapgis/webclient-common"
       * const point1 = new Point({
       *   coordinates: [100.0, 0.0]
       * })
       * const str = point1.toString()
       */
      toString() {
        if (!this.hasZ) {
          return `${this.coordinates[0]},${this.coordinates[1]}`
        }
        return `${this.coordinates[0]},${this.coordinates[1]},${this.coordinates[2]}`
      }

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

        return json
      }

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

      /**
       * 返回igs1.0的几何对象<a id='toOldIGSGeometry'></a>
       * @returns Object igs1.0的几何对象
       */
      toOldIGSGeometry() {
        return {
          PntGeom: [
            {
              Dot: {
                x: this.coordinates[0],
                y: this.coordinates[1]
              }
            }
          ],
          GID: parseInt(String(Math.random() * 10000000))
        }
      }

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

      /**
       * @function Point.prototype._calcExtent
       * @private
       * @description 计算外包盒
       * @param {Array} coordinates 坐标点
       * @param {Boolean} hasZ 是否是三维
       * @returns {Extent}
       */
      _calcExtent(coordinates, hasZ) {
        if (!hasZ) {
          return new Extent({
            xmin: coordinates[0],
            ymin: coordinates[1],
            xmax: coordinates[0],
            ymax: coordinates[1]
          })
        }
        return new Extent({
          xmin: coordinates[0],
          ymin: coordinates[1],
          zmin: coordinates[2],
          xmax: coordinates[0],
          ymax: coordinates[1],
          zmax: coordinates[2]
        })
      }

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

    Zondy.Geometry.Point = Point
    export default Point