use crate::hittable::Hittable;
use crate::ray::Ray;
use crate::rtweekend::degrees_to_radians;
use crate::vec3::*;

pub struct Camera {
    origin: Point3,
    lower_left_corner: Point3,
    horizontal: Vec3,
    vertical: Vec3,
    u: Vec3,
    v: Vec3,
    lens_radius: f64,
}

impl Camera {
    pub fn new(
        lookfrom: Point3,
        lookat: Point3,
        vup: Vec3,
        focal_length_mm: f64,
        aspect_ratio: f64,
        defocus_angle: f64,
        focus_dist: f64,
    ) -> Self {
        let sensor_height = 24.0; // in mm
        let theta = 2.0 * f64::atan(sensor_height / (2.0 * focal_length_mm));

        let h = f64::tan(theta / 2.0);
        let viewport_height = 2.0 * h;
        let viewport_width = aspect_ratio * viewport_height;

        let w = (lookfrom - lookat).unit_vector();
        let u = (vup.cross(&w)).unit_vector();
        let v = w.cross(&u);

        let origin = lookfrom;
        let horizontal = focus_dist * viewport_width * u;
        let vertical = focus_dist * viewport_height * v;
        let lower_left_corner = origin - horizontal / 2.0 - vertical / 2.0 - focus_dist * w;

        let defocus_theta = degrees_to_radians(defocus_angle);
        let lens_radius = focus_dist * f64::tan(defocus_theta / 2.0);

        Camera {
            origin,
            lower_left_corner,
            horizontal,
            vertical,
            u,
            v,
            lens_radius,
        }
    }

    pub fn get_ray(&self, s: f64, t: f64) -> Ray {
        let rd = self.lens_radius * random_in_unit_disk();
        let offset = self.u * rd.x() + self.v * rd.y();

        let ray_origin = self.origin + offset;

        Ray {
            origin: ray_origin,
            direction: self.lower_left_corner + s * self.horizontal + t * self.vertical
                - ray_origin,
        }
    }

    pub fn ray_color(&self, r: Ray, world: &dyn Hittable, depth: i32) -> Color {
        // Limit the bounces
        if depth <= 0 {
            return Color::new(0.0, 0.0, 0.0);
        }

        if let Some(rec) = world.hit(&r, 0.001, f64::INFINITY) {
            let emitted = rec.material.emitted();
            let mut scattered = Ray::new_empty();
            let mut attenuation = Color::new_empty();

            if rec
                .material
                .scatter(r, rec.clone(), &mut attenuation, &mut scattered)
            {
                return emitted + attenuation * self.ray_color(scattered, world, depth - 1);
            } else {
                return emitted;
            }
        }

        // Sky background
        // let unit_direction: Vec3 = r.direction().unit_vector();
        // let t = 0.5 * (unit_direction.y() + 1.0);
        //((1.0 - t) * Color::new(1.0, 1.0, 1.0)) + (t * Color::new(0.5, 0.7, 1.0))
        Color::new_empty()
    }
}
