// Meshlet data structures for GPU-driven meshlet rendering.
// A Meshlet is a compact geometry cluster (up to 64 vertices, 126 primitives) with bounding info.
// MeshletBuffer manages GPU storage for meshlet data.
//
// NVIDIA Turing mesh shader budget: 64 vertices, 126 primitives.
// 3 × 126 + 4 = 382 bytes fits exactly in 3 × 128 = 384 byte allocation block.

/// Maximum vertices per meshlet (NVIDIA Turing recommendation).
pub const MESHLET_MAX_VERTICES: u32 = 64;

/// Maximum primitives (triangles) per meshlet. The '6' in 126 is intentional:
/// first-gen hardware allocates primitive indices in 128-byte granularity and
/// reserves 4 bytes for the primitive count. 3 × 126 + 4 = 382 maximizes fit
/// into a 3 × 128 = 384 byte block.
pub const MESHLET_MAX_TRIANGLES: u32 = 126;

/// GPU-uploadable meshlet descriptor (48 bytes).
/// Each meshlet references a contiguous range of vertices and triangles.
/// `lod_level` is used by the Quantum Cull shader to select appropriate detail.
#[repr(C)]
#[derive(Debug, Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
pub struct GpuMeshlet {
    pub vertex_offset: u32,
    pub vertex_count: u32,
    pub triangle_offset: u32,
    pub triangle_count: u32,
    pub layer_index: u32,
    pub object_id: u32,
    pub bounding_sphere: [f32; 4], // xyz=center, w=radius
    /// LOD level this meshlet belongs to (0=Full, 1=Medium, 2=Low).
    /// Quantum Cull shader filters meshlets by computed LOD vs declared level.
    pub lod_level: u32,
    pub _pad: u32,
}

/// Per-meshlet visibility output from cull pass.
#[repr(C)]
#[derive(Debug, Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
pub struct MeshletVisibility {
    pub visible: u32,
}

/// GPU buffer set for meshlet rendering.
pub struct MeshletBuffer {
    pub meshlet_buffer: wgpu::Buffer,
    pub vertex_buffer: wgpu::Buffer,
    pub triangle_buffer: wgpu::Buffer,
    meshlet_count: u32,
}

impl MeshletBuffer {
    /// Upload meshlet data to GPU buffers.
    pub fn upload(device: &wgpu::Device, meshlets: &[GpuMeshlet], vertices: &[[f32; 3]], triangles: &[u8]) -> Self {
        use wgpu::util::DeviceExt;

        let meshlet_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
            label: Some("meshlet_buffer"),
            contents: bytemuck::cast_slice(meshlets),
            usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
        });

        let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
            label: Some("meshlet_vertex_buffer"),
            contents: bytemuck::cast_slice(vertices),
            usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::VERTEX,
        });

        // Pad triangle data to 4-byte alignment for storage buffer.
        let padded_len = (triangles.len() + 3) & !3;
        let mut padded = vec![0u8; padded_len];
        padded[..triangles.len()].copy_from_slice(triangles);

        let triangle_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
            label: Some("meshlet_triangle_buffer"),
            contents: &padded,
            usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::INDEX,
        });

        Self {
            meshlet_buffer,
            vertex_buffer,
            triangle_buffer,
            meshlet_count: meshlets.len() as u32,
        }
    }

    /// Number of meshlets in this buffer.
    pub fn count(&self) -> u32 {
        self.meshlet_count
    }

    /// Explicitly destroy GPU resources.
    pub fn destroy(&mut self) {
        self.meshlet_buffer.destroy();
        self.vertex_buffer.destroy();
        self.triangle_buffer.destroy();
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use bytemuck::Zeroable;

    #[test]
    fn gpu_meshlet_size() {
        assert_eq!(std::mem::size_of::<GpuMeshlet>(), 48);
    }

    #[test]
    fn meshlet_visibility_size() {
        assert_eq!(std::mem::size_of::<MeshletVisibility>(), 4);
    }

    #[test]
    fn gpu_meshlet_zeroed() {
        let m = GpuMeshlet::zeroed();
        assert_eq!(m.vertex_offset, 0);
        assert_eq!(m.bounding_sphere, [0.0; 4]);
    }

    #[test]
    fn gpu_meshlet_pod_roundtrip() {
        let m = GpuMeshlet {
            vertex_offset: 100,
            vertex_count: 64,
            triangle_offset: 200,
            triangle_count: 126,
            layer_index: 6,
            object_id: 42,
            bounding_sphere: [1.0, 2.0, 3.0, 5.0],
            lod_level: 1,
            _pad: 0,
        };
        let bytes = bytemuck::bytes_of(&m);
        let m2: &GpuMeshlet = bytemuck::from_bytes(bytes);
        assert_eq!(m2.vertex_offset, 100);
        assert_eq!(m2.object_id, 42);
        assert_eq!(m2.lod_level, 1);
    }
}
