import Location from '../geom/Location'
import EdgeEnd from './EdgeEnd'
import Position from './Position'
import TopologyException from '../geom/TopologyException'
import Label from './Label'
export default class DirectedEdge extends EdgeEnd {
  constructor() {
    super()
    DirectedEdge.constructor_.apply(this, arguments)
  }
  static constructor_() {
    this._isForward = null
    this._isInResult = false
    this._isVisited = false
    this._sym = null
    this._next = null
    this._nextMin = null
    this._edgeRing = null
    this._minEdgeRing = null
    this._depth = [0, -999, -999]
    const edge = arguments[0], isForward = arguments[1]
    EdgeEnd.constructor_.call(this, edge)
    this._isForward = isForward
    if (isForward) {
      this.init(edge.getCoordinate(0), edge.getCoordinate(1))
    } else {
      const n = edge.getNumPoints() - 1
      this.init(edge.getCoordinate(n), edge.getCoordinate(n - 1))
    }
    this.computeDirectedLabel()
  }
  static depthFactor(currLocation, nextLocation) {
    if (currLocation === Location.EXTERIOR && nextLocation === Location.INTERIOR) return 1; else if (currLocation === Location.INTERIOR && nextLocation === Location.EXTERIOR) return -1
    return 0
  }
  getNextMin() {
    return this._nextMin
  }
  getDepth(position) {
    return this._depth[position]
  }
  setVisited(isVisited) {
    this._isVisited = isVisited
  }
  computeDirectedLabel() {
    this._label = new Label(this._edge.getLabel())
    if (!this._isForward) this._label.flip()
  }
  getNext() {
    return this._next
  }
  setDepth(position, depthVal) {
    if (this._depth[position] !== -999) 
      if (this._depth[position] !== depthVal) throw new TopologyException('assigned depths do not match', this.getCoordinate())
    
    this._depth[position] = depthVal
  }
  isInteriorAreaEdge() {
    let isInteriorAreaEdge = true
    for (let i = 0; i < 2; i++) 
      if (!(this._label.isArea(i) && this._label.getLocation(i, Position.LEFT) === Location.INTERIOR && this._label.getLocation(i, Position.RIGHT) === Location.INTERIOR)) 
        isInteriorAreaEdge = false
      
    
    return isInteriorAreaEdge
  }
  setNextMin(nextMin) {
    this._nextMin = nextMin
  }
  print(out) {
    super.print.call(this, out)
    out.print(' ' + this._depth[Position.LEFT] + '/' + this._depth[Position.RIGHT])
    out.print(' (' + this.getDepthDelta() + ')')
    if (this._isInResult) out.print(' inResult')
  }
  setMinEdgeRing(minEdgeRing) {
    this._minEdgeRing = minEdgeRing
  }
  isLineEdge() {
    const isLine = this._label.isLine(0) || this._label.isLine(1)
    const isExteriorIfArea0 = !this._label.isArea(0) || this._label.allPositionsEqual(0, Location.EXTERIOR)
    const isExteriorIfArea1 = !this._label.isArea(1) || this._label.allPositionsEqual(1, Location.EXTERIOR)
    return isLine && isExteriorIfArea0 && isExteriorIfArea1
  }
  setEdgeRing(edgeRing) {
    this._edgeRing = edgeRing
  }
  getMinEdgeRing() {
    return this._minEdgeRing
  }
  getDepthDelta() {
    let depthDelta = this._edge.getDepthDelta()
    if (!this._isForward) depthDelta = -depthDelta
    return depthDelta
  }
  setInResult(isInResult) {
    this._isInResult = isInResult
  }
  getSym() {
    return this._sym
  }
  isForward() {
    return this._isForward
  }
  getEdge() {
    return this._edge
  }
  printEdge(out) {
    this.print(out)
    out.print(' ')
    if (this._isForward) this._edge.print(out); else this._edge.printReverse(out)
  }
  setSym(de) {
    this._sym = de
  }
  setVisitedEdge(isVisited) {
    this.setVisited(isVisited)
    this._sym.setVisited(isVisited)
  }
  setEdgeDepths(position, depth) {
    let depthDelta = this.getEdge().getDepthDelta()
    if (!this._isForward) depthDelta = -depthDelta
    let directionFactor = 1
    if (position === Position.LEFT) directionFactor = -1
    const oppositePos = Position.opposite(position)
    const delta = depthDelta * directionFactor
    const oppositeDepth = depth + delta
    this.setDepth(position, depth)
    this.setDepth(oppositePos, oppositeDepth)
  }
  getEdgeRing() {
    return this._edgeRing
  }
  isInResult() {
    return this._isInResult
  }
  setNext(next) {
    this._next = next
  }
  isVisited() {
    return this._isVisited
  }
}
