import {Point, TriangleOrientation} from '../../math/geometry/point'
import {Rectangle} from '../../math/geometry/rectangle'
// import {Assert} from '../../utils/assert'
import {CdtEdge} from './CdtEdge'
import {CdtSite} from './CdtSite'
import {ThreeArray} from './ThreeArray'

//  a trianlge oriented counterclockwise
export class CdtTriangle {
  //  the edges
  public TriEdges: ThreeArray<CdtEdge> = new ThreeArray<CdtEdge>()

  //  the sites
  public Sites: ThreeArray<CdtSite> = new ThreeArray<CdtSite>()

  static mkSSSD(a: CdtSite, b: CdtSite, c: CdtSite, createEdgeDelegate: (a: CdtSite, b: CdtSite) => CdtEdge) {
    const orientation = Point.getTriangleOrientation(a.point, b.point, c.point)
    const r = new CdtTriangle()
    switch (orientation) {
      case TriangleOrientation.Counterclockwise:
        r.FillCcwTriangle(a, b, c, createEdgeDelegate)
        break
      case TriangleOrientation.Clockwise:
        r.FillCcwTriangle(a, c, b, createEdgeDelegate)
        break
      default:
        throw new Error()
        break
    }
    return r
  }

  static mkSED(pi: CdtSite, edge: CdtEdge, createEdgeDelegate: (a: CdtSite, b: CdtSite) => CdtEdge) {
    const tri = new CdtTriangle()
    switch (Point.getTriangleOrientation(edge.upperSite.point, edge.lowerSite.point, pi.point)) {
      case TriangleOrientation.Counterclockwise:
        edge.CcwTriangle = tri
        tri.Sites.setItem(0, edge.upperSite)
        tri.Sites.setItem(1, edge.lowerSite)
        break
      case TriangleOrientation.Clockwise:
        edge.CwTriangle = tri
        tri.Sites.setItem(0, edge.lowerSite)
        tri.Sites.setItem(1, edge.upperSite)
        break
      default:
        throw new Error()
    }

    tri.TriEdges.setItem(0, edge)
    tri.Sites.setItem(2, pi)
    tri.CreateEdge(1, createEdgeDelegate)
    tri.CreateEdge(2, createEdgeDelegate)
    return tri
  }

  //
  static mkSSSEE(
    aLeft: CdtSite,
    aRight: CdtSite,
    bRight: CdtSite,
    a: CdtEdge,
    b: CdtEdge,
    createEdgeDelegate: (a: CdtSite, b: CdtSite) => CdtEdge,
  ) {
    /*Assert.assert(
      Point.getTriangleOrientation(aLeft.point, aRight.point, bRight.point) ==
        TriangleOrientation.Counterclockwise,
    )*/
    const tri = CdtTriangle.mkSSSD(aLeft, aRight, bRight, createEdgeDelegate)
    tri.TriEdges.setItem(0, a)
    tri.TriEdges.setItem(1, b)
    tri.BindEdgeToTriangle(aLeft, a)
    tri.BindEdgeToTriangle(aRight, b)
    tri.CreateEdge(2, createEdgeDelegate)
    return tri
  }

  //  in the trianlge, which is always oriented counterclockwise, the edge starts at site
  BindEdgeToTriangle(site: CdtSite, edge: CdtEdge) {
    if (site == edge.upperSite) {
      edge.CcwTriangle = this
    } else {
      edge.CwTriangle = this
    }
  }

  //  here a,b,c comprise a ccw triangle
  FillCcwTriangle(a: CdtSite, b: CdtSite, c: CdtSite, createEdgeDelegate: (a: CdtSite, b: CdtSite) => CdtEdge) {
    this.Sites.setItem(0, a)
    this.Sites.setItem(1, b)
    this.Sites.setItem(2, c)
    for (let i = 0; i < 3; i++) {
      this.CreateEdge(i, createEdgeDelegate)
    }
  }

  CreateEdge(i: number, createEdgeDelegate: (a: CdtSite, b: CdtSite) => CdtEdge) {
    const a = this.Sites.getItem(i)
    const b = this.Sites.getItem(i + 1)
    const edge: CdtEdge = createEdgeDelegate(a, b)
    this.TriEdges.setItem(i, edge)
    this.BindEdgeToTriangle(a, edge)
  }

  Contains(cdtSite: CdtSite): boolean {
    return this.Sites.has(cdtSite)
  }

  OppositeEdge(pi: CdtSite): CdtEdge {
    const index = this.Sites.index(pi)
    /*Assert.assert(index != -1)*/
    return this.TriEdges.getItem(index + 1)
  }

  //  #if TEST_MSAGL&&TEST_MSAGL
  //          // Returns a <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
  //          // A <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
  //          // <filterpriority>2</filterpriority>
  //          public override string ToString() {
  //              return String.Format("({0},{1},{2}", Sites[0], Sites[1], Sites[2]);
  //          }
  //  #endif
  OppositeSite(cdtEdge: CdtEdge): CdtSite {
    const i = this.TriEdges.index(cdtEdge)
    return this.Sites.getItem(i + 2)
  }

  BoundingBox(): Rectangle {
    const rect: Rectangle = Rectangle.mkPP(this.Sites.getItem(0).point, this.Sites.getItem(1).point)
    rect.add(this.Sites.getItem(2).point)
    return rect
  }

  static mkSSSEED(
    aLeft: CdtSite,
    aRight: CdtSite,
    bRight: CdtSite,
    a: CdtEdge,
    b: CdtEdge,
    createEdgeDelegate: (a: CdtSite, b: CdtSite) => CdtEdge,
  ) {
    const t = new CdtTriangle()
    t.Sites.setItem(0, aLeft)
    t.Sites.setItem(1, aRight)
    t.Sites.setItem(2, bRight)
    t.TriEdges.setItem(0, a)
    t.TriEdges.setItem(1, b)
    t.BindEdgeToTriangle(aLeft, a)
    t.BindEdgeToTriangle(aRight, b)
    t.CreateEdge(2, createEdgeDelegate)
    return t
  }
  toString(): string {
    return this.Sites.getItem(0).toString() + ',' + this.Sites.getItem(1).toString() + ',' + this.Sites.getItem(2).toString()
  }
}
