use std::sync::Arc;

use rand;

use crate::camera::Camera;
use crate::hitable::Hit;
use crate::hitable_list::HitableList;
use crate::kdtree::KDTree;
use crate::material::Lambertian;
use crate::noise::perlin::Perlin;
use crate::noise::NoiseType;
use crate::renderer::Opt;
use crate::renderer::Scene;
use crate::sphere::Sphere;
use crate::texture::NoiseTexture;
use crate::texture::Texture;
use crate::vec3::Vec3;

pub fn new(opt: &Opt) -> Scene {
    let lookfrom = Vec3::new(13., 2., 3.);
    let lookat = Vec3::new(0., 0., 0.);
    let dist_to_focus = 10.0;
    let aperture = 0.0;
    let time_min = 0.;
    let time_max = 1.;
    let camera = Camera::new(
        lookfrom,
        lookat,
        Vec3::new(0., 1., 0.),
        20.,
        opt.width as f32 / opt.height as f32,
        aperture,
        dist_to_focus,
        time_min,
        time_max,
    );
    let rng = &mut rand::thread_rng();
    let noise_source = Perlin::new(rng);
    let noise_type = NoiseType::Marble {
        period: Vec3::new(0., 1., 0.),
        power: 4.,
        size: 32,
        scale: 0.05,
    };
    let pertext: Arc<dyn Texture> = Arc::new(NoiseTexture::new(noise_source, noise_type));

    let objects: Vec<Box<dyn Hit>> = vec![
        Box::new(Sphere::new(
            Vec3::new(0., -1000., 0.),
            1000.,
            Lambertian::new(Arc::clone(&pertext)),
        )),
        Box::new(Sphere::new(
            Vec3::new(0., 2., 0.),
            2.0,
            Lambertian::new(Arc::clone(&pertext)),
        )),
    ];
    let world: Box<dyn Hit> = if opt.use_accel {
        Box::new(KDTree::new(objects, time_min, time_max))
    } else {
        Box::new(HitableList::new(objects))
    };
    Scene {
        camera,
        world,
        subsamples: opt.subsamples,
        num_threads: opt.num_threads,
        width: opt.width,
        height: opt.height,
        global_illumination: true,
        ..Default::default()
    }
}
