use std::sync::Arc;

use crate::camera::Camera;
use crate::cuboid::Cuboid;
use crate::flip_normals::FlipNormals;
use crate::hitable::Hit;
use crate::hitable_list::HitableList;
use crate::kdtree::KDTree;
use crate::material::DiffuseLight;
use crate::material::Lambertian;
use crate::rect::XYRect;
use crate::rect::XZRect;
use crate::rect::YZRect;
use crate::renderer::Opt;
use crate::renderer::Scene;
use crate::rotate::RotateY;
use crate::texture::ConstantTexture;
use crate::translate::Translate;
use crate::vec3::Vec3;

pub fn new(opt: &Opt) -> Scene {
    let lookfrom = Vec3::new(278., 278., -800.);
    let lookat = Vec3::new(278., 278., 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.),
        40.,
        opt.width as f32 / opt.height as f32,
        aperture,
        dist_to_focus,
        time_min,
        time_max,
    );

    let objects: Vec<Box<dyn Hit>> = vec![
        // Box1
        Box::new(Translate::new(
            RotateY::new(
                Cuboid::new(
                    Vec3::new(0., 0., 0.),
                    Vec3::new(165., 165., 165.),
                    Arc::new(Lambertian::new(ConstantTexture::new(Vec3::new(
                        0.73, 0.73, 0.73,
                    )))),
                ),
                -18.,
            ),
            Vec3::new(100., 0., 0.),
        )),
        // Box2
        Box::new(Translate::new(
            RotateY::new(
                Cuboid::new(
                    Vec3::new(0., 0., 0.),
                    Vec3::new(165., 330., 165.),
                    Arc::new(Lambertian::new(ConstantTexture::new(Vec3::new(
                        0.73, 0.73, 0.73,
                    )))),
                ),
                15.,
            ),
            Vec3::new(265., 0., 295.),
        )),
        // Green wall left
        Box::new(FlipNormals::new(YZRect::new(
            0.,
            555.,
            0.,
            555.,
            555.,
            Lambertian::new(ConstantTexture::new(Vec3::new(0.12, 0.45, 0.15))),
        ))),
        // Red floor right
        Box::new(YZRect::new(
            0.,
            555.,
            0.,
            555.,
            0.,
            Lambertian::new(ConstantTexture::new(Vec3::new(0.65, 0.05, 0.05))),
        )),
        // Light in ceiling
        Box::new(XZRect::new(
            213.,
            343.,
            227.,
            332.,
            554.,
            DiffuseLight::new(ConstantTexture::new(Vec3::new(15., 15., 15.))),
        )),
        // Grey ceiling
        Box::new(FlipNormals::new(XZRect::new(
            0.,
            555.,
            0.,
            555.,
            555.,
            Lambertian::new(ConstantTexture::new(Vec3::new(0.73, 0.73, 0.73))),
        ))),
        // Grey floor
        Box::new(XZRect::new(
            0.,
            555.,
            0.,
            555.,
            0.,
            Lambertian::new(ConstantTexture::new(Vec3::new(0.73, 0.73, 0.73))),
        )),
        // Grey back wall
        Box::new(FlipNormals::new(XYRect::new(
            0.,
            555.,
            0.,
            555.,
            555.,
            Lambertian::new(ConstantTexture::new(Vec3::new(0.73, 0.73, 0.73))),
        ))),
    ];
    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: false,
        ..Default::default()
    }
}
