// camera.rs

use nalgebra::*;

use crate::renderer::{Ray,Intersection};
use crate::elements::*;


enum Camera {
    OrthoCamera(OrthoCamera),
    PerspectiveCamera(PerspectiveCamera),
}

// TODO: Create a separate scene object
pub trait RaySource {
    fn trace(&self, ray: &Ray) -> Option<Intersection>;
}

pub struct OrthoCamera {
  pub pos: Vec3<f64>,
  pub output_img: bmp::Image,
  pub elements: Vec<Element>, 
  pub lights: Vec<LightSrc>,
  //spheres: Vec<Sphere>,
  //light: LightSrc,

  pub shadow_bias: f64,
  pub max_recursion_depth: u32
}

impl RaySource for OrthoCamera {
  fn trace(&self, ray: &Ray) -> Option<Intersection> {
    self.elements.iter()
      .filter_map(|s| s.intersect(ray).map(|d| Intersection::new(d, s) ))
      .min_by(|i1, i2| i1.distance.partial_cmp(&i2.distance).unwrap())
  }
}

pub struct PerspectiveCamera {
    pub pos: Vec3<f64>,
    pub output_img: bmp::Image,
    pub elements: Vec<Element>,
    pub lights: Vec<LightSrc>,
    pub shadow_bias: f64,
    pub max_recursion_depth: u32,
    pub fov: f64,
    pub scene_width: u32,
    pub scene_height: u32,
}

impl PerspectiveCamera {
  pub fn create_prime(&self, x: u32, y: u32) -> Ray {
      let sensor_x = ((x as f64 + 0.5) / self.scene_width as f64) * 2.0 - 1.0;
      let sensor_y = 1.0 - ((y as f64 + 0.5) / self.scene_height as f64) * 2.0;

      Ray {
        pos: self.pos,
        dir: Vec3::new(sensor_x, sensor_y, 1.0).normalize(),
      }
  }

  pub fn percent_complete(&self, row: u32) -> f64 {
      let float_row = row as f64;
      let float_scene_height = self.scene_height as f64;
      let float_scene_width = self.scene_width as f64;

      ((float_row * float_scene_width) / (float_scene_width * float_scene_height)) * 100.0
  }
}

impl RaySource for PerspectiveCamera {
  fn trace(&self, ray: &Ray) -> Option<Intersection> {
    self.elements.iter()
      .filter_map(|s| s.intersect(ray).map(|d| Intersection::new(d, s) ))
      .min_by(|i1, i2| i1.distance.partial_cmp(&i2.distance).unwrap())
  }
}


