    import * as turf from '@turf/turf'
    import {
      Point,
      LineString,
      MultiLineString,
      SpatialReference
    } from '../../base/geometry'
    import GeometryEngine from '../../base/geometry/GeometryEngine'
    import { Projection } from '../../base'
    import { defaultValue } from '../../util'
    /**
     * 草图捕捉工具类
     * @class SketchPolygonDrawTool
     * @moduleEX SketchEditorModule
     * @param {Object} options 构造参数
     * @param {Object}  [options.pixelTolerance]  容差（像素单位）
     * @param {Object}  [options.tolerance]  容差（米单位）
     * @param {Object}  [options.isSnappingVertex]  是否自动捕捉顶点
     * @param {Object}  [options.isSnappingMidVertex]  是否自动捕捉中点
     * @param {Object}  [options.isSnappingVertexInLine]  是否自动捕捉线上的点
     * @param {Object}  [options.isSnappingPerpendicular]  是否自动捕捉垂点
     * @param {Object}  [options.isSnappingParallel]  是否自动捕捉水平交点
     */

    class SketchSnappingTool {
      constructor(options) {
        /**
         *像素容差
         */
        this.pixelTolerance = 10
        /**
         *容差（米单位）
         */
        this.tolerance = 5
        /**
         *斜率容差
         */
        this.toleranceSlope = 0.02
        /**
         *是否自动捕捉顶点
         */
        this.isSnappingVertex = false
        /**
         *是否自动捕捉中点
         */
        this.isSnappingMidVertex = false
        /**
         *是否自动捕捉顶点重合
         */
        this.isSnappingVertexCoincident = defaultValue(
          options.isSnappingVertexCoincident,
          false
        )
        /**
         *是否自动捕捉线上的点
         */
        this.isSnappingVertexInLine = defaultValue(
          options.isSnappingVertexInLine,
          false
        )
        /**
         *是否自动捕捉垂线垂点
         */
        this.isSnappingPerpendicular = defaultValue(
          options.isSnappingPerpendicular,
          false
        )
        /**
         *是否自动捕捉平行线
         */
        this.isSnappingParallel = defaultValue(options.isSnappingParallel, false)

        this.turf = turf
      }

      /**
       * 绘制时捕捉
       * @param {Point} targetPoint 捕获结果
       * @param {LineString} targetLine 捕获结果
       * @param {Array} pointArray 被捕获的面端点数组
       * @param {Array} lineSlopes 被捕获的面线段斜率
       * @return {Object} 捕获结果
       */
      test() {
        // const res2 = this.turf.bearing(
        //   this.turf.point([0, 10]),
        //   this.turf.point([10, 10])
        // )
        // const res2_1 = this.turf.rhumbBearing(
        //   this.turf.point([0, 10]),
        //   this.turf.point([10, 10])
        // )
      }

      drawingSnap(targetPoint, targetLine, pointArray, lineSlopes, tolerance) {
        this.tolerance = tolerance
        const spatialReference = targetLine.spatialReference
        targetPoint = this._convertReference(targetPoint)
        targetLine = this._convertReference(targetLine)
        const snapGeometry = this.getSnapGeometryByPointArray(
          pointArray,
          spatialReference
        )
        const snapResult = {}
        if (this.isSnappingVertexCoincident) {
          // 捕捉顶点重合
          snapResult.coincidentPoint = this.pointCoincident(
            targetPoint,
            snapGeometry.points,
            spatialReference
          )
        }
        if (this.isSnappingVertexInLine) {
          // 捕捉点在线上
          snapResult.pointInLine = this.getPointInLines(
            targetPoint,
            snapGeometry.lines,
            spatialReference
          )
        }
        if (this.isSnappingParallel) {
          // 捕捉平行线
          snapResult.parallelLines = this.getParallelLine(
            targetPoint,
            targetLine,
            snapGeometry.lines,
            lineSlopes,
            spatialReference
          )
        }
        if (this.isSnappingPerpendicular) {
          // 捕捉垂线垂点
          snapResult.perpendicularLines = this.getPerpendicularLine(
            targetPoint,
            targetLine,
            snapGeometry.lines,
            lineSlopes,
            spatialReference
          )
        }
        return snapResult
      }

      getSnapGeometryByPointArray(pointArray, spatialReference) {
        const points = []
        const lines = []
        pointArray.forEach((coord, index) => {
          const point = this._convertReference(
            new Point({
              coordinates: coord,
              spatialReference
            })
          )
          points.push(point)
          if (index > 0) {
            const line = this._convertReference(
              new LineString({
                coordinates: [pointArray[index - 1], pointArray[index]],
                spatialReference
              })
            )
            lines.push(line)
          }
        })
        return { points, lines }
      }

      _convertReference(geometry) {
        if (!geometry.isWSG48) {
          let newGeometry = geometry.clone()
          newGeometry = Projection.project(
            newGeometry,
            new SpatialReference('EPSG:4326')
          )
          return newGeometry
        } else {
          return geometry
        }
      }

      /**
       *非绘制状态时草图捕捉
       */
      featureSnap() {}

      /**
       *两点是否重合
       */
      pointCoincident1(targetPoint, points) {
        const bufferedPolygon = GeometryEngine.buffer(targetPoint, this.tolerance)
        const containPoints = []
        points.forEach((point) => {
          if (point) {
            if (GeometryEngine.contains(bufferedPolygon, point)) {
              containPoints.push(point)
            }
          }
        })
        return containPoints
      }

      getDistance(point1, point2) {
        const distance = turf.distance(turf.point(point1), turf.point(point2), {
          units: 'kilometers'
        })
        return distance * 1000
      }

      /**
       * 两点是否重合
       * @param {Point} targetPoint
       * @param {Array} points
       */
      pointCoincident(targetPoint, points, spatialReference) {
        let containPoint = null
        for (let i = 0; i < points.length; i++) {
          if (
            GeometryEngine.distance(targetPoint, points[i]) * 1000 <=
            this.tolerance
          ) {
            containPoint = Projection.project(points[i], spatialReference)
            break
          }
        }
        return containPoint
      }

      /**
       *捕获目标点所在在多个被捕获的直线上，返回被捕获的的直线
       * @param {Point} targetPoint
       * @param {Array} lines
       * @return {Object} 被捕获的的直线对象
       */
      getPointInLines(targetPoint, lines, spatialReference) {
        const containLines = []
        lines.forEach((line) => {
          if (line) {
            const distance = GeometryEngine.distance(targetPoint, line)
            if (distance * 1000 <= this.tolerance) {
              // 计算容差范围内点到线的垂点
              const getPerpendicularPoint = (
                centerCoordinate,
                lineCoordinate1,
                lineCoordinate2
              ) => {
                // 计算线段的方向（以度为单位，逆时针方向为正方向）
                const direction1 =
                  -90 + turf.rhumbBearing(lineCoordinate1, lineCoordinate2)
                const slope =
                  (lineCoordinate2[1] - lineCoordinate1[1]) /
                  (lineCoordinate2[0] - lineCoordinate1[0])
                const point1 = turf.rhumbDestination(
                  centerCoordinate,
                  distance,
                  direction1
                )
                const slope1 =
                  (point1.geometry.coordinates[1] - lineCoordinate1[1]) /
                  (point1.geometry.coordinates[0] - lineCoordinate1[0])
                const direction2 =
                  90 + turf.rhumbBearing(lineCoordinate1, lineCoordinate2)
                const point2 = turf.rhumbDestination(
                  centerCoordinate,
                  distance,
                  direction2
                )
                const slope2 =
                  (point2.geometry.coordinates[1] - lineCoordinate1[1]) /
                  (point2.geometry.coordinates[0] - lineCoordinate1[0])
                const resultPoint =
                  Math.abs(slope1 - slope) < Math.abs(slope2 - slope)
                    ? point1
                    : point2
                return resultPoint
              }
              const perpendicularPoint = getPerpendicularPoint(
                targetPoint.coordinates,
                line.coordinates[0],
                line.coordinates[1]
              )

              line = Projection.project(line, spatialReference)
              const realPoint = Projection.project(
                new Point({
                  coordinates: perpendicularPoint.geometry.coordinates
                }),
                spatialReference
              )
              containLines.push({
                line,
                realPoint,
                toleranceDistance: distance
              })
            }
          }
        })
        let minDistance = this.tolerance
        let containLine = null
        const intersectLines = []
        // 如果点整好在（多个）线上(没有容差)，则需要返回多个线的数据。否则只返回距离最近的线
        containLines.forEach((line) => {
          if (line.toleranceDistance < minDistance) {
            minDistance = line.toleranceDistance
            containLine = line
          }
          if (line.toleranceDistance === 0) {
            intersectLines.push(line.line)
          }
        })
        if (containLine) {
          containLine.line =
            intersectLines.length > 0 ? intersectLines : [containLine.line]
        }
        return containLine
      }

      /**
       * 获取目标直线的平行线捕获结果。判断目标直线与被捕获的直线是否平行，返回平行线结果
       * @private
       * @param {Point} targetPoint
       * @param {LineString} targetLine 目标直线
       * @param {Array} lines 被捕获的直线对象
       * @param {Array} lineSlopes 被捕获的直线斜率
       * @return {Object} 平行线捕获结果
       */
      // targetPoint目标终点，targetLine目标线段，lines被比较的直线，lineSlopes被比较的直线斜率
      getParallelLine(
        targetPoint,
        targetLine,
        lines,
        lineSlopes,
        spatialReference
      ) {
        const parallelLines = []
        const angle = turf.rhumbBearing(
          turf.point(targetLine.coordinates[0]),
          turf.point(targetLine.coordinates[1])
        )
        const distance =
          turf.distance(
            turf.point(targetLine.coordinates[0]),
            turf.point(targetLine.coordinates[1])
          ) * 1000
        // const toleranceSlope = distance / this.tolerance
        const toleranceAngle =
          (Math.asin(this.tolerance / distance) * 180) / Math.PI
        let parallelPoint = undefined
        if (lineSlopes && lineSlopes.length === lines.length) {
          lineSlopes.forEach((slope, i) => {
            // 如果两线相交，则不平行，直接返回
            const intersect = GeometryEngine.intersects(targetLine, lines[i])
            if (intersect) {
              return
            }
            // 判断斜率是否在容差范围内
            let lineAngle = turf.rhumbBearing(
              turf.point(lines[i].coordinates[0]),
              turf.point(lines[i].coordinates[1])
            )
            let deltaAngle
            if (angle * lineAngle > 0) {
              // 两线方向相同
              deltaAngle = Math.abs(lineAngle - angle)
            } else {
              // 两线方向不相同,则把被比较线的方向变为同向
              if (angle < 0) {
                lineAngle -= 180
              } else {
                lineAngle += 180
              }
              deltaAngle = Math.abs(angle - lineAngle)
            }
            if (deltaAngle <= toleranceAngle) {
              parallelPoint =
                parallelPoint ||
                turf.rhumbDestination(
                  turf.point(targetLine.coordinates[0]),
                  distance / 1000,
                  lineAngle
                )
              const realPoint = Projection.project(
                new Point({
                  coordinates: parallelPoint.geometry.coordinates
                }),
                spatialReference
              )
              parallelLines.push({
                line: lines[i],
                symbolGeometry: this._getParallelSymbolGeometry(
                  new LineString({
                    coordinates: [
                      targetLine.coordinates[0],
                      parallelPoint.geometry.coordinates
                    ],
                    spatialReference
                  }),
                  lines[i]
                ),
                realPoint
              })
            }
          })
        }
        return parallelLines
      }

      /**
       * 获取描述平行线的特殊符号
       * @private
       * @param {LineString} targetLine
       * @param {Array} lines 被捕获的直线对象
       * @return {Object} 平行线的特殊符号对象
       */
      _getParallelSymbolGeometry(targetLine, line) {
        const scale = 1 / 4
        const pan = this.tolerance / 1000
        const translatedLine = function (line, scale, pan) {
          const polyline = turf.lineString(line.coordinates)
          const translatedPolyline = turf.transformTranslate(polyline, pan, 180)
          const scaledLine = turf.transformScale(translatedPolyline, scale)
          return scaledLine.geometry.coordinates
        }
        return new MultiLineString({
          coordinates: [
            translatedLine(targetLine, scale, pan),
            translatedLine(targetLine, scale, -pan),
            translatedLine(line, scale, pan),
            translatedLine(line, scale, -pan)
          ]
        })
      }

      /**
       * 获取目标直线的垂线捕获结果
       * @private
       * @param {Point} targetPoint
       * @param {LineString} targetLine
       * @param {Array} lines 被捕获的直线对象
       * @param {Array} lineSlopes 被捕获的直线斜率
       * @return {Object} 垂线结果
       */
      getPerpendicularLine0(targetPoint, targetLine, lines, lineSlopes) {
        const perpendicularLines = []
        let perpendicularPoint = null
        let symbolGeometry = []
        const targetLineSlope =
          (targetLine.coordinates[0][1] - targetLine.coordinates[1][1]) /
          (targetLine.coordinates[0][0] - targetLine.coordinates[1][0])

        if (lineSlopes && lineSlopes.length === lines.length) {
          lineSlopes.forEach((slope, i) => {
            const distance = GeometryEngine.distance(targetPoint, lines[i]) * 1000
            const deltaAngle = Math.atan(this.tolerance / distance)
            const lineAngle = Math.atan(slope)
            const maxA = lineAngle + deltaAngle
            const minA = lineAngle - deltaAngle
            if (
              targetLineSlope <= Math.tan(Math.PI / 2 + maxA) &&
              targetLineSlope >= Math.tan(Math.PI / 2 + minA)
            ) {
              const intersect = GeometryEngine.intersect(lines[i], targetLine)
              if (!intersect.point) {
                perpendicularPoint = this._getPerpendicularPoint(
                  targetPoint,
                  lines[i],
                  slope
                )
                perpendicularPoint = new Point({
                  coordinates: perpendicularPoint
                })
                symbolGeometry = this.getPerpendicularSymbolGeometry(
                  targetLine,
                  lines[i],
                  perpendicularPoint
                )
              } else {
                perpendicularPoint = intersect.point

                symbolGeometry = this.getPerpendicularSymbolGeometry2(
                  targetLine,
                  lines[i],
                  perpendicularPoint
                )
              }
              perpendicularLines.push({
                line: lines[i],
                perpendicularPoint,
                targetLine,
                symbolGeometry
              })
            }
          })
        }
        return perpendicularLines
      }

      getPerpendicularLine(
        targetPoint,
        targetLine,
        lines,
        lineSlopes,
        spatialReference
      ) {
        const perpendicularLines = []
        const angle = turf.rhumbBearing(
          turf.point(targetLine.coordinates[0]),
          turf.point(targetLine.coordinates[1])
        )
        const distance =
          turf.distance(
            turf.point(targetLine.coordinates[0]),
            turf.point(targetLine.coordinates[1])
          ) * 1000
        const toleranceAngle =
          (Math.asin(this.tolerance / distance) * 180) / Math.PI
        let perpendicularPoint = undefined
        lines.forEach((line, i) => {
          // // 如果两线不相交，则不垂直，直接返回
          // const intersect = GeometryEngine.intersects(targetLine, lines[i])
          // if (!intersect) {
          //   return
          // }
          // 判断斜率是否在容差范围内
          let lineAngle = turf.rhumbBearing(
            turf.point(lines[i].coordinates[0]),
            turf.point(lines[i].coordinates[1])
          )
          let deltaAngle
          if (angle * lineAngle > 0) {
            // 两线方向相同
            deltaAngle = Math.abs(lineAngle - angle)
          } else {
            // 两线方向不相同,则把被比较线的方向变为同向
            if (angle < 0) {
              lineAngle -= 180
            } else {
              lineAngle += 180
            }
            deltaAngle = Math.abs(angle - lineAngle)
          }
          if (Math.abs(90 - deltaAngle) <= toleranceAngle) {
            const aimAngle = lineAngle < angle ? lineAngle + 90 : lineAngle - 90
            let realPoint = turf.rhumbDestination(
              turf.point(targetLine.coordinates[0]),
              distance / 1000,
              aimAngle
            )
            realPoint = Projection.project(
              new Point({
                coordinates: realPoint.geometry.coordinates
              }),
              spatialReference
            )
            // 求垂足坐标
            const pDistance = turf.pointToLineDistance(
              turf.point(targetLine.coordinates[0]),
              lines[i]
            )
            perpendicularPoint = turf.rhumbDestination(
              turf.point(targetLine.coordinates[0]),
              pDistance,
              aimAngle
            )
            perpendicularPoint = new Point({
              coordinates: perpendicularPoint.geometry.coordinates,
              spatialReference
            })
            const symbolGeometry = undefined
            // 暂时屏蔽计算垂足图形方法，方法需进一步修复
            // const symbolGeometry = this.getPerpendicularSymbolGeometry(
            //   new LineString({
            //     coordinates: [targetLine.coordinates[0], realPoint.coordinates]，
            //     spatialReference
            //   }),
            //   lines[i],
            //   perpendicularPoint
            // )
            perpendicularLines.push({
              line: lines[i],
              realPoint,
              symbolGeometry
            })
          }
        })
        return perpendicularLines
      }

      /**
       * 获取目标点到目标线的垂点对象
       * @private
       * @param {Point} targetPoint 目标点
       * @param {LineString} targetLine 目标线
       * @param {Number} lineSlope 直线斜率
       * @return {Array} 垂点坐标
       */
      _getPerpendicularPoint(targetPoint, targetLine, lineSlope) {
        const delta1 =
          targetLine.coordinates[0][1] - lineSlope * targetLine.coordinates[0][0]
        const delta2 =
          targetPoint.coordinates[1] + (1 / lineSlope) * targetPoint.coordinates[0]
        const x = ((delta2 - delta1) * lineSlope) / (lineSlope * lineSlope + 1)
        const y = (-1 / lineSlope) * x + delta2
        return [x, y]
      }

      /**
       * 获取描述垂线的特殊符号
       * @private
       * @param {LineString} targetLine
       * @param {LineString} line 被捕获的直线对象
       * @param {Point} perpendicularPoint 垂足坐标点
       * @return {Object} 垂线的特殊符号对象
       */
      getPerpendicularSymbolGeometry(targetLine, line, perpendicularPoint) {
        const linePoints1 = [
          turf.point(targetLine.coordinates[0]),
          turf.point(targetLine.coordinates[1])
        ]
        const linePoints2 = [
          turf.point(line.coordinates[0]),
          turf.point(line.coordinates[1])
        ]
        const distance1 = turf.distance(linePoints1[0], linePoints1[1]) // GeometryEngine.geodesicLength(targetLine)
        const distance2 = turf.distance(linePoints2[0], linePoints2[1]) // GeometryEngine.geodesicLength(line)
        const distance = Math.min(distance1, distance2) / 8
        perpendicularPoint = turf.point(perpendicularPoint.coordinates)
        let bearing2 = 0
        if (
          linePoints2[0].geometry.coordinates[0] ===
            perpendicularPoint.geometry.coordinates[0] &&
          linePoints2[0].geometry.coordinates[1] ===
            perpendicularPoint.geometry.coordinates[1]
        ) {
          bearing2 = turf.rhumbBearing(perpendicularPoint, linePoints2[1])
        } else {
          bearing2 = turf.rhumbBearing(perpendicularPoint, linePoints2[0])
        }
        let bearing1 = 0
        if (
          linePoints1[0].geometry.coordinates[0] ===
            perpendicularPoint.geometry.coordinates[0] &&
          linePoints1[0].geometry.coordinates[1] ===
            perpendicularPoint.geometry.coordinates[1]
        ) {
          bearing1 = turf.rhumbBearing(perpendicularPoint, linePoints1[1])
        } else {
          bearing1 = turf.rhumbBearing(perpendicularPoint, linePoints1[0])
        }
        const bearing3 = (bearing1 + bearing2) / 2
        const destination1 = turf.rhumbDestination(
          perpendicularPoint,
          distance,
          bearing1
        )
        const destination2 = turf.rhumbDestination(
          perpendicularPoint,
          distance,
          bearing2
        )
        const destination3 = turf.rhumbDestination(
          perpendicularPoint,
          distance * Math.sqrt(2),
          bearing3
        )
        const point1 = destination1.geometry.coordinates
        const point2 = destination2.geometry.coordinates
        const point3 = destination3.geometry.coordinates
        return new LineString({
          coordinates: [point1, point3, point2]
        })
      }

      /**
       * 正在测试中的方法
       * @private
       */
      getPerpendicularSymbolGeometry2(targetLine, line, perpendicularPoint) {
        let p1 = turf.point(targetLine.coordinates[0])
        if (
          targetLine.coordinates[0][0] === perpendicularPoint.coordinates[0] &&
          targetLine.coordinates[0][1] === perpendicularPoint.coordinates[1]
        ) {
          p1 = turf.point(targetLine.coordinates[1])
        }
        const p2 = turf.point(line.coordinates[0])
        const p3 = turf.point(line.coordinates[1])
        const distance1 = turf.distance(p1, perpendicularPoint)
        const distance2 = turf.distance(p2, perpendicularPoint)
        const distance3 = turf.distance(p3, perpendicularPoint)
        let distance = Math.min(distance1, distance2, distance3) / 8
        if (distance2 === 0) {
          distance = Math.min(distance1, distance3) / 8
        } else if (distance3 === 0) {
          distance = Math.min(distance1, distance2) / 8
        }
        perpendicularPoint = turf.point(perpendicularPoint.coordinates)

        const bearing1 = turf.rhumbBearing(perpendicularPoint, p1)
        const destination1 = turf.rhumbDestination(
          perpendicularPoint,
          distance,
          bearing1
        )
        const bearing2 = turf.rhumbBearing(perpendicularPoint, p2)
        const destination2 = turf.rhumbDestination(
          perpendicularPoint,
          distance,
          bearing2
        )
        const bearing3 = (bearing1 + bearing2) / 2
        const dis = distance * Math.cos(((bearing3 - bearing2) * Math.PI) / 180) * 2
        const destination3 = turf.rhumbDestination(
          perpendicularPoint,
          dis,
          bearing3
        )

        const point1 = destination1.geometry.coordinates
        const point2 = destination2.geometry.coordinates
        const point3 = destination3.geometry.coordinates
        return new LineString({
          coordinates: [point1, point3, point2]
        })
      }

      /**
       *根据垂足获取真正终点位置
       * @private
       * @param {Feature} currentPoint 当前终点
       * @param {Feature} startPoint 线段起点
       * @param {Feature} perpendicularPoint 垂点位置
       */
      getPerpendicularRealPosition(currentPoint, startPoint, perpendicularPoint) {
        let realPosition = null
        const distance = GeometryEngine.distance(startPoint, currentPoint)
        const direction = turf.rhumbBearing(
          turf.point(startPoint.coordinates),
          turf.point(perpendicularPoint.currentPoint)
        )
        realPosition = turf.rhumbDestination(startPoint, distance, direction)
          .geometry.coordinates
        return realPosition
      }

      /**
       *面和线是否相交
       */
      isPolylineCrossPolygon() {}

      /**
       *两个面是否相交
       */
      isPolygonCross() {}
    }
    export default SketchSnappingTool