// OBJ / PLY 的平面简单多边形三角化。凸多边形保留原扇形切分,
// 凹多边形用耳切;自相交或非平面的多边形明确报错。

///|
fn polygon_turn(
  a : (Double, Double),
  b : (Double, Double),
  c : (Double, Double),
) -> Double {
  (b.0 - a.0) * (c.1 - a.1) - (b.1 - a.1) * (c.0 - a.0)
}

///|
fn polygon_on_segment(
  p : (Double, Double),
  a : (Double, Double),
  b : (Double, Double),
) -> Bool {
  polygon_turn(a, b, p).abs() <= 1.0e-14 &&
  p.0 >= a.0.min(b.0) &&
  p.0 <= a.0.max(b.0) &&
  p.1 >= a.1.min(b.1) &&
  p.1 <= a.1.max(b.1)
}

///|
fn polygon_edges_cross(
  a : (Double, Double),
  b : (Double, Double),
  c : (Double, Double),
  d : (Double, Double),
) -> Bool {
  let ab_c = polygon_turn(a, b, c)
  let ab_d = polygon_turn(a, b, d)
  let cd_a = polygon_turn(c, d, a)
  let cd_b = polygon_turn(c, d, b)
  (ab_c * ab_d < 0.0 && cd_a * cd_b < 0.0) ||
  polygon_on_segment(c, a, b) ||
  polygon_on_segment(d, a, b) ||
  polygon_on_segment(a, c, d) ||
  polygon_on_segment(b, c, d)
}

///|
fn triangulate_polygon(
  vertices : Array[Vec3],
) -> Result[Array[(Int, Int, Int)], String] {
  let n = vertices.length()
  if n < 3 {
    return Err("一个面至少要有三个顶点")
  }
  // 三角形的退化性由校验层报告,解析层不丢弃原始三点。
  if n == 3 {
    return Ok([(0, 1, 2)])
  }
  let origin = vertices[0]
  let mut scale = 0.0
  for v in vertices {
    let p = v.sub(origin)
    scale = scale.max(p.x.abs()).max(p.y.abs()).max(p.z.abs())
  }
  if scale == 0.0 || scale.is_inf() || scale.is_nan() {
    return Err("多边形没有可用的有限面积")
  }
  let normalized : Array[Vec3] = []
  for v in vertices {
    let p = v.sub(origin)
    normalized.push(Vec3::new(p.x / scale, p.y / scale, p.z / scale))
  }
  let mut nx = 0.0
  let mut ny = 0.0
  let mut nz = 0.0
  for i = 0; i < n; i = i + 1 {
    let cross = normalized[i].cross(normalized[(i + 1) % n])
    nx = nx + cross.x
    ny = ny + cross.y
    nz = nz + cross.z
  }
  let normal = Vec3::new(nx, ny, nz)
  if normal.length() == 0.0 {
    return Err("多边形零面积或自相交,无法三角化")
  }
  let unit = normal.normalize()
  for p in normalized {
    if p.dot(unit).abs() > 1.0e-8 {
      return Err("仅支持平面多边形;这个面的顶点不共面")
    }
  }
  let axis = if nx.abs() >= ny.abs() && nx.abs() >= nz.abs() {
    0
  } else if ny.abs() >= nz.abs() {
    1
  } else {
    2
  }
  let points : Array[(Double, Double)] = []
  for p in normalized {
    points.push(
      if axis == 0 {
        (p.y, p.z)
      } else if axis == 1 {
        (p.z, p.x)
      } else {
        (p.x, p.y)
      },
    )
  }
  let mut area = 0.0
  for i = 0; i < n; i = i + 1 {
    let a = points[i]
    let b = points[(i + 1) % n]
    area = area + a.0 * b.1 - a.1 * b.0
    for j = i + 1; j < n; j = j + 1 {
      if j == i + 1 || (i == 0 && j == n - 1) {
        continue
      }
      if polygon_edges_cross(a, b, points[j], points[(j + 1) % n]) {
        return Err("多边形有自相交或重复边,无法三角化")
      }
    }
  }
  let sign = if area > 0.0 { 1.0 } else { -1.0 }
  let result : Array[(Int, Int, Int)] = []
  let mut convex = true
  for i = 0; i < n; i = i + 1 {
    if polygon_turn(points[(i + n - 1) % n], points[i], points[(i + 1) % n]) *
      sign <
      0.0 {
      convex = false
    }
  }
  if convex {
    for i = 1; i + 1 < n; i = i + 1 {
      result.push((0, i, i + 1))
    }
    return Ok(result)
  }
  let mut live : Array[Int] = []
  for i = 0; i < n; i = i + 1 {
    live.push(i)
  }
  while live.length() > 3 {
    let count = live.length()
    let mut ear : Int? = None
    for i = 0; i < count; i = i + 1 {
      let a = live[(i + count - 1) % count]
      let b = live[i]
      let c = live[(i + 1) % count]
      if polygon_turn(points[a], points[b], points[c]) * sign <= 0.0 {
        continue
      }
      let mut blocked = false
      for j in live {
        if j == a || j == b || j == c {
          continue
        }
        if polygon_turn(points[a], points[b], points[j]) * sign >= -1.0e-14 &&
          polygon_turn(points[b], points[c], points[j]) * sign >= -1.0e-14 &&
          polygon_turn(points[c], points[a], points[j]) * sign >= -1.0e-14 {
          blocked = true
        }
      }
      if !blocked {
        ear = Some(i)
        break
      }
    }
    match ear {
      None =>
        return Err("多边形无法安全三角化,请先修正这个面")
      Some(i) => {
        result.push(
          (live[(i + count - 1) % count], live[i], live[(i + 1) % count]),
        )
        let next : Array[Int] = []
        for j = 0; j < count; j = j + 1 {
          if j != i {
            next.push(live[j])
          }
        }
        live = next
      }
    }
  }
  result.push((live[0], live[1], live[2]))
  Ok(result)
}