//! Scene light management -- collects light descriptors for GPU upload.

use dreamwell_engine::lighting::{DirectionalLightDesc, PointLightDesc, SpotLightDesc};

/// Collected scene lights for a frame. Passed to render passes.
#[derive(Debug, Clone, Default)]
pub struct SceneLights {
    /// Directional lights (sun, moon). Typically 1-2.
    pub directional: Vec<DirectionalLightDesc>,
    /// Point lights. Bounded per scene.
    pub point: Vec<PointLightDesc>,
    /// Spot lights. Bounded per scene.
    pub spot: Vec<SpotLightDesc>,
}

impl SceneLights {
    /// Maximum lights per category to prevent GPU buffer overflow.
    pub const MAX_DIRECTIONAL: usize = 4;
    /// Maximum point lights per scene.
    pub const MAX_POINT: usize = 64;
    /// Maximum spot lights per scene.
    pub const MAX_SPOT: usize = 32;

    /// Create empty light set.
    pub fn new() -> Self {
        Self::default()
    }

    /// Add a directional light. Returns false if at capacity.
    pub fn add_directional(&mut self, light: DirectionalLightDesc) -> bool {
        if self.directional.len() >= Self::MAX_DIRECTIONAL {
            return false;
        }
        self.directional.push(light);
        true
    }

    /// Add a point light. Returns false if at capacity.
    pub fn add_point(&mut self, light: PointLightDesc) -> bool {
        if self.point.len() >= Self::MAX_POINT {
            return false;
        }
        self.point.push(light);
        true
    }

    /// Add a spot light. Returns false if at capacity.
    pub fn add_spot(&mut self, light: SpotLightDesc) -> bool {
        if self.spot.len() >= Self::MAX_SPOT {
            return false;
        }
        self.spot.push(light);
        true
    }

    /// Total light count across all categories.
    pub fn total_count(&self) -> usize {
        self.directional.len() + self.point.len() + self.spot.len()
    }

    /// Clear all lights for frame rebuild.
    pub fn clear(&mut self) {
        self.directional.clear();
        self.point.clear();
        self.spot.clear();
    }
}

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

    #[test]
    fn empty_by_default() {
        let l = SceneLights::new();
        assert_eq!(l.total_count(), 0);
    }

    #[test]
    fn add_directional() {
        let mut l = SceneLights::new();
        assert!(l.add_directional(DirectionalLightDesc::default()));
        assert_eq!(l.total_count(), 1);
    }

    #[test]
    fn directional_capacity() {
        let mut l = SceneLights::new();
        for _ in 0..SceneLights::MAX_DIRECTIONAL {
            assert!(l.add_directional(DirectionalLightDesc::default()));
        }
        assert!(!l.add_directional(DirectionalLightDesc::default()));
    }

    #[test]
    fn clear_resets() {
        let mut l = SceneLights::new();
        l.add_directional(DirectionalLightDesc::default());
        l.add_point(PointLightDesc::default());
        l.clear();
        assert_eq!(l.total_count(), 0);
    }
}
