use log::trace;
use rand;
use rand::Rng;

use crate::bvh::BVH;
use crate::camera::Camera;
use crate::hitable::Hit;
use crate::hitable_list::HitableList;
use crate::kdtree::KDTree;
use crate::material::Lambertian;
use crate::renderer::Opt;
use crate::renderer::Scene;
use crate::sphere::Sphere;
use crate::texture::ConstantTexture;
use crate::vec3::Vec3;

pub fn new(opt: &Opt) -> Scene {
    let lookfrom = Vec3::new(20., 20., 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 = 1.;
    let camera = Camera::new(
        lookfrom,
        lookat,
        Vec3::new(0., 1., 0.),
        70.,
        opt.width as f32 / opt.height as f32,
        aperture,
        dist_to_focus,
        time_min,
        time_max,
    );
    let mut rng = rand::thread_rng();
    let mut grid: Vec<Box<dyn Hit>> = Vec::new();
    let len = 1000;
    for x in 0..len {
        for z in 0..len {
            let r = rng.gen();
            let g = rng.gen();
            let b = rng.gen();

            let x_pos = (x - len / 2) as f32 + rng.gen_range(-0.1..0.1);
            let z_pos = (z - len / 2) as f32 + rng.gen_range(-0.1..0.1);

            grid.push(Box::new(Sphere::new(
                Vec3::new(x_pos, 0., z_pos),
                0.5,
                Lambertian::new(ConstantTexture::new(Vec3::new(r, g, b))),
            )));
        }
    }
    let world: Box<dyn Hit>;
    if opt.use_accel {
        let use_kd = true;
        if use_kd {
            let kdtree = KDTree::new(grid, time_min, time_max);
            trace!("k-d tree world {}", kdtree);
            world = Box::new(kdtree);
        } else {
            let bvh = BVH::new(grid, time_min, time_max);
            trace!("BVH world {}", bvh);
            world = Box::new(bvh);
        }
    } else {
        world = Box::new(HitableList::new(grid));
    }
    Scene {
        camera,
        world,
        subsamples: opt.subsamples,
        num_threads: opt.num_threads,
        width: opt.width,
        height: opt.height,
        ..Default::default()
    }
}
