///|
/// Mesh geometry from triangle collections.
/// Supports building from indexed vertex and face arrays.

pub(all) struct Mesh {
  vertices : Array[Vec3]
  indices : Array[(Int, Int, Int)]
  material : Material
} derive(Debug)

pub fn Mesh::new(material~ : Material) -> Mesh {
  {
    vertices: Array::new(capacity=1024),
    indices: Array::new(capacity=1024),
    material,
  }
}

pub fn Mesh::add_vertex(self : Mesh, v : Vec3) -> Mesh {
  let verts = self.vertices
  verts.push(v)
  { ..self, vertices: verts }
}

pub fn Mesh::add_triangle(self : Mesh, i0 : Int, i1 : Int, i2 : Int) -> Mesh {
  let idx = self.indices
  idx.push((i0, i1, i2))
  { ..self, indices: idx }
}

pub fn Mesh::build_cube(center~ : Vec3, size~ : Double, material~ : Material) -> Mesh {
  let half = size / 2.0
  let cx = center.x
  let cy = center.y
  let cz = center.z

  let verts = Array::new(capacity=8)
  verts.push({ x: cx - half, y: cy - half, z: cz - half })
  verts.push({ x: cx + half, y: cy - half, z: cz - half })
  verts.push({ x: cx - half, y: cy + half, z: cz - half })
  verts.push({ x: cx + half, y: cy + half, z: cz - half })
  verts.push({ x: cx - half, y: cy - half, z: cz + half })
  verts.push({ x: cx + half, y: cy - half, z: cz + half })
  verts.push({ x: cx - half, y: cy + half, z: cz + half })
  verts.push({ x: cx + half, y: cy + half, z: cz + half })

  let mut mesh = Mesh::new(material=material)
  for i in 0..<8 {
    mesh = mesh.add_vertex(verts[i])
  }

  let faces = Array::new(capacity=12)
  faces.push((0, 2, 3))
  faces.push((0, 3, 1))
  faces.push((4, 5, 7))
  faces.push((4, 7, 6))
  faces.push((0, 4, 6))
  faces.push((0, 6, 2))
  faces.push((1, 3, 7))
  faces.push((1, 7, 5))
  faces.push((0, 1, 5))
  faces.push((0, 5, 4))
  faces.push((2, 6, 7))
  faces.push((2, 7, 3))

  for i in 0..<12 {
    let (a, b, c) = faces[i]
    mesh = mesh.add_triangle(a, b, c)
  }

  mesh
}

pub fn Mesh::build_uv_sphere(center~ : Vec3, radius~ : Double, segments~ : Int, rings~ : Int, material~ : Material) -> Mesh {
  let mut mesh = Mesh::new(material=material)
  let seg = segments.max(3)
  let ring = rings.max(2)

  let vertices = Array::new(capacity=(seg + 1) * (ring + 1))
  for j in 0..=ring {
    let phi = @math.PI * j.to_double() / ring.to_double()
    for i in 0..=seg {
      let theta = 2.0 * @math.PI * i.to_double() / seg.to_double()
      let x = @math.cos(theta) * @math.sin(phi)
      let y = @math.cos(phi)
      let z = @math.sin(theta) * @math.sin(phi)
      vertices.push({ x: x * radius + center.x, y: y * radius + center.y, z: z * radius + center.z })
    }
  }

  for j in 0.. HitableList {
  let mut world = HitableList::new()
  for i in 0..