use ray_tracing_core::light::AreaLight;
use ray_tracing_core::prelude::*;
use ray_tracing_core::scene::{Intersection, Scene};
use ray_tracing_material::default::DefaultMaterial;

#[derive(Clone)]
pub struct BasicScene<M> {
    pub(crate) spheres: Vec<(Pos3, Float)>,
    area_light: AreaLight,
    material: M,
}

impl<M> BasicScene<M> {
    pub fn new(material: M) -> Self {
        Self {
            spheres: vec![(Pos3::zero(), 1.0), (Pos3::new(0.0, -4.5, 0.0), 4.0)],
            area_light: AreaLight::new(Color::white()),
            material,
        }
    }
}

impl Default for BasicScene<DefaultMaterial> {
    fn default() -> Self {
        Self::new(DefaultMaterial {})
    }
}

impl<R: Rng, M: Material<R>> Scene<R> for BasicScene<M> {
    fn intersect(&self, ray: Ray, min: Float, max: Float) -> Option<Intersection<'_, R>> {
        let mut intersection: Option<Intersection<'_, R>> = None;

        for &(c, r) in &self.spheres {
            let offset = ray.start() - c;
            let p = Dir3::dot(ray.dir(), offset);
            let delta = p * p - (offset.length_squared() - r * r);

            if delta >= 0.0 {
                let d = -p - Float::sqrt(delta);

                let int = Intersection::new(
                    d,
                    ((ray.start() + d * ray.dir()) - c).normalize(),
                    Some(&self.material),
                    None,
                    0.0,
                );
                if d >= min && d <= max {
                    if let Some(i) = intersection.as_ref() {
                        if i.t() > d {
                            intersection.replace(int);
                        }
                    } else {
                        intersection = Some(int)
                    }
                }
            }
        }

        Some(intersection.unwrap_or(Intersection::new(
            Float::INFINITY,
            -ray.dir(),
            None,
            Some(&self.area_light),
            0.0,
        )))
    }

    fn sample_light(
        &self,
        _w_in: Dir3,
        _intersection: &Intersection<'_, R>,
        _rng: &mut R,
    ) -> Option<ray_tracing_core::scene::LightSample<'_, R>> {
        None
    }
}
