use bone_types::{Point3, UnitVec3};

#[derive(Copy, Clone, Debug, PartialEq)]
pub struct MeshVertex {
    position: Point3,
    normal: UnitVec3,
}

impl MeshVertex {
    #[must_use]
    pub(crate) const fn new(position: Point3, normal: UnitVec3) -> Self {
        Self { position, normal }
    }

    #[must_use]
    pub const fn position(self) -> Point3 {
        self.position
    }

    #[must_use]
    pub const fn normal(self) -> UnitVec3 {
        self.normal
    }
}

#[derive(Clone, Debug, PartialEq)]
pub struct TriMesh {
    vertices: Vec<MeshVertex>,
    triangles: Vec<[u32; 3]>,
}

impl TriMesh {
    #[must_use]
    pub fn vertices(&self) -> &[MeshVertex] {
        &self.vertices
    }

    #[must_use]
    pub fn triangles(&self) -> &[[u32; 3]] {
        &self.triangles
    }

    pub(crate) fn from_grid(
        columns: u32,
        rows: u32,
        vertex: impl Fn(u32, u32) -> MeshVertex,
    ) -> Self {
        let vertices = (0..rows)
            .flat_map(|j| (0..columns).map(move |i| (i, j)))
            .map(|(i, j)| vertex(i, j))
            .collect();
        let index = |i: u32, j: u32| j * columns + i;
        let triangles = (0..rows.saturating_sub(1))
            .flat_map(move |j| (0..columns.saturating_sub(1)).map(move |i| (i, j)))
            .flat_map(|(i, j)| {
                [
                    [index(i, j), index(i + 1, j), index(i + 1, j + 1)],
                    [index(i, j), index(i + 1, j + 1), index(i, j + 1)],
                ]
            })
            .collect();
        Self {
            vertices,
            triangles,
        }
    }
}
