use std::sync::Arc;

use crate::aabb::AABB;
use crate::flip_normals::FlipNormals;
use crate::hitable::Hit;
use crate::hitable::HitRecord;
use crate::hitable_list::HitableList;
use crate::material::Material;
use crate::ray::Ray;
use crate::rect::XYRect;
use crate::rect::XZRect;
use crate::rect::YZRect;
use crate::vec3::Vec3;

pub struct Cuboid {
    p_min: Vec3,
    p_max: Vec3,
    walls: HitableList,
}

impl Cuboid {
    // This clippy doesn't work right with Arc.
    #[allow(clippy::needless_pass_by_value)]
    pub fn new(p_min: Vec3, p_max: Vec3, material: Arc<dyn Material>) -> Cuboid {
        Cuboid {
            p_min,
            p_max,
            walls: HitableList::new(vec![
                Box::new(XYRect::new(
                    p_min.x,
                    p_max.x,
                    p_min.y,
                    p_max.y,
                    p_max.z,
                    Arc::clone(&material),
                )),
                Box::new(FlipNormals::new(XYRect::new(
                    p_min.x,
                    p_max.x,
                    p_min.y,
                    p_max.y,
                    p_min.z,
                    Arc::clone(&material),
                ))),
                Box::new(XZRect::new(
                    p_min.x,
                    p_max.x,
                    p_min.z,
                    p_max.z,
                    p_max.y,
                    Arc::clone(&material),
                )),
                Box::new(FlipNormals::new(XZRect::new(
                    p_min.x,
                    p_max.x,
                    p_min.z,
                    p_max.z,
                    p_min.y,
                    Arc::clone(&material),
                ))),
                Box::new(YZRect::new(
                    p_min.y,
                    p_max.y,
                    p_min.z,
                    p_max.z,
                    p_max.x,
                    Arc::clone(&material),
                )),
                Box::new(FlipNormals::new(YZRect::new(
                    p_min.y,
                    p_max.y,
                    p_min.z,
                    p_max.z,
                    p_min.x,
                    Arc::clone(&material),
                ))),
            ]),
        }
    }
}

impl Hit for Cuboid {
    fn hit(&self, r: Ray, t_min: f32, t_max: f32) -> Option<HitRecord> {
        self.walls.hit(r, t_min, t_max)
    }

    fn bounding_box(&self, _t_min: f32, _t_max: f32) -> Option<AABB> {
        Some(AABB::new(self.p_min, self.p_max))
    }
}
