use std;

use crate::aabb::surrounding_box;
use crate::aabb::AABB;
use crate::hitable::Hit;
use crate::hitable::HitRecord;
use crate::ray::Ray;
use crate::vec3::Vec3;

#[derive(Default)]
pub struct HitableList {
    list: Vec<Box<dyn Hit>>,
}

impl HitableList {
    pub fn new(list: Vec<Box<dyn Hit>>) -> HitableList {
        HitableList { list }
    }
}

impl Hit for HitableList {
    fn hit(&self, r: Ray, t_min: f32, t_max: f32) -> Option<HitRecord> {
        let mut min_hit = None;
        let mut closest_so_far = t_max;
        for hitable in &self.list {
            if let Some(h) = hitable.hit(r, t_min, closest_so_far) {
                closest_so_far = h.t;
                min_hit = Some(h);
            }
        }
        min_hit
    }

    fn bounding_box(&self, t_min: f32, t_max: f32) -> Option<AABB> {
        // TODO(wathiede): I don't understand the version from the book, this implementation
        // returns a superset of all bounding boxes.
        let super_box = AABB::new(
            Vec3::new(std::f32::MAX, std::f32::MAX, std::f32::MAX),
            Vec3::new(std::f32::MIN, std::f32::MIN, std::f32::MIN),
        );
        Some(self.list.iter().fold(super_box, |acc, hitable| {
            match hitable.bounding_box(t_min, t_max) {
                Some(bbox) => surrounding_box(&acc, &bbox),
                None => acc,
            }
        }))
    }
}
