//! GPU buffer management for the physics system.
//! Follows PHYSICS.md section 12 — 10 required buffer types.

use crate::compute;

/// All GPU buffers required by the physics system.
pub struct GpuPhysicsBuffers {
    pub particle_state: wgpu::Buffer,
    pub particle_state_back: wgpu::Buffer,
    pub force_field_buffer: wgpu::Buffer,
    pub emitter_buffer: wgpu::Buffer,
    pub collision_plane_buffer: wgpu::Buffer,
    pub promotion_candidates: wgpu::Buffer,
    pub indirect_draw: wgpu::Buffer,
    pub indirect_dispatch: wgpu::Buffer,
    pub debug_stats: wgpu::Buffer,
    pub debug_stats_readback: wgpu::Buffer,
    pub alive_counter: wgpu::Buffer,
    pub max_particles: u32,
    pub max_force_fields: u32,
    pub max_emitters: u32,
    pub max_collision_planes: u32,
    pub max_promotion_candidates: u32,
}

impl GpuPhysicsBuffers {
    /// Particle state size in bytes (128 bytes per particle).
    const PARTICLE_SIZE: u64 = 128;
    /// Force field size in bytes.
    const FORCE_FIELD_SIZE: u64 = 48;
    /// Emitter GPU struct size in bytes.
    const EMITTER_SIZE: u64 = 160;
    /// Collision plane size in bytes.
    const COLLISION_PLANE_SIZE: u64 = 32;
    /// Promotion candidate size in bytes.
    const PROMOTION_CANDIDATE_SIZE: u64 = 32;

    pub fn new(device: &wgpu::Device, max_particles: u32) -> Self {
        let max_particles = max_particles.min(10_000_000);
        let max_force_fields = 64u32;
        let max_emitters = 32u32;
        let max_collision_planes = 16u32;
        let max_promotion_candidates = 256u32;

        let particle_state = compute::create_buffer_empty(
            device,
            "physics_particle_state",
            Self::PARTICLE_SIZE * max_particles as u64,
            wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_SRC | wgpu::BufferUsages::COPY_DST,
        );

        let particle_state_back = compute::create_buffer_empty(
            device,
            "physics_particle_state_back",
            Self::PARTICLE_SIZE * max_particles as u64,
            wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_SRC | wgpu::BufferUsages::COPY_DST,
        );

        let force_field_buffer = compute::create_buffer_empty(
            device,
            "physics_force_fields",
            Self::FORCE_FIELD_SIZE * max_force_fields as u64,
            wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
        );

        let emitter_buffer = compute::create_buffer_empty(
            device,
            "physics_emitters",
            Self::EMITTER_SIZE * max_emitters as u64,
            wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
        );

        let collision_plane_buffer = compute::create_buffer_empty(
            device,
            "physics_collision_planes",
            Self::COLLISION_PLANE_SIZE * max_collision_planes as u64,
            wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
        );

        let promotion_candidates = compute::create_buffer_empty(
            device,
            "physics_promotion_candidates",
            Self::PROMOTION_CANDIDATE_SIZE * max_promotion_candidates as u64,
            wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_SRC,
        );

        let indirect_draw = compute::create_buffer_empty(
            device,
            "physics_indirect_draw",
            16, // DrawIndirect: 4 * u32
            wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::INDIRECT,
        );

        let indirect_dispatch = compute::create_buffer_empty(
            device,
            "physics_indirect_dispatch",
            16, // DispatchIndirect: 4 * u32
            wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::INDIRECT,
        );

        let debug_stats = compute::create_buffer_empty(
            device,
            "physics_debug_stats",
            32, // 8 * u32
            wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_SRC | wgpu::BufferUsages::COPY_DST,
        );

        let debug_stats_readback = compute::create_buffer_empty(
            device,
            "physics_debug_stats_readback",
            32,
            wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST,
        );

        let alive_counter = compute::create_buffer_empty(
            device,
            "physics_alive_counter",
            4,
            wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
        );

        Self {
            particle_state,
            particle_state_back,
            force_field_buffer,
            emitter_buffer,
            collision_plane_buffer,
            promotion_candidates,
            indirect_draw,
            indirect_dispatch,
            debug_stats,
            debug_stats_readback,
            alive_counter,
            max_particles,
            max_force_fields,
            max_emitters,
            max_collision_planes,
            max_promotion_candidates,
        }
    }
}
