use super::*;
use crate::Arena;
use crate::core::film::{CreateFilmBase, PixelSensor};
use crate::utils::containers::Array2D;
use anyhow::Result;
use shared::core::camera::CameraTransform;
use shared::core::film::{Film, FilmBase, RGBFilm, RGBPixel};
use shared::core::filter::FilterTrait;
use shared::spectra::RGBColorSpace;

struct RGBFilmStorage {
    pixels: Array2D<RGBPixel>,
}

pub struct RGBFilmHost {
    pub device: RGBFilm,
    storage: RGBFilmStorage,
}

impl RGBFilmHost {
    pub fn new(
        base: FilmBase,
        colorspace: &RGBColorSpace,
        max_component_value: Float,
        write_fp16: bool,
    ) -> Self {
        let sensor_ptr = base.sensor;
        if sensor_ptr.is_null() {
            panic!("Film must have a sensor");
        }
        let sensor = &*sensor_ptr;
        let filter_integral = base.filter.integral();
        let sensor_matrix = sensor.xyz_from_sensor_rgb;
        let output_rgbf_from_sensor_rgb = colorspace.rgb_from_xyz * sensor_matrix;

        let width = base.pixel_bounds.p_max.x() - base.pixel_bounds.p_min.x();
        let height = base.pixel_bounds.p_max.y() - base.pixel_bounds.p_min.y();
        let count = (width * height) as usize;

        let mut pixel_vec = Vec::with_capacity(count);
        for _ in 0..count {
            pixel_vec.push(RGBPixel::default());
        }

        let pixels: Array2D<RGBPixel> = Array2D::new(base.pixel_bounds);

        let device_pixels = pixels.device.clone();
        let storage = RGBFilmStorage { pixels };

        let device = RGBFilm {
            base,
            max_component_value,
            write_fp16,
            filter_integral,
            output_rgbf_from_sensor_rgb,
            pixels: device_pixels,
        };

        Self { device, storage }
    }
}

impl CreateFilm for RGBFilm {
    fn create(
        params: &ParameterDictionary,
        exposure_time: Float,
        filter: Filter,
        _camera_transform: Option<CameraTransform>,
        loc: &FileLoc,
        _arena: &Arena,
    ) -> Result<Film> {
        let colorspace = params.color_space.as_ref().unwrap();
        let max_component_value = params.get_one_float("maxcomponentvalue", Float::INFINITY)?;
        let write_fp16 = params.get_one_bool("savefp16", true)?;
        let sensor = PixelSensor::create(params, colorspace.clone(), exposure_time, loc)?;
        let film_base = FilmBase::create(params, filter, Some(&sensor.device()), loc)?;
        let film = RGBFilmHost::new(film_base, &colorspace, max_component_value, write_fp16);
        Ok(Film::RGB(film.device))
    }
}
