import LineString from '../geom/LineString'
import CoordinateList from '../geom/CoordinateList'
import GeometryTransformer from '../geom/util/GeometryTransformer'
import IllegalArgumentException from '../../../../java/lang/IllegalArgumentException'
import MultiPolygon from '../geom/MultiPolygon'
import LineSegment from '../geom/LineSegment'

export default class Densifier {
  constructor () {
    this._inputGeom = null
    this._distanceTolerance = null
    let inputGeom = arguments[0]
    this._inputGeom = inputGeom
  }
  getResultGeometry () {
    return new DensifyTransformer(this._distanceTolerance).transform(this._inputGeom)
  }
  setDistanceTolerance (distanceTolerance) {
    if (distanceTolerance <= 0.0) throw new IllegalArgumentException('Tolerance must be positive')
    this._distanceTolerance = distanceTolerance
  }
  interfaces_ () {
    return []
  }
  getClass () {
    return Densifier
  }
  static densifyPoints (pts, distanceTolerance, precModel) {
    const seg = new LineSegment()
    const coordList = new CoordinateList()
    for (let i = 0; i < pts.length - 1; i++) {
      seg.p0 = pts[i]
      seg.p1 = pts[i + 1]
      coordList.add(seg.p0, false)
      const len = seg.getLength()
      const densifiedSegCount = Math.trunc(len / distanceTolerance) + 1
      if (densifiedSegCount > 1) {
        const densifiedSegLen = len / densifiedSegCount
        for (let j = 1; j < densifiedSegCount; j++) {
          const segFract = j * densifiedSegLen / len
          const p = seg.pointAlong(segFract)
          precModel.makePrecise(p)
          coordList.add(p, false)
        }
      }
    }
    coordList.add(pts[pts.length - 1], false)
    return coordList.toCoordinateArray()
  }
  static densify (geom, distanceTolerance) {
    const densifier = new Densifier(geom)
    densifier.setDistanceTolerance(distanceTolerance)
    return densifier.getResultGeometry()
  }
  static get DensifyTransformer () { return DensifyTransformer }
}

class DensifyTransformer extends GeometryTransformer {
  constructor () {
    super()
    this.distanceTolerance = null
    let distanceTolerance = arguments[0]
    this.distanceTolerance = distanceTolerance
  }
  transformMultiPolygon (geom, parent) {
    const roughGeom = GeometryTransformer.prototype.transformMultiPolygon.call(this, geom, parent)
    return this.createValidArea(roughGeom)
  }
  transformPolygon (geom, parent) {
    const roughGeom = GeometryTransformer.prototype.transformPolygon.call(this, geom, parent)
    if (parent instanceof MultiPolygon) {
      return roughGeom
    }
    return this.createValidArea(roughGeom)
  }
  transformCoordinates (coords, parent) {
    const inputPts = coords.toCoordinateArray()
    let newPts = Densifier.densifyPoints(inputPts, this.distanceTolerance, parent.getPrecisionModel())
    if (parent instanceof LineString && newPts.length === 1) {
      newPts = new Array(0).fill(null)
    }
    return this._factory.getCoordinateSequenceFactory().create(newPts)
  }
  createValidArea (roughAreaGeom) {
    return roughAreaGeom.buffer(0.0)
  }
  interfaces_ () {
    return []
  }
  getClass () {
    return DensifyTransformer
  }
}
