use crate::camera::Camera;
use crate::hitable::Hit;
use crate::hitable_list::HitableList;
use crate::material::Dielectric;
use crate::material::Lambertian;
use crate::material::Metal;
use crate::moving_sphere::MovingSphere;
use crate::rect::XYRect;
use crate::renderer::Opt;
use crate::renderer::Scene;
use crate::sphere::Sphere;
use crate::texture::CheckerTexture;
use crate::texture::ConstantTexture;
use crate::texture::EnvMap;
use crate::vec3::Vec3;

// Draws many spheres along each positive axis.
// Blue X-positive
// Green Y-positive
// Red Z-positive
fn draw_axises() -> Vec<Box<dyn Hit>> {
    let half_diameter = 0.1;
    let x = (0..100).map(|i| -> Box<dyn Hit> {
        // X-axis
        Box::new(Sphere::new(
            Vec3::new(i as f32 * 2. * half_diameter, 0., 0.),
            half_diameter,
            Lambertian::new(ConstantTexture::new([0., 0., 1.])),
        ))
    });
    let y = (0..100).map(|i| -> Box<dyn Hit> {
        // Y-axis
        Box::new(Sphere::new(
            Vec3::new(0., i as f32 * 2. * half_diameter, 0.),
            half_diameter,
            Lambertian::new(ConstantTexture::new([0., 1., 0.])),
        ))
    });
    let z = (0..100).map(|i| -> Box<dyn Hit> {
        // Z-axis
        Box::new(Sphere::new(
            Vec3::new(0., 0., i as f32 * 2. * half_diameter),
            half_diameter,
            Lambertian::new(ConstantTexture::new([1., 0., 0.])),
        ))
    });
    x.chain(y.chain(z)).collect()
}

pub fn new(opt: &Opt) -> Scene {
    let lookfrom = Vec3::new(10., 10., 20.);
    let lookat = Vec3::new(0., 0., 0.);
    let dist_to_focus = (lookfrom - lookat).length();
    let aperture = 0.1;
    let time_min = 0.;
    let time_max = 0.;
    let camera = Camera::new(
        lookfrom,
        lookat,
        Vec3::new(0., 0., 1.),
        45.,
        opt.width as f32 / opt.height as f32,
        aperture,
        dist_to_focus,
        time_min,
        time_max,
    );
    let ground_color = CheckerTexture::new(
        ConstantTexture::new([0., 0., 0.]),
        ConstantTexture::new([1.0, 1.0, 1.0]),
    );

    let mut objects: Vec<Box<dyn Hit>> = vec![
        Box::new(XYRect::new(
            -10.,
            10.,
            -10.,
            10.,
            0.,
            Lambertian::new(ground_color),
        )),
        Box::new(Sphere::new([1., 0.5, 1.], 0.5, Dielectric::new(1.5))),
        Box::new(Sphere::new(
            Vec3::new(0., 0., 0.5),
            0.5,
            Lambertian::new(ConstantTexture::new(Vec3::new(0.1, 0.2, 0.5))),
        )),
        Box::new(Sphere::new(
            Vec3::new(1., 0., -1.),
            0.5,
            Metal::new(Vec3::new(0.8, 0.6, 0.2), 0.2),
        )),
        Box::new(MovingSphere::new(
            Vec3::new(-1., 0., -1.25),
            Vec3::new(-1., 0., -0.75),
            0.5,
            0.,
            1.,
            Lambertian::new(ConstantTexture::new(Vec3::new(0.2, 0.8, 0.2))),
        )),
    ];
    objects.extend(draw_axises());
    //let world = Box::new(KDTree::new(objects, time_min, time_max));
    let world = Box::new(HitableList::new(objects));
    let skybox_bytes = include_bytes!("../../images/envmap.jpg");
    let skybox = image::load_from_memory(skybox_bytes).unwrap().to_rgb();
    Scene {
        camera,
        world,
        subsamples: opt.subsamples,
        adaptive_subsampling: None,
        // adaptive_subsampling: Some(0.5),
        num_threads: opt.num_threads,
        width: opt.width,
        height: opt.height,
        global_illumination: true,
        env_map: Some(EnvMap::new(skybox)),
        ..Default::default()
    }
}
