///|
fn min3(a : Double, b : Double, c : Double) -> Double {
  let m1 = if a < b { a } else { b }
  if m1 < c {
    m1
  } else {
    c
  }
}

///|
fn max3(a : Double, b : Double, c : Double) -> Double {
  let m1 = if a > b { a } else { b }
  if m1 > c {
    m1
  } else {
    c
  }
}

///|
/// 3D Triangle represented by three 3D vertices in continuous space.
pub struct Triangle3D {
  p0_x : Double
  p0_y : Double
  p0_z : Double
  p1_x : Double
  p1_y : Double
  p1_z : Double
  p2_x : Double
  p2_y : Double
  p2_z : Double
} derive(Eq, Debug, Default)

///|
/// Constructs a new Triangle3D with vertices (p0, p1, p2).
pub fn Triangle3D::new(
  p0_x : Double,
  p0_y : Double,
  p0_z : Double,
  p1_x : Double,
  p1_y : Double,
  p1_z : Double,
  p2_x : Double,
  p2_y : Double,
  p2_z : Double,
) -> Triangle3D {
  Triangle3D::{ p0_x, p0_y, p0_z, p1_x, p1_y, p1_z, p2_x, p2_y, p2_z, }
}

///|
/// Computes the tight Axis-Aligned Bounding Box (Box3D) enclosing the triangle.
pub fn Triangle3D::to_box(self : Triangle3D) -> Box3D {
  let min_x = min3(self.p0_x, self.p1_x, self.p2_x)
  let min_y = min3(self.p0_y, self.p1_y, self.p2_y)
  let min_z = min3(self.p0_z, self.p1_z, self.p2_z)
  let max_x = max3(self.p0_x, self.p1_x, self.p2_x)
  let max_y = max3(self.p0_y, self.p1_y, self.p2_y)
  let max_z = max3(self.p0_z, self.p1_z, self.p2_z)
  Box3D::{ min_x, min_y, min_z, max_x, max_y, max_z, }
}

///|
/// Computes the centroid (arithmetic center) of the triangle.
pub fn Triangle3D::centroid(self : Triangle3D) -> (Double, Double, Double) {
  (
    (self.p0_x + self.p1_x + self.p2_x) / 3.0,
    (self.p0_y + self.p1_y + self.p2_y) / 3.0,
    (self.p0_z + self.p1_z + self.p2_z) / 3.0,
  )
}

///|
/// Computes the surface normal vector of the triangle (unnormalized if degenerate, normalized otherwise).
pub fn Triangle3D::normal(self : Triangle3D) -> (Double, Double, Double) {
  let e1_x = self.p1_x - self.p0_x
  let e1_y = self.p1_y - self.p0_y
  let e1_z = self.p1_z - self.p0_z
  let e2_x = self.p2_x - self.p0_x
  let e2_y = self.p2_y - self.p0_y
  let e2_z = self.p2_z - self.p0_z
  let nx = e1_y * e2_z - e1_z * e2_y
  let ny = e1_z * e2_x - e1_x * e2_z
  let nz = e1_x * e2_y - e1_y * e2_x
  let len_sq = nx * nx + ny * ny + nz * nz
  if len_sq < 1.0e-18 {
    (0.0, 0.0, 1.0)
  } else {
    let len = len_sq.sqrt()
    (nx / len, ny / len, nz / len)
  }
}

///|
/// Computes the surface area of the triangle.
pub fn Triangle3D::area(self : Triangle3D) -> Double {
  let e1_x = self.p1_x - self.p0_x
  let e1_y = self.p1_y - self.p0_y
  let e1_z = self.p1_z - self.p0_z
  let e2_x = self.p2_x - self.p0_x
  let e2_y = self.p2_y - self.p0_y
  let e2_z = self.p2_z - self.p0_z
  let nx = e1_y * e2_z - e1_z * e2_y
  let ny = e1_z * e2_x - e1_x * e2_z
  let nz = e1_x * e2_y - e1_y * e2_x
  0.5 * (nx * nx + ny * ny + nz * nz).sqrt()
}

///|
/// Tests if a 3D ray from (ox, oy, oz) with direction (dx, dy, dz) intersects the triangle.
/// Returns Some(t) where t >= 0.0 is the ray distance if hit, or None if missed.
/// Implemented using the two-sided Möller–Trumbore intersection algorithm.
pub fn Triangle3D::ray_intersection(
  self : Triangle3D,
  ox : Double,
  oy : Double,
  oz : Double,
  dx : Double,
  dy : Double,
  dz : Double,
) -> Double? {
  let e1_x = self.p1_x - self.p0_x
  let e1_y = self.p1_y - self.p0_y
  let e1_z = self.p1_z - self.p0_z
  let e2_x = self.p2_x - self.p0_x
  let e2_y = self.p2_y - self.p0_y
  let e2_z = self.p2_z - self.p0_z

  let p_x = dy * e2_z - dz * e2_y
  let p_y = dz * e2_x - dx * e2_z
  let p_z = dx * e2_y - dy * e2_x

  let det = e1_x * p_x + e1_y * p_y + e1_z * p_z
  if det > -1.0e-12 && det < 1.0e-12 {
    return None
  }
  let inv_det = 1.0 / det

  let t_x = ox - self.p0_x
  let t_y = oy - self.p0_y
  let t_z = oz - self.p0_z

  let u = (t_x * p_x + t_y * p_y + t_z * p_z) * inv_det
  if u < 0.0 || u > 1.0 {
    return None
  }

  let q_x = t_y * e1_z - t_z * e1_y
  let q_y = t_z * e1_x - t_x * e1_z
  let q_z = t_x * e1_y - t_y * e1_x

  let v = (dx * q_x + dy * q_y + dz * q_z) * inv_det
  if v < 0.0 || u + v > 1.0 {
    return None
  }

  let t = (e2_x * q_x + e2_y * q_y + e2_z * q_z) * inv_det
  if t < 0.0 {
    None
  } else {
    Some(t)
  }
}

///|
/// Tests if a finite 3D line segment (x1, y1, z1) -> (x2, y2, z2) intersects the triangle.
pub fn Triangle3D::intersects_segment(
  self : Triangle3D,
  x1 : Double,
  y1 : Double,
  z1 : Double,
  x2 : Double,
  y2 : Double,
  z2 : Double,
) -> Bool {
  let dx = x2 - x1
  let dy = y2 - y1
  let dz = z2 - z1
  match self.ray_intersection(x1, y1, z1, dx, dy, dz) {
    Some(t) => t >= 0.0 && t <= 1.0
    None => false
  }
}