use super::state::PathState;
use shared::core::geometry::Ray;
use shared::core::interaction::{Interaction, InteractionTrait};
use shared::core::light::{Light, LightTrait};
use shared::core::primitive::{Primitive, PrimitiveTrait};
use shared::core::shape::ShapeIntersection;
use shared::lights::sampler::{LightSampler, LightSamplerTrait};
use shared::spectra::SampledWavelengths;
use shared::utils::sampling::power_heuristic;
use shared::{Float, SHADOW_EPSILON};
use std::sync::Arc;

#[derive(Clone, Debug)]
pub struct IntegratorBase {
    pub aggregate: Arc<Primitive>,
    pub lights: Vec<Arc<Light>>,
    pub infinite_lights: Vec<Arc<Light>>,
}

impl IntegratorBase {
    pub fn new(aggregate: Arc<Primitive>, lights: Vec<Arc<Light>>) -> Self {
        let infinite_lights = lights
            .iter()
            .filter(|light| light.light_type().is_infinite())
            .cloned()
            .collect();

        Self {
            aggregate,
            lights,
            infinite_lights,
        }
    }

    pub fn intersect(&self, ray: &Ray, t_max: Option<Float>) -> Option<ShapeIntersection> {
        self.aggregate.intersect(ray, t_max)
    }

    pub fn intersect_p(&self, ray: &Ray, t_max: Option<Float>) -> bool {
        self.aggregate.intersect_p(ray, t_max)
    }

    pub fn unoccluded(&self, p0: &Interaction, p1: &Interaction) -> bool {
        !self.intersect_p(
            &p0.spawn_ray_to_interaction(*p1.get_common()),
            Some(1. - SHADOW_EPSILON),
        )
    }

    pub fn add_infinite_light_contribution(
        &self,
        state: &mut PathState,
        ray: &Ray,
        lambda: &SampledWavelengths,
        light_sampler: Option<&LightSampler>,
        use_mis: bool,
    ) {
        for light in &self.infinite_lights {
            let le = light.le(ray, lambda);
            if le.is_black() {
                continue;
            }

            if state.depth == 0 || state.specular_bounce || !use_mis {
                state.l += state.beta * le;
            } else if let Some(sampler) = light_sampler {
                let p_l = sampler.pmf_with_context(&state.prev_ctx, light)
                    * light.pdf_li(&state.prev_ctx, ray.d, true);
                let w_b = power_heuristic(1, state.prev_pdf, 1, p_l);
                state.l += state.beta * w_b * le;
            }
        }
    }
}
