import {SuperMap} from '../SuperMap'
import {Point} from '../commontypes/geometry/Point'
import {MultiPoint} from '../commontypes/geometry/MultiPoint'
import {LinearRing} from '../commontypes/geometry/LinearRing'
import {LineString} from '../commontypes/geometry/LineString'
import {MultiLineString} from '../commontypes/geometry/MultiLineString'
import {Polygon} from '../commontypes/geometry/Polygon'
import {MultiPolygon} from '../commontypes/geometry/MultiPolygon'
import {ServerStyle} from './ServerStyle'
import {Route} from './Route'
import {Util} from '../commontypes/Util'
import {GeometryType} from '../REST'

/**
 * @private
 * @class SuperMap.ServerGeometry
 * @category  iServer
 * @classdesc 服务端几何对象类。
 * 该类描述几何对象（矢量）的特征数据（坐标点对、几何对象的类型等）。
 * 基于服务端的空间分析、空间关系运算、查询等 GIS 服务功能使用服务端几何对象。
 */
export class ServerGeometry {
    /*
     * Constructor: SuperMap.ServerGeometry
     * 服务端几何对象类构造函数。
     *
     * Parameters:
     * options - {Object} 参数。
     *
     * Allowed options properties:
     * id - {String} 服务端几何对象唯一标识符。
     * style - {<SuperMap.ServerStyle>}  服务端几何对象的风格。
     * parts - {Array(Number)} 服务端几何对象中各个子对象所包含的节点个数。
     * points - {Array(<Point>)} 组成几何对象的节点的坐标对数组。
     * type - {<SuperMap.GeometryType>} 几何对象的类型。
     */
  constructor (options) {
        /*
         * APIProperty: id
         * {String} 服务端几何对象唯一标识符。
         */
    this.id = 0

        /*
         * APIProperty: style
         * {<SuperMap.ServerStyle>} 服务端几何对象的风格(ServerStyle)。
         */
    this.style = null
        /*
         * APIProperty: parts
         * {Array(Number)} 服务端几何对象中各个子对象所包含的节点个数。
         * 1.几何对象从结构上可以分为简单几何对象和复杂几何对象。
         * 简单几何对象与复杂几何对象的区别：简单的几何对象一般为单一对象，
         * 而复杂的几何对象由多个简单对象组成或经过一定的空间运算之后产生，
         * 如：矩形为简单的区域对象，而中空的矩形为复杂的区域对象。
         * 2.通常情况，一个简单几何对象的子对象就是它本身，
         * 因此对于简单对象来说的该字段为长度为1的整型数组，
         * 该字段的值就是这个简单对象节点的个数。
         * 如果一个几何对象是由几个简单对象组合而成的，
         * 例如，一个岛状几何对象由3个简单的多边形组成而成，
         * 那么这个岛状的几何对象的 Parts 字段值就是一个长度为3的整型数组，
         * 数组中每个成员的值分别代表这三个多边形所包含的节点个数。
         */
    this.parts = null
        /*
         * APIProperty: points
         * {Array(<Point>)} 组成几何对象的节点的坐标对数组。
         * 1.所有几何对象（点、线、面）都是由一些简单的点坐标组成的，
         * 该字段存放了组成几何对象的点坐标的数组。
         * 对于简单的面对象，他的起点和终点的坐标点相同。
         * 2.对于复杂的几何对象，根据 Parts 属性来确定每一个组成复杂几何对象的简单对象所对应的节点的个数，
         * 从而确定 Points 字段中坐标对的分配归属问题。
         */
    this.points = null
        /*
         * APIProperty: type
         * {<SuperMap.GeometryType>} 几何对象的类型(GeometryType)。
         */
    this.type = null
        /*
         * APIProperty: prjCoordSys
         * {Object}投影坐标参数,现仅在缓冲区分析中有效。
         */
    this.prjCoordSys = null
    if (options) {
      Util.extend(this, options)
    }

    this.CLASS_NAME = 'SuperMap.ServerGeometry'
  }

    /*
     * APIMethod: destroy
     * 释放资源，将引用资源的属性置空。
     */
  destroy () {
    var me = this
    me.id = null
    me.style = null
    me.parts = null
    me.partTopo = null
    me.points = null
    me.type = null
    me.prjCoordSys = null
  }

    /*
     * APIMethod: toGeometry
     * 将服务端几何对象 ServerGeometry 转换为客户端几何对象 Geometry。
     *
     * Returns
     * {<SuperMap.Geometry>} 转换后的客户端几何对象。
     */
  toGeometry () {
    var me = this
    var geoType = me.type
    switch (geoType) {
      case GeometryType.POINT:
        return me.toGeoPoint()
      case GeometryType.LINE:
        return me.toGeoLine()
      case GeometryType.LINEM:
        return me.toGeoLinem()
      case GeometryType.REGION:
        return me.toGeoRegion()
      case GeometryType.POINTEPS:
        return me.toGeoPoint()
      case GeometryType.LINEEPS:
        return me.toGeoLineEPS()
      case GeometryType.REGIONEPS:
        return me.toGeoRegionEPS()
    }
  }

    /*
     * Method: toGeoPoint
     * 将服务端的点几何对象转换为客户端几何对象。
     * 包括 Point 、MultiPoint。
     *
     * Returns
     * {<SuperMap.Geometry>} 转换后的客户端几何对象。
     */
  toGeoPoint () {
    var me = this
    var geoParts = me.parts || []
    var geoPoints = me.points || []
    var len = geoParts.length
    if (len > 0) {
      if (len === 1) {
        return new Point(geoPoints[0].x, geoPoints[0].y)
      } else {
        var pointList = []
        for (let i = 0; i < len; i++) {
          pointList.push(new Point(geoPoints[i].x, geoPoints[i].y))
        }
        return new MultiPoint(pointList)
      }
    } else {
      return null
    }
  }

    /*
     * Method: toGeoLine
     * 将服务端的线几何对象转换为客户端几何对象。
     * 包括LinearRing、LineString、MultiLineString。
     *
     * Returns
     * {<SuperMap.Geometry>} 转换后的客户端几何对象。
     */
  toGeoLine () {
    var me = this
    var geoParts = me.parts || []
    var geoPoints = me.points || []
    var len = geoParts.length
    if (len > 0) {
      if (len === 1) {
        let pointList = []
        for (let i = 0; i < geoParts[0]; i++) {
          pointList.push(new Point(geoPoints[i].x, geoPoints[i].y))
        }
                // 判断线是否闭合，如果闭合，则返回LinearRing，否则返回LineString
        if (pointList[0].equals(pointList[geoParts[0] - 1])) {
          return new LinearRing(pointList)
        } else {
          return new LineString(pointList)
        }
      } else {
        let lineList = []
        for (let i = 0; i < len; i++) {
          let pointList = []
          for (let j = 0; j < geoParts[i]; j++) {
            pointList.push(new Point(geoPoints[j].x, geoPoints[j].y))
          }
          lineList.push(new LineString(pointList))
          geoPoints.splice(0, geoParts[i])
        }
        return new MultiLineString(lineList)
      }
    } else {
      return null
    }
  }

    /*
     * Method: toGeoLineEPS
     * 将服务端的线几何对象转换为客户端几何对象。
     * 包括LinearRing、LineString、MultiLineString。
     *
     * Returns
     * {<SuperMap.Geometry>} 转换后的客户端几何对象。
     */
  toGeoLineEPS () {
    var me = this
    var geoParts = me.parts || []
    var geoPoints = me.points || []
    var i
    var j
    var pointList
    var lineList
    var lineEPS
    var len = geoParts.length
    if (len > 0) {
      if (len === 1) {
        for (i = 0, pointList = []; i < geoParts[0]; i++) {
          pointList.push(new Point(geoPoints[i].x, geoPoints[i].y, geoPoints[i].type))
        }
                // 判断线是否闭合，如果闭合，则返回LinearRing，否则返回LineString
        if (pointList[0].equals(pointList[geoParts[0] - 1])) {
          lineEPS = LineString.createLineEPS(pointList)
          return new LinearRing(lineEPS)
        } else {
          lineEPS = LineString.createLineEPS(pointList)
          return new LineString(lineEPS)
        }
      } else {
        for (i = 0, lineList = []; i < len; i++) {
          for (j = 0, pointList = []; j < geoParts[i]; j++) {
            pointList.push(new Point(geoPoints[j].x, geoPoints[j].y))
          }
          lineEPS = LineString.createLineEPS(pointList)
          lineList.push(new LineString(lineEPS))
          geoPoints.splice(0, geoParts[i])
        }
        return new MultiLineString(lineList)
      }
    } else {
      return null
    }
  }

    /*
     * Method: toGeoLine
     * 将服务端的路由线几何对象转换为客户端几何对象。
     * 包括LinearRing、LineString、MultiLineString。
     *
     * Returns
     * {<SuperMap.Geometry>} 转换后的客户端几何对象。
     */
  toGeoLinem () {
    var me = this
    return new Route.fromJson(me)
  }

    /*
     * Method: toGeoRegion
     * 将服务端的面几何对象转换为客户端几何对象。
     * 类型为Polygon。
     *
     * Returns
     * {<SuperMap.Geometry>} 转换后的客户端几何对象。
     */
  toGeoRegion () {
    var me = this
    var geoParts = me.parts || []
    var geoTopo = me.partTopo || []
    var geoPoints = me.points || []
    var len = geoParts.length
    if (len <= 0) {
      return null
    }
    var polygonArray = []
    var pointList = []
    if (len === 1) {
      for (let i = 0; i < geoPoints.length; i++) {
        pointList.push(
                    new Point(geoPoints[i].x, geoPoints[i].y))
      }
      polygonArray.push(
                new Polygon(
                    [new LinearRing(pointList)]
                )
            )
      return new MultiPolygon(polygonArray)
    }
        // 处理复杂面
    var CCWArray = []
    var areaArray = []
    var polygonArrayTemp = []
        // polyon岛洞标识数组，初始都是岛。
    var CCWIdent = []
    for (let i = 0, pointIndex = 0; i < len; i++) {
      for (let j = 0; j < geoParts[i]; j++) {
        pointList.push(
                    new Point(geoPoints[pointIndex + j].x, geoPoints[pointIndex + j].y)
                )
      }
      pointIndex += geoParts[i]
      var polygon = new Polygon(
                [new LinearRing(pointList)]
            )
      pointList = []
      polygonArrayTemp.push(polygon)
      CCWIdent.push(1)
      areaArray.push(polygon.getArea())
    }
        // 根据面积排序
    ServerGeometry.bubbleSort(areaArray, polygonArrayTemp, geoTopo)
        // iServer 9D新增字段
    if (geoTopo.length === 0) {
            // 岛洞底层判断原则：将所有的子对象按照面积排序，面积最大的直接判定为岛（1），从面积次大的开始处理，
            // 如果发现该对象在某个面积大于它的对象之中（即被包含），则根据包含它的对象的标识（1 or -1），指定其标识（-1 or 1），
            // 依次处理完所有对象，就得到了一个标识数组，1表示岛，-1表示洞
            // 目标polygon索引列表 -1标示没有被任何polygon包含，
      var targetArray = []
      for (let i = 1; i < polygonArrayTemp.length; i++) {
        for (let j = i - 1; j >= 0; j--) {
          targetArray[i] = -1
          if (polygonArrayTemp[j].getBounds().containsBounds(polygonArrayTemp[i].getBounds())) {
            CCWIdent[i] = CCWIdent[j] * -1
            if (CCWIdent[i] < 0) {
              targetArray[i] = j
            }
            break
          }
        }
      }
      for (let i = 0; i < polygonArrayTemp.length; i++) {
        if (CCWIdent[i] > 0) {
          polygonArray.push(polygonArrayTemp[i])
        } else {
          polygonArray[targetArray[i]].components = polygonArray[targetArray[i]].components.concat(polygonArrayTemp[i].components)
                    // 占位
          polygonArray.push('')
        }
      }
    } else {
            // 根据面积排序
            // ServerGeometry.bubbleSort(areaArray, polygonArrayTemp,geoTopo)
      polygonArray = []
      for (let i = 0; i < polygonArrayTemp.length; i++) {
        if (geoTopo[i] && geoTopo[i] === -1) {
          CCWArray = CCWArray.concat(polygonArrayTemp[i].components)
        } else {
          if (CCWArray.length > 0 && polygonArray.length > 0) {
            polygonArray[polygonArray.length - 1].components = polygonArray[polygonArray.length - 1].components.concat(CCWArray)
            CCWArray = []
          }
          polygonArray.push(
                        polygonArrayTemp[i]
                    )
        }
        if (i === len - 1) {
          var polyLength = polygonArray.length
          if (polyLength) {
            polygonArray[polyLength - 1].components = polygonArray[polyLength - 1].components.concat(CCWArray)
          } else {
            for (let k = 0, length = CCWArray.length; k < length; k++) {
              polygonArray.push(
                                new Polygon(CCWArray)
                            )
            }
          }
        }
      }
    }
    return new MultiPolygon(polygonArray)
  }

    /*
     * Method: toGeoRegionEPS
     * 将服务端的面几何对象转换为客户端几何对象。
     * 类型为Polygon。
     *
     * Returns
     * {<SuperMap.Geometry>} 转换后的客户端几何对象。
     */
  toGeoRegionEPS () {
    var me = this
    var geoParts = me.parts || []
    var geoTopo = me.partTopo || []
    var geoPoints = me.points || []
    var len = geoParts.length

    if (len <= 0) {
      return null
    }
    var polygonArray = []
    var pointList = []
    var lineEPS
    if (len === 1) {
      for (var i = 0; i < geoPoints.length; i++) {
        pointList.push(
                    new Point(geoPoints[i].x, geoPoints[i].y))
      }

      lineEPS = LineString.createLineEPS(pointList)
      polygonArray.push(
                new Polygon(
                    [new LinearRing(lineEPS)]
                )
            )
      return new MultiPolygon(polygonArray)
    }
        // 处理复杂面
    var CCWArray = []
    var areaArray = []
    var polygonArrayTemp = []
        // polyon岛洞标识数组，初始都是岛。
    var CCWIdent = []
    for (let i = 0, pointIndex = 0; i < len; i++) {
      for (let j = 0; j < geoParts[i]; j++) {
        pointList.push(
                    new Point(geoPoints[pointIndex + j].x, geoPoints[pointIndex + j].y)
                )
      }
      pointIndex += geoParts[i]

      lineEPS = LineString.createLineEPS(pointList)
      var polygon = new Polygon(
                [new LinearRing(lineEPS)]
            )
      pointList = []
      polygonArrayTemp.push(polygon)
      CCWIdent.push(1)
      areaArray.push(polygon.getArea())
    }
        // 根据面积排序
    ServerGeometry.bubbleSort(areaArray, polygonArrayTemp, geoTopo)
        // iServer 9D新增字段
    if (geoTopo.length === 0) {
            // 岛洞底层判断原则：将所有的子对象按照面积排序，面积最大的直接判定为岛（1），从面积次大的开始处理，
            // 如果发现该对象在某个面积大于它的对象之中（即被包含），则根据包含它的对象的标识（1 or -1），指定其标识（-1 or 1），
            // 依次处理完所有对象，就得到了一个标识数组，1表示岛，-1表示洞
            // 目标polygon索引列表 -1标示没有被任何polygon包含，
      var targetArray = []
      for (let i = 1; i < polygonArrayTemp.length; i++) {
        for (let j = i - 1; j >= 0; j--) {
          targetArray[i] = -1
          if (polygonArrayTemp[j].getBounds().containsBounds(polygonArrayTemp[i].getBounds())) {
            CCWIdent[i] = CCWIdent[j] * -1
            if (CCWIdent[i] < 0) {
              targetArray[i] = j
            }
            break
          }
        }
      }
      for (let i = 0; i < polygonArrayTemp.length; i++) {
        if (CCWIdent[i] > 0) {
          polygonArray.push(polygonArrayTemp[i])
        } else {
          polygonArray[targetArray[i]].components = polygonArray[targetArray[i]].components.concat(polygonArrayTemp[i].components)
                    // 占位
          polygonArray.push('')
        }
      }
    } else {
            // 根据面积排序
      polygonArray = []
      for (let i = 0; i < polygonArrayTemp.length; i++) {
        if (geoTopo[i] && geoTopo[i] === -1) {
          CCWArray = CCWArray.concat(polygonArrayTemp[i].components)
        } else {
          if (CCWArray.length > 0 && polygonArray.length > 0) {
            polygonArray[polygonArray.length - 1].components = polygonArray[polygonArray.length - 1].components.concat(CCWArray)
            CCWArray = []
          }
          polygonArray.push(
                        polygonArrayTemp[i]
                    )
        }
        if (i === len - 1) {
          var polyLength = polygonArray.length
          if (polyLength) {
            polygonArray[polyLength - 1].components = polygonArray[polyLength - 1].components.concat(CCWArray)
          } else {
            for (let k = 0, length = CCWArray.length; k < length; k++) {
              polygonArray.push(
                                new Polygon(CCWArray)
                            )
            }
          }
        }
      }
    }
    return new MultiPolygon(polygonArray)
  }

    /*
     * Function: ServerGeometry.fromJson
     * 将 JSON 对象表示服务端几何对象转换为 ServerGeometry。
     *
     * Parameters:
     * jsonObject - {Object} 要转换的 JSON 对象。
     *
     * Returns:
     * {<SuperMap.ServerGeometry>} 转化后的 ServerGeometry 对象。
     */
  static fromJson (jsonObject) {
    if (!jsonObject) {
      return
    }
    return new ServerGeometry({
      id: jsonObject.id,
      style: ServerStyle.fromJson(jsonObject.style),
      parts: jsonObject.parts,
      partTopo: jsonObject.partTopo,
      points: jsonObject.points,
      center: jsonObject.center,
      length: jsonObject.length,
      maxM: jsonObject.maxM,
      minM: jsonObject.minM,
      type: jsonObject.type
    })
  }

    /*
     * Function: ServerGeometry.fromGeometry
     * 将客户端Geometry转换成服务端ServerGeometry。
     *
     * Parameters:
     * geometry - {<SuperMap.Geometry>} 要转换的客户端Geometry对象。

     *
     * Returns:
     * {<SuperMap.ServerGeometry>} 转化后的 ServerGeometry 对象。
     */
  static fromGeometry (geometry) {
    if (!geometry) {
      return
    }
    var id = 0
    var parts = []
    var points = []
    var type = null
    var icomponents = geometry.components
    var className = geometry.CLASS_NAME
    var prjCoordSys = {'epsgCode': geometry.SRID}

    if (!isNaN(geometry.id)) {
      id = geometry.id
    }
        // 坑爹的改法，没法，为了支持态势标绘，有时间就得全改
    if (className !== 'SuperMap.Geometry.LinearRing' && className !== 'SuperMap.Geometry.LineString' && (geometry instanceof MultiPoint || geometry instanceof MultiLineString)) {
      let ilen = icomponents.length
      for (let i = 0; i < ilen; i++) {
        let partPointsCount = icomponents[i].getVertices().length
        parts.push(partPointsCount)
        for (let j = 0; j < partPointsCount; j++) {
          points.push(new Point(icomponents[i].getVertices()[j].x, icomponents[i].getVertices()[j].y))
        }
      }
            // 这里className不是多点就全部是算线
      type = (className === 'SuperMap.Geometry.MultiPoint') ? GeometryType.POINT : GeometryType.LINE
    } else if (geometry instanceof MultiPolygon) {
      let ilen = icomponents.length
      for (let i = 0; i < ilen; i++) {
        let polygon = icomponents[i]
        let linearRingOfPolygon = polygon.components
        let linearRingOfPolygonLen = linearRingOfPolygon.length
        for (let j = 0; j < linearRingOfPolygonLen; j++) {
          let partPointsCount = linearRingOfPolygon[j].getVertices().length + 1
          parts.push(partPointsCount)
          for (let k = 0; k < partPointsCount - 1; k++) {
            points.push(new Point(linearRingOfPolygon[j].getVertices()[k].x, linearRingOfPolygon[j].getVertices()[k].y))
          }
          points.push(new Point(linearRingOfPolygon[j].getVertices()[0].x, linearRingOfPolygon[j].getVertices()[0].y))
        }
      }
      type = GeometryType.REGION
    } else if (geometry instanceof Polygon) {
      let ilen = icomponents.length
      for (let i = 0; i < ilen; i++) {
        let partPointsCount = icomponents[i].getVertices().length + 1
        parts.push(partPointsCount)
        for (let j = 0; j < partPointsCount - 1; j++) {
          points.push(new Point(icomponents[i].getVertices()[j].x, icomponents[i].getVertices()[j].y))
        }
        points.push(new Point(icomponents[i].getVertices()[0].x, icomponents[i].getVertices()[0].y))
      }
      type = GeometryType.REGION
    } else {
      let geometryVerticesCount = geometry.getVertices().length
      for (let j = 0; j < geometryVerticesCount; j++) {
        points.push(new Point(geometry.getVertices()[j].x, geometry.getVertices()[j].y))
      }
      if (geometry instanceof LinearRing) {
        points.push(new Point(geometry.getVertices()[0].x, geometry.getVertices()[0].y))
        geometryVerticesCount++
      }
      parts.push(geometryVerticesCount)
      type = (geometry instanceof Point) ? GeometryType.POINT : GeometryType.LINE
    }

    return new ServerGeometry({
      id: id,
      style: null,
      parts: parts,
      points: points,
      type: type,
      prjCoordSys: prjCoordSys
    })
  }

    /*
     * Function: SuperMap.Geometry.IsClockWise
     * 判断linearRing中的点的顺序，顺时针？逆时针
     * 返回值大于0, 逆时针 小于0, 顺时针
     *
     * Parameters:
     * geometry - {<SuperMap.Geometry>} 要转换的客户端Geometry对象。

     *
     * Returns:
     * {<Number>}。
     */
  static IsClockWise (points) {
    var length = points.length
    if (length < 3) {
      return 0.0
    }
    var s = points[0].y * (points[length - 1].x - points[1].x)
    points.push(points[0])
    for (var i = 1; i < length; i++) {
      s += points[i].y * (points[i - 1].x - points[i + 1].x)
    }
    return s * 0.5
  }

  static bubbleSort (areaArray, pointList, geoTopo) {
    for (var i = 0; i < areaArray.length; i++) {
      for (var j = 0; j < areaArray.length; j++) {
        if (areaArray[i] > areaArray[j]) {
          var d = areaArray[j]
          areaArray[j] = areaArray[i]
          areaArray[i] = d
          var b = pointList[j]
          pointList[j] = pointList[i]
          pointList[i] = b
          if (geoTopo && geoTopo.length > 0) {
            var c = geoTopo[j]
            geoTopo[j] = geoTopo[i]
            geoTopo[i] = c
          }
        }
      }
    }
  }
}

SuperMap.ServerGeometry = ServerGeometry
