import {GeomConstants} from '../../math/geometry/geomConstants'
import {LinearSystem2} from '../../math/geometry/linearSystem'
import {Point, TriangleOrientation} from '../../math/geometry/point'
import {PolylinePoint} from '../../math/geometry/polylinePoint'
import {Assert} from '../../utils/assert'

export class ActiveEdgeComparerWithRay {
  pivot: Point

  get Pivot() {
    return this.pivot
  }
  set Pivot(value: Point) {
    this.pivot = value
  }

  pointOnTheRay: Point

  get IntersectionOfTheRayAndInsertedEdge() {
    return this.pointOnTheRay
  }
  set IntersectionOfTheRayAndInsertedEdge(value: Point) {
    this.pointOnTheRay = value
  }

  Compare(x: PolylinePoint, y: PolylinePoint) {
    Assert.assert(this.IntersectionPointBelongsToTheInsertedEdge(x))

    switch (Point.getTriangleOrientation(this.IntersectionOfTheRayAndInsertedEdge, y.point, y.nextOnPolyline.point)) {
      case TriangleOrientation.Counterclockwise:
        return -1
      default:
        return 1
    }
  }

  IntersectionPointBelongsToTheInsertedEdge(x: PolylinePoint): boolean {
    const a = x.point.sub(this.IntersectionOfTheRayAndInsertedEdge)
    const b = x.nextOnPolyline.point.sub(this.IntersectionOfTheRayAndInsertedEdge)
    return Math.abs(a.x * b.y - b.x * a.y) < GeomConstants.distanceEpsilon
  }

  IntersectEdgeWithRayPPP(source: Point, target: Point, ray: Point): Point {
    //let x(t-s)+s is on the ray, then for some y we x(t-s)+s=y*ray+pivot, or x(t-s)-y*ray=pivot-s
    const result = LinearSystem2.solve(
      target.x - source.x,
      -ray.x,
      this.Pivot.x - source.x,
      target.y - source.y,
      -ray.y,
      this.Pivot.y - source.y,
    )
    if (!(-GeomConstants.tolerance <= result.x && result.x <= 1 + GeomConstants.tolerance)) throw new Error()
    if (!result) throw new Error()

    return this.Pivot.add(ray.mul(result.y))
  }

  IntersectEdgeWithRay(side: PolylinePoint, ray: Point): Point {
    return this.IntersectEdgeWithRayPPP(side.point, side.nextOnPolyline.point, ray)
  }

  static constructorPP(pivot: Point, pointOnTheRay: Point) {
    const r = new ActiveEdgeComparerWithRay()
    r.pivot = pivot
    r.pointOnTheRay = pointOnTheRay
    return r
  }
}
