use crate::globals::get_options;
use shared::core::interaction::{
    InteractionBase, InteractionTrait, MediumInteraction, SimpleInteraction, SurfaceInteraction,
};

pub trait InteractionGetter {
    fn get_bsdf(
        &mut self,
        r: &Ray,
        lambda: &SampledWavelengths,
        camera: &Camera,
        sampler: &mut Sampler,
    ) -> Option<BSDF> {
        self.compute_differentials(r, camera, sampler.samples_per_pixel() as i32);

        let material = {
            let root_mat = self.material;
            let mut active_mat: &Material = &*root_mat;
            let tex_eval = UniversalTextureEvaluator;
            while let Material::Mix(mix) = active_mat {
                // We need a context to evaluate the 'amount' texture
                let ctx = MaterialEvalContext::from(&*self);
                active_mat = mix.choose_material(&tex_eval, &ctx)?;
            }
            active_mat.clone()
        };

        let ctx = MaterialEvalContext::from(&*self);
        let tex_eval = UniversalTextureEvaluator;
        let displacement = material.get_displacement();
        let normal_map = Ptr::from(material.get_normal_map().unwrap());
        if !displacement.is_null() || !normal_map.is_null() {
            // This calls the function defined above
            self.compute_bump_geom(&tex_eval, displacement, normal_map);
        }

        let mut bsdf = material.get_bsdf(&tex_eval, &ctx, lambda);
        if get_options().force_diffuse {
            let r = bsdf.rho_wo(self.common.wo, &[sampler.get1d()], &[sampler.get2d()]);
            let diff_bxdf = BxDF::Diffuse(DiffuseBxDF::new(r));
            bsdf = BSDF::new(self.shading.n, self.shading.dpdu, Ptr::from(&diff_bxdf));
        }
        Some(bsdf)
    }

    fn get_bssrdf(
        &self,
        _ray: &Ray,
        lambda: &SampledWavelengths,
        _camera: &Camera,
    ) -> Option<BSSRDF> {
        let material = {
            let mut active_mat = unsafe { self.material.as_ref() };
            let tex_eval = UniversalTextureEvaluator;
            while let Material::Mix(mix) = active_mat {
                // We need a context to evaluate the 'amount' texture
                let ctx = MaterialEvalContext::from(self);
                active_mat = mix.choose_material(&tex_eval, &ctx)?;
            }
            active_mat.clone()
        };

        let ctx = MaterialEvalContext::from(self);
        let tex_eval = UniversalTextureEvaluator;
        material.get_bssrdf(&tex_eval, &ctx, lambda)
    }
}

impl InteractionGetter for SurfaceInteraction {}
impl InteractionGetter for MediumInteraction {}
impl InteractionGetter for SimpleInteraction {}
