use mem_arena::MemArena;

use crate::{
    bbox::BBox,
    math::{Normal, Point, Vector},
    shading::surface_closure::SurfaceClosure,
};

use super::LightAccel;

#[derive(Debug, Copy, Clone)]
pub struct LightArray<'a> {
    indices: &'a [usize],
    aprx_energy: f32,
}

impl<'a> LightArray<'a> {
    #[allow(dead_code)]
    pub fn from_objects<'b, T, F>(
        arena: &'a MemArena,
        objects: &mut [T],
        info_getter: F,
    ) -> LightArray<'a>
    where
        F: 'b + Fn(&T) -> (&'b [BBox], f32),
    {
        let mut indices = Vec::new();
        let mut aprx_energy = 0.0;
        for (i, thing) in objects.iter().enumerate() {
            let (_, power) = info_getter(thing);
            if power > 0.0 {
                indices.push(i);
                aprx_energy += power;
            }
        }

        LightArray {
            indices: arena.copy_slice(&indices),
            aprx_energy: aprx_energy,
        }
    }
}

impl<'a> LightAccel for LightArray<'a> {
    fn select(
        &self,
        inc: Vector,
        pos: Point,
        nor: Normal,
        nor_g: Normal,
        sc: &SurfaceClosure,
        time: f32,
        n: f32,
    ) -> Option<(usize, f32, f32)> {
        let _ = (inc, pos, nor, nor_g, sc, time); // Not using these, silence warnings

        assert!(n >= 0.0 && n <= 1.0);

        if self.indices.is_empty() {
            return None;
        }

        let n2 = n * self.indices.len() as f32;
        let i = if n == 1.0 {
            *self.indices.last().unwrap()
        } else {
            self.indices[n2 as usize]
        };

        let whittled_n = n2 - i as f32;
        let pdf = 1.0 / self.indices.len() as f32;

        Some((i, pdf, whittled_n))
    }

    fn approximate_energy(&self) -> f32 {
        self.aprx_energy
    }
}
