use ray_tracing_core::{camera::Camera, prelude::*, renderer::ClassicalRenderer, scene::Scene};

pub struct PathTracer {
    width: u32,
    height: u32,
}

impl PathTracer {
    pub fn new(width: u32, height: u32) -> Self {
        Self { width, height }
    }
}

impl<S, C, R> ClassicalRenderer<R, S, C> for PathTracer
where
    S: Scene<R>,
    C: Camera<R>,
    R: Rng,
{
    fn render_pixel(&self, scene: &S, camera: &C, x: u32, y: u32, rng: &mut R) -> Color {
        let mut sum = Color::black();
        let mut alpha = Color::white();

        let mut r = camera.forward(x, y, rng);

        let mut count = 0;

        while let Some(i) = scene.intersect(r, 0.001, Float::INFINITY) {
            let frame = i.tangent_frame();
            let w_in = frame.to_frame(-r.dir());
            let w_out = Dir3::sample_uniform_hemisphere(rng);

            if let Some(light) = i.light() {
                sum += alpha * light.emit(w_in, rng);
            }

            if let Some(material) = i.material() {
                alpha *= material.eval(w_in, w_out, rng) * w_out.y() * 2.0 * FloatConsts::PI;
            } else {
                return sum;
            }

            r = Ray::new(r.at(i.t()), frame.to_world(w_out), r.time());
            count += 1;
            if count > 4 {
                break;
            }
        }

        sum
    }

    fn width(&self) -> u32 {
        self.width
    }

    fn height(&self) -> u32 {
        self.height
    }
}
