use crate::bxdfs::{
    CoatedConductorBxDF, CoatedDiffuseBxDF, ConductorBxDF, DielectricBxDF, DiffuseBxDF, HairBxDF,
};
use crate::core::bsdf::BSDF;
use crate::core::bssrdf::BSSRDF;
use crate::core::bxdf::BxDF;
use crate::core::image::Image;
use crate::core::material::{Material, MaterialEvalContext, MaterialTrait};
use crate::core::scattering::TrowbridgeReitzDistribution;
use crate::core::spectrum::{Spectrum, SpectrumTrait};
use crate::core::texture::{GPUFloatTexture, GPUSpectrumTexture, TextureEvaluator};
use crate::spectra::{SampledSpectrum, SampledWavelengths};
use crate::utils::Ptr;
use crate::utils::math::clamp;

#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct DielectricMaterial {
    pub normal_map: Ptr<Image>,
    pub displacement: Ptr<GPUFloatTexture>,
    pub u_roughness: Ptr<GPUFloatTexture>,
    pub v_roughness: Ptr<GPUFloatTexture>,
    pub eta: Ptr<Spectrum>,
    pub remap_roughness: bool,
}

impl MaterialTrait for DielectricMaterial {
    fn get_bsdf<T: TextureEvaluator>(
        &self,
        tex_eval: &T,
        ctx: &MaterialEvalContext,
        lambda: &SampledWavelengths,
    ) -> BSDF {
        let mut sampled_eta = self.eta.evaluate(lambda[0]);
        if !self.eta.is_constant() {
            lambda.terminate_secondary();
        }

        if sampled_eta == 0.0 {
            sampled_eta = 1.0;
        }

        let mut u_rough = tex_eval.evaluate_float(&self.u_roughness, ctx);
        let mut v_rough = tex_eval.evaluate_float(&self.v_roughness, ctx);

        if self.remap_roughness {
            u_rough = TrowbridgeReitzDistribution::roughness_to_alpha(u_rough);
            v_rough = TrowbridgeReitzDistribution::roughness_to_alpha(v_rough);
        }

        let distrib = TrowbridgeReitzDistribution::new(u_rough, v_rough);
        let bxdf = BxDF::Dielectric(DielectricBxDF::new(sampled_eta, distrib));

        BSDF::new(ctx.ns, ctx.dpdus, Ptr::from(&bxdf))
    }

    fn get_bssrdf<T>(
        &self,
        _tex_eval: &T,
        _ctx: &MaterialEvalContext,
        _lambda: &SampledWavelengths,
    ) -> Option<BSSRDF> {
        None
    }

    fn can_evaluate_textures(&self, tex_eval: &dyn TextureEvaluator) -> bool {
        tex_eval.can_evaluate(&[self.u_roughness, self.v_roughness], &[])
    }

    fn get_normal_map(&self) -> Option<&Image> {
        Some(&*self.normal_map)
    }

    fn get_displacement(&self) -> Ptr<GPUFloatTexture> {
        self.displacement
    }

    fn has_subsurface_scattering(&self) -> bool {
        false
    }
}

#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct ThinDielectricMaterial {
    pub displacement: Ptr<GPUFloatTexture>,
    pub normal_map: Ptr<Image>,
    pub eta: Ptr<Spectrum>,
}

impl MaterialTrait for ThinDielectricMaterial {
    fn get_bsdf<T: TextureEvaluator>(
        &self,
        _tex_eval: &T,
        _ctx: &MaterialEvalContext,
        _lambda: &SampledWavelengths,
    ) -> BSDF {
        todo!()
    }
    fn get_bssrdf<T>(
        &self,
        _tex_eval: &T,
        _ctx: &MaterialEvalContext,
        _lambda: &SampledWavelengths,
    ) -> Option<BSSRDF> {
        todo!()
    }

    fn can_evaluate_textures(&self, _tex_eval: &dyn TextureEvaluator) -> bool {
        true
    }

    fn get_normal_map(&self) -> Option<&Image> {
        Some(&*self.normal_map)
    }

    fn get_displacement(&self) -> Ptr<GPUFloatTexture> {
        self.displacement
    }

    fn has_subsurface_scattering(&self) -> bool {
        false
    }
}
