import NotRepresentableException from './NotRepresentableException'
import CGAlgorithms from './CGAlgorithms'
import Coordinate from '../geom/Coordinate'
import CGAlgorithmsDD from './CGAlgorithmsDD'
import System from '../../../../java/lang/System'
import HCoordinate from './HCoordinate'
import Envelope from '../geom/Envelope'
import LineIntersector from './LineIntersector'

export default class RobustLineIntersector extends LineIntersector {
  isInSegmentEnvelopes (intPt) {
    const env0 = new Envelope(this._inputLines[0][0], this._inputLines[0][1])
    const env1 = new Envelope(this._inputLines[1][0], this._inputLines[1][1])
    return env0.contains(intPt) && env1.contains(intPt)
  }
  computeIntersection () {
    if (arguments.length === 3) {
      const p = arguments[0]
      const p1 = arguments[1]
      const p2 = arguments[2]
      this._isProper = false
      if (Envelope.intersects(p1, p2, p)) {
        if (CGAlgorithms.orientationIndex(p1, p2, p) === 0 && CGAlgorithms.orientationIndex(p2, p1, p) === 0) {
          this._isProper = true
          if (p.equals(p1) || p.equals(p2)) {
            this._isProper = false
          }
          this._result = LineIntersector.POINT_INTERSECTION
          return null
        }
      }
      this._result = LineIntersector.NO_INTERSECTION
    } else return LineIntersector.prototype.computeIntersection.apply(this, arguments)
  }
  normalizeToMinimum (n1, n2, n3, n4, normPt) {
    normPt.x = this.smallestInAbsValue(n1.x, n2.x, n3.x, n4.x)
    normPt.y = this.smallestInAbsValue(n1.y, n2.y, n3.y, n4.y)
    n1.x -= normPt.x
    n1.y -= normPt.y
    n2.x -= normPt.x
    n2.y -= normPt.y
    n3.x -= normPt.x
    n3.y -= normPt.y
    n4.x -= normPt.x
    n4.y -= normPt.y
  }
  safeHCoordinateIntersection (p1, p2, q1, q2) {
    let intPt = null
    try {
      intPt = HCoordinate.intersection(p1, p2, q1, q2)
    } catch (e) {
      if (e instanceof NotRepresentableException) {
        intPt = RobustLineIntersector.nearestEndpoint(p1, p2, q1, q2)
      } else throw e
    } finally {}
    return intPt
  }
  intersection (p1, p2, q1, q2) {
    let intPt = this.intersectionWithNormalization(p1, p2, q1, q2)
    if (!this.isInSegmentEnvelopes(intPt)) {
      intPt = new Coordinate(RobustLineIntersector.nearestEndpoint(p1, p2, q1, q2))
    }
    if (this._precisionModel !== null) {
      this._precisionModel.makePrecise(intPt)
    }
    return intPt
  }
  smallestInAbsValue (x1, x2, x3, x4) {
    let x = x1
    let xabs = Math.abs(x)
    if (Math.abs(x2) < xabs) {
      x = x2
      xabs = Math.abs(x2)
    }
    if (Math.abs(x3) < xabs) {
      x = x3
      xabs = Math.abs(x3)
    }
    if (Math.abs(x4) < xabs) {
      x = x4
    }
    return x
  }
  checkDD (p1, p2, q1, q2, intPt) {
    const intPtDD = CGAlgorithmsDD.intersection(p1, p2, q1, q2)
    const isIn = this.isInSegmentEnvelopes(intPtDD)
    System.out.println('DD in env = ' + isIn + '  --------------------- ' + intPtDD)
    if (intPt.distance(intPtDD) > 0.0001) {
      System.out.println('Distance = ' + intPt.distance(intPtDD))
    }
  }
  intersectionWithNormalization (p1, p2, q1, q2) {
    const n1 = new Coordinate(p1)
    const n2 = new Coordinate(p2)
    const n3 = new Coordinate(q1)
    const n4 = new Coordinate(q2)
    const normPt = new Coordinate()
    this.normalizeToEnvCentre(n1, n2, n3, n4, normPt)
    const intPt = this.safeHCoordinateIntersection(n1, n2, n3, n4)
    intPt.x += normPt.x
    intPt.y += normPt.y
    return intPt
  }
  computeCollinearIntersection (p1, p2, q1, q2) {
    const p1q1p2 = Envelope.intersects(p1, p2, q1)
    const p1q2p2 = Envelope.intersects(p1, p2, q2)
    const q1p1q2 = Envelope.intersects(q1, q2, p1)
    const q1p2q2 = Envelope.intersects(q1, q2, p2)
    if (p1q1p2 && p1q2p2) {
      this._intPt[0] = q1
      this._intPt[1] = q2
      return LineIntersector.COLLINEAR_INTERSECTION
    }
    if (q1p1q2 && q1p2q2) {
      this._intPt[0] = p1
      this._intPt[1] = p2
      return LineIntersector.COLLINEAR_INTERSECTION
    }
    if (p1q1p2 && q1p1q2) {
      this._intPt[0] = q1
      this._intPt[1] = p1
      return q1.equals(p1) && !p1q2p2 && !q1p2q2 ? LineIntersector.POINT_INTERSECTION : LineIntersector.COLLINEAR_INTERSECTION
    }
    if (p1q1p2 && q1p2q2) {
      this._intPt[0] = q1
      this._intPt[1] = p2
      return q1.equals(p2) && !p1q2p2 && !q1p1q2 ? LineIntersector.POINT_INTERSECTION : LineIntersector.COLLINEAR_INTERSECTION
    }
    if (p1q2p2 && q1p1q2) {
      this._intPt[0] = q2
      this._intPt[1] = p1
      return q2.equals(p1) && !p1q1p2 && !q1p2q2 ? LineIntersector.POINT_INTERSECTION : LineIntersector.COLLINEAR_INTERSECTION
    }
    if (p1q2p2 && q1p2q2) {
      this._intPt[0] = q2
      this._intPt[1] = p2
      return q2.equals(p2) && !p1q1p2 && !q1p1q2 ? LineIntersector.POINT_INTERSECTION : LineIntersector.COLLINEAR_INTERSECTION
    }
    return LineIntersector.NO_INTERSECTION
  }
  normalizeToEnvCentre (n00, n01, n10, n11, normPt) {
    const minX0 = n00.x < n01.x ? n00.x : n01.x
    const minY0 = n00.y < n01.y ? n00.y : n01.y
    const maxX0 = n00.x > n01.x ? n00.x : n01.x
    const maxY0 = n00.y > n01.y ? n00.y : n01.y
    const minX1 = n10.x < n11.x ? n10.x : n11.x
    const minY1 = n10.y < n11.y ? n10.y : n11.y
    const maxX1 = n10.x > n11.x ? n10.x : n11.x
    const maxY1 = n10.y > n11.y ? n10.y : n11.y
    const intMinX = minX0 > minX1 ? minX0 : minX1
    const intMaxX = maxX0 < maxX1 ? maxX0 : maxX1
    const intMinY = minY0 > minY1 ? minY0 : minY1
    const intMaxY = maxY0 < maxY1 ? maxY0 : maxY1
    const intMidX = (intMinX + intMaxX) / 2.0
    const intMidY = (intMinY + intMaxY) / 2.0
    normPt.x = intMidX
    normPt.y = intMidY
    n00.x -= normPt.x
    n00.y -= normPt.y
    n01.x -= normPt.x
    n01.y -= normPt.y
    n10.x -= normPt.x
    n10.y -= normPt.y
    n11.x -= normPt.x
    n11.y -= normPt.y
  }
  computeIntersect (p1, p2, q1, q2) {
    this._isProper = false
    if (!Envelope.intersects(p1, p2, q1, q2)) return LineIntersector.NO_INTERSECTION
    const Pq1 = CGAlgorithms.orientationIndex(p1, p2, q1)
    const Pq2 = CGAlgorithms.orientationIndex(p1, p2, q2)
    if ((Pq1 > 0 && Pq2 > 0) || (Pq1 < 0 && Pq2 < 0)) {
      return LineIntersector.NO_INTERSECTION
    }
    const Qp1 = CGAlgorithms.orientationIndex(q1, q2, p1)
    const Qp2 = CGAlgorithms.orientationIndex(q1, q2, p2)
    if ((Qp1 > 0 && Qp2 > 0) || (Qp1 < 0 && Qp2 < 0)) {
      return LineIntersector.NO_INTERSECTION
    }
    const collinear = Pq1 === 0 && Pq2 === 0 && Qp1 === 0 && Qp2 === 0
    if (collinear) {
      return this.computeCollinearIntersection(p1, p2, q1, q2)
    }
    if (Pq1 === 0 || Pq2 === 0 || Qp1 === 0 || Qp2 === 0) {
      this._isProper = false
      if (p1.equals2D(q1) || p1.equals2D(q2)) {
        this._intPt[0] = p1
      } else if (p2.equals2D(q1) || p2.equals2D(q2)) {
        this._intPt[0] = p2
      } else if (Pq1 === 0) {
        this._intPt[0] = new Coordinate(q1)
      } else if (Pq2 === 0) {
        this._intPt[0] = new Coordinate(q2)
      } else if (Qp1 === 0) {
        this._intPt[0] = new Coordinate(p1)
      } else if (Qp2 === 0) {
        this._intPt[0] = new Coordinate(p2)
      }
    } else {
      this._isProper = true
      this._intPt[0] = this.intersection(p1, p2, q1, q2)
    }
    return LineIntersector.POINT_INTERSECTION
  }
  interfaces_ () {
    return []
  }
  getClass () {
    return RobustLineIntersector
  }
  static nearestEndpoint (p1, p2, q1, q2) {
    let nearestPt = p1
    let minDist = CGAlgorithms.distancePointLine(p1, q1, q2)
    let dist = CGAlgorithms.distancePointLine(p2, q1, q2)
    if (dist < minDist) {
      minDist = dist
      nearestPt = p2
    }
    dist = CGAlgorithms.distancePointLine(q1, p1, p2)
    if (dist < minDist) {
      minDist = dist
      nearestPt = q1
    }
    dist = CGAlgorithms.distancePointLine(q2, p1, p2)
    if (dist < minDist) {
      minDist = dist
      nearestPt = q2
    }
    return nearestPt
  }
}
