use ash::vk::Image;
use shared::cameras::{Mapping, RealisticCamera, realistic::LensElementInterface};

struct RealisticCameraData {
    aperture_image: Arc<Image>,
    element_interfaces: Vec<LensElementInterface>,
}

pub struct RealisticCameraHost {
    device: RealisticCamera,
    data: RealisticCameraData,
}

impl RealisticCameraHost {
    pub fn new(
        base: CameraBase,
        lens_params: &[Float],
        focus_distance: Float,
        set_aperture_diameter: Float,
        aperture_image: Arc<Image>,
    ) -> Self {
        let film_ptr = base.film;
        if film_ptr.is_null() {
            panic!("Camera must have a film");
        }
        let film = &*film_ptr;

        let aspect = film.full_resolution().x() as Float / film.full_resolution().y() as Float;
        let diagonal = film.diagonal();
        let x = (square(diagonal) / (1.0 + square(diagonal))).sqrt();
        let y = x * aspect;
        let physical_extent =
            Bounds2f::from_points(Point2f::new(-x / 2., -y / 2.), Point2f::new(x / 2., y / 2.));
        let mut element_interface: Vec<LensElementInterface> = Vec::new();

        for i in (0..lens_params.len()).step_by(4) {
            let curvature_radius = lens_params[i] / 1000.0;
            let thickness = lens_params[i + 1] / 1000.0;
            let eta = lens_params[i + 2];
            let mut aperture_diameter = lens_params[i + 3] / 1000.0;

            if curvature_radius == 0.0 {
                aperture_diameter /= 1000.0;
                if set_aperture_diameter > aperture_diameter {
                    println!("Aperture is larger than possible")
                } else {
                    aperture_diameter = set_aperture_diameter;
                }
            }
            let el_int = LensElementInterface {
                curvature_radius,
                thickness,
                eta,
                aperture_radius: aperture_diameter / 2.0,
            };
            element_interface.push(el_int);
        }

        let half_diag = film.diagonal() / 2.0;
        let mut exit_pupil_bounds = [Bounds2f::default(); EXIT_PUPIL_SAMPLES];

        for i in 0..EXIT_PUPIL_SAMPLES {
            let r0 = (i as Float / EXIT_PUPIL_SAMPLES as Float) * half_diag;
            let r1 = ((i + 1) as Float / EXIT_PUPIL_SAMPLES as Float) * half_diag;
            exit_pupil_bounds[i] = Self::compute_exit_pupil_bounds(&element_interface, r0, r1);
        }

        let n_elements = element_interface.len();
        let element_interfaces = element_interface.as_ptr();
        std::mem::forget(element_interface);

        let data = RealisticCameraData {
            element_interfaces,
            aperture_image,
        };

        let device = RealisticCamera {
            base,
            focus_distance,
            element_interfaces: Ptr::from(element_interfaces),
            n_elements,
            physical_extent,
            set_aperture_diameter,
            aperture_image,
            exit_pupil_bounds,
        };

        Self { device, data }
    }
}
