// picogkffi primitives: Higher-level voxel primitives (box, cylinder).
// These are built from mesh primitives and rasterized to voxels,
// since the PicoGK C API does not have direct box/cylinder voxel constructors.

///|
/// new_box creates a box voxel object from min/max corners.
pub fn new_box(
  min_x : Double,
  min_y : Double,
  min_z : Double,
  max_x : Double,
  max_y : Double,
  max_z : Double,
) -> Voxels {
  let m = new_mesh()
  // 8 corner vertices
  let v0 = m.add_vertex(Vec3::new(min_x, min_y, min_z))
  let v1 = m.add_vertex(Vec3::new(max_x, min_y, min_z))
  let v2 = m.add_vertex(Vec3::new(max_x, max_y, min_z))
  let v3 = m.add_vertex(Vec3::new(min_x, max_y, min_z))
  let v4 = m.add_vertex(Vec3::new(min_x, min_y, max_z))
  let v5 = m.add_vertex(Vec3::new(max_x, min_y, max_z))
  let v6 = m.add_vertex(Vec3::new(max_x, max_y, max_z))
  let v7 = m.add_vertex(Vec3::new(min_x, max_y, max_z))
  ignore(v0)
  ignore(v1)
  ignore(v2)
  ignore(v3)
  ignore(v4)
  ignore(v5)
  ignore(v6)
  ignore(v7)
  // Bottom face (z = min_z): v0, v1, v2, v3
  ignore(m.add_triangle(0, 1, 2))
  ignore(m.add_triangle(0, 2, 3))
  // Top face (z = max_z): v4, v5, v6, v7
  ignore(m.add_triangle(4, 6, 5))
  ignore(m.add_triangle(4, 7, 6))
  // Front face (y = min_y): v0, v1, v5, v4
  ignore(m.add_triangle(0, 4, 5))
  ignore(m.add_triangle(0, 5, 1))
  // Back face (y = max_y): v3, v2, v6, v7
  ignore(m.add_triangle(3, 2, 6))
  ignore(m.add_triangle(3, 6, 7))
  // Left face (x = min_x): v0, v3, v7, v4
  ignore(m.add_triangle(0, 3, 7))
  ignore(m.add_triangle(0, 7, 4))
  // Right face (x = max_x): v1, v2, v6, v5
  ignore(m.add_triangle(1, 5, 6))
  ignore(m.add_triangle(1, 6, 2))
  let vox = from_mesh(m)
  m.destroy()
  vox
}

///|
/// new_cylinder creates a cylinder voxel object.
/// By default the cylinder runs along +Z from (x, y, z) to (x, y, z + height).
/// Pass dir_x/dir_y/dir_z to orient the axis differently.
pub fn new_cylinder(
  x : Double,
  y : Double,
  z : Double,
  radius : Double,
  height : Double,
  dir_x : Double?,
  dir_y : Double?,
  dir_z : Double?,
) -> Voxels {
  // Determine axis direction (default +Z)
  let dx = dir_x.unwrap_or(0.0)
  let dy = dir_y.unwrap_or(0.0)
  let dz = dir_z.unwrap_or(1.0)
  // Normalize direction
  let dlen = (dx * dx + dy * dy + dz * dz).sqrt()
  let (ax, ay, az) = if dlen > 0.0 {
    (dx / dlen, dy / dlen, dz / dlen)
  } else {
    (0.0, 0.0, 1.0)
  }
  // Compute a perpendicular vector for the circle
  let (px, py, pz) = if ax.abs() > 0.9 {
    (0.0, 1.0, 0.0)
  } else {
    (1.0, 0.0, 0.0)
  }
  // Make px,py,pz perpendicular to axis via Gram-Schmidt
  let dot = px * ax + py * ay + pz * az
  let p1x = px - dot * ax
  let p1y = py - dot * ay
  let p1z = pz - dot * az
  let p1len = (p1x * p1x + p1y * p1y + p1z * p1z).sqrt()
  let (ux, uy, uz) = (p1x / p1len, p1y / p1len, p1z / p1len)
  // Second perpendicular = axis × first
  let vx = ay * uz - az * uy
  let vy = az * ux - ax * uz
  let vz = ax * uy - ay * ux
  // Bottom and top centers
  let bx = x
  let by = y
  let bz = z
  let tx = x + ax * height
  let ty = y + ay * height
  let tz = z + az * height
  let n_seg = 32
  let m = new_mesh()
  // Bottom ring
  let bottom_start = m.add_vertex(Vec3::new(bx, by, bz)) // center bottom
  let bottom_ring = Array::make(n_seg, 0)
  for i in 0..