import MonotoneChainSelectAction from '../index/chain/MonotoneChainSelectAction'
import Bintree from '../index/bintree/Bintree'
import Interval from '../index/bintree/Interval'
import Double from '../../../../java/lang/Double'
import MonotoneChainBuilder from '../index/chain/MonotoneChainBuilder'
import CoordinateArrays from '../geom/CoordinateArrays'
import RobustDeterminant from './RobustDeterminant'
import Envelope from '../geom/Envelope'
import PointInRing from './PointInRing'

export default class MCPointInRing {
  constructor () {
    this._ring = null
    this._tree = null
    this._crossings = 0
    this._interval = new Interval()
    const ring = arguments[0]
    this._ring = ring
    this.buildIndex()
  }
  testLineSegment (p, seg) {
    let xInt = null
    let x1 = null
    let y1 = null
    let x2 = null
    let y2 = null
    const p1 = seg.p0
    const p2 = seg.p1
    x1 = p1.x - p.x
    y1 = p1.y - p.y
    x2 = p2.x - p.x
    y2 = p2.y - p.y
    if ((y1 > 0 && y2 <= 0) || (y2 > 0 && y1 <= 0)) {
      xInt = RobustDeterminant.signOfDet2x2(x1, y1, x2, y2) / (y2 - y1)
      if (xInt > 0.0) {
        this._crossings++
      }
    }
  }
  buildIndex () {
    this._tree = new Bintree()
    const pts = CoordinateArrays.removeRepeatedPoints(this._ring.getCoordinates())
    const mcList = MonotoneChainBuilder.getChains(pts)
    for (let i = 0; i < mcList.size(); i++) {
      const mc = mcList.get(i)
      const mcEnv = mc.getEnvelope()
      this._interval.min = mcEnv.getMinY()
      this._interval.max = mcEnv.getMaxY()
      this._tree.insert(this._interval, mc)
    }
  }
  testMonotoneChain (rayEnv, mcSelecter, mc) {
    mc.select(rayEnv, mcSelecter)
  }
  isInside (pt) {
    this._crossings = 0
    const rayEnv = new Envelope(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, pt.y, pt.y)
    this._interval.min = pt.y
    this._interval.max = pt.y
    const segs = this._tree.query(this._interval)
    const mcSelecter = new MCSelecter(this, pt)
    for (const i = segs.iterator(); i.hasNext();) {
      const mc = i.next()
      this.testMonotoneChain(rayEnv, mcSelecter, mc)
    }
    if (this._crossings % 2 === 1) {
      return true
    }
    return false
  }
  interfaces_ () {
    return [PointInRing]
  }
  getClass () {
    return MCPointInRing
  }
  static get MCSelecter () { return MCSelecter }
}

class MCSelecter extends MonotoneChainSelectAction {
  constructor () {
    super()
    this.mcp = null
    this.p = null
    const mcp = arguments[0]
    const p = arguments[1]
    this.mcp = mcp
    this.p = p
  }
  select () {
    if (arguments.length === 1) {
      const ls = arguments[0]
      this.mcp.testLineSegment(this.p, ls)
    } else return MonotoneChainSelectAction.prototype.select.apply(this, arguments)
  }
  interfaces_ () {
    return []
  }
  getClass () {
    return MCSelecter
  }
}
