use crate::core::image::Image;
use crate::core::material::CreateMaterial;
use crate::core::texture::SpectrumTexture;
use crate::spectra::get_colorspace_device;
use crate::utils::TextureParameterDictionary;
use crate::{Arena, FileLoc, Upload, ArenaUpload};
use anyhow::Result;
use shared::bxdfs::HairBxDF;
use shared::core::material::Material;
use shared::core::spectrum::Spectrum;
use shared::core::texture::SpectrumType;
use shared::materials::complex::*;
use shared::textures::SpectrumConstantTexture;
use std::collections::HashMap;
use std::sync::Arc;

impl CreateMaterial for HairMaterial {
    fn create(
        parameters: &TextureParameterDictionary,
        _normal_map: Option<Arc<Image>>,
        _named_materials: &HashMap<String, Material>,
        _loc: &FileLoc,
        arena: &Arena,
    ) -> Result<Material> {
        let sigma_a = parameters.get_spectrum_texture_or_null("sigma_a", SpectrumType::Unbounded);
        let reflectance = parameters
            .get_spectrum_texture_or_null("reflectance", SpectrumType::Albedo)
            .or_else(|| parameters.get_spectrum_texture_or_null("color", SpectrumType::Albedo));
        let eumelanin = parameters.get_float_texture_or_null("eumelanin")?;
        let pheomelanin = parameters.get_float_texture_or_null("pheomelanin")?;
        let has_melanin = eumelanin.is_some() || pheomelanin.is_some();

        let sigma_a = if sigma_a.is_none() && reflectance.is_none() && !has_melanin {
            let stdcs = get_colorspace_device();
            let default_rgb = HairBxDF::sigma_a_from_concentration(1.3, 0.0, stdcs);
            let spectrum = Spectrum::RGBUnbounded(default_rgb);
            let texture = SpectrumTexture::Constant(SpectrumConstantTexture::new(spectrum));
            Some(Arc::new(texture))
        } else {
            sigma_a
        };

        let eta = parameters.get_float_texture("eta", 1.55)?;
        let beta_m = parameters.get_float_texture("beta_m", 0.3)?;
        let beta_n = parameters.get_float_texture("beta_n", 0.3)?;
        let alpha = parameters.get_float_texture("alpha", 2.)?;

        let material = HairMaterial::new(
            arena.upload(sigma_a),
            arena.upload(reflectance),
            arena.upload(eumelanin),
            arena.upload(pheomelanin),
            arena.upload(eta),
            arena.upload(beta_m),
            arena.upload(beta_n),
            arena.upload(alpha),
        );

        Ok(Material::Hair(material))
    }
}


impl CreateMaterial for SubsurfaceMaterial {
    fn create(
        _parameters: &TextureParameterDictionary,
        _normal_map: Option<Arc<Image>>,
        _named_materials: &HashMap<String, Material>,
        _loc: &FileLoc,
        _arena: &Arena,
    ) -> Result<Material> {
        todo!()
    }
}

impl CreateMaterial for MeasuredMaterial {
    fn create(
        _parameters: &TextureParameterDictionary,
        _normal_map: Option<Arc<Image>>,
        _named_materials: &HashMap<String, Material>,
        _loc: &FileLoc,
        _arena: &Arena,
    ) -> Result<Material> {
        todo!()
    }
}
