use rand::Rng;

use crate::{Point3, Ray, Vec3};

pub struct Camera {
    aspect_ratio: f64,
    viewport_height: f64,
    viewport_width: f64,
    origin: Point3,
    horizontal: Vec3,
    vertical: Vec3,
    lower_left_corner: Vec3,
    lens_radius: f64,
    u: Vec3,
    v: Vec3,
    w: Vec3,
    time0: f64,
    time1: f64
}

impl Camera {
    pub fn get_ray(&self, s: f64, t: f64) -> Ray {
        let rd = self.lens_radius * Vec3::random_unit_disk();
        let offset = self.u * rd.x() + self.v * rd.y();
        let time = rand::thread_rng().gen_range(self.time0..self.time1);
        Ray::new(
            self.origin + offset,
            self.lower_left_corner + s*self.horizontal + t*self.vertical - self.origin - offset,
            time)
    }
    pub fn still(
        look_from: Point3,
        look_at: Point3,
        up: Vec3,
        aspect_ratio: f64,
        vfov: f64,
        aperture: f64,
        focus_dist: f64) -> Self {

        Camera::new(
            look_from,
            look_at,
            up,
            aspect_ratio,
            vfov,
            aperture,
            focus_dist,
            0.0,
            0.0)
    }

    pub fn new(
        look_from: Point3,
        look_at: Point3,
        up: Vec3,
        aspect_ratio: f64,
        vfov: f64,
        aperture: f64,
        focus_dist: f64,
        time0: f64,
        time1: f64) -> Self {

        let theta = vfov.to_radians();
        let h = (theta / 2.0).tan();
        let viewport_height = 2.0 * h;
        let viewport_width = aspect_ratio * viewport_height;

        let w = (look_from - look_at).unit_vector();
        let u = up.cross(&w).unit_vector();
        let v = w.cross(&u);

        let horizontal = focus_dist * viewport_width * u;
        let vertical = focus_dist * viewport_height * v;
        Camera {
            aspect_ratio,
            viewport_height,
            viewport_width,
            origin: look_from,
            horizontal,
            vertical,
            lower_left_corner: look_from - horizontal/2.0 - vertical/2.0 - focus_dist*w,
            lens_radius: aperture / 2.0,
            u,
            v,
            w,
            time0,
            time1
        }
    }

    pub fn aspect_ratio(&self) -> f64 { self.aspect_ratio }
}