use serde::{Deserialize, Serialize};

use crate::ecs::material::components::{AlphaMode, Material};

use super::asset_uuid::AssetUuid;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SceneMaterial {
    pub base_color: [f32; 4],
    pub emissive_factor: [f32; 3],
    #[serde(default, skip_serializing_if = "is_default_alpha_mode")]
    pub alpha_mode: SceneAlphaMode,
    #[serde(
        default = "default_alpha_cutoff",
        skip_serializing_if = "is_default_alpha_cutoff"
    )]
    pub alpha_cutoff: f32,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub base_texture_uuid: Option<AssetUuid>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub base_texture_name: Option<String>,
    #[serde(default, skip_serializing_if = "is_zero_u32")]
    pub base_texture_uv_set: u32,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub emissive_texture_uuid: Option<AssetUuid>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub emissive_texture_name: Option<String>,
    #[serde(default, skip_serializing_if = "is_zero_u32")]
    pub emissive_texture_uv_set: u32,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub normal_texture_uuid: Option<AssetUuid>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub normal_texture_name: Option<String>,
    #[serde(default, skip_serializing_if = "is_zero_u32")]
    pub normal_texture_uv_set: u32,
    #[serde(
        default = "default_normal_scale",
        skip_serializing_if = "is_default_normal_scale"
    )]
    pub normal_scale: f32,
    #[serde(default, skip_serializing_if = "is_false")]
    pub normal_map_flip_y: bool,
    #[serde(default, skip_serializing_if = "is_false")]
    pub normal_map_two_component: bool,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub metallic_roughness_texture_uuid: Option<AssetUuid>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub metallic_roughness_texture_name: Option<String>,
    #[serde(default, skip_serializing_if = "is_zero_u32")]
    pub metallic_roughness_texture_uv_set: u32,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub occlusion_texture_uuid: Option<AssetUuid>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub occlusion_texture_name: Option<String>,
    #[serde(default, skip_serializing_if = "is_zero_u32")]
    pub occlusion_texture_uv_set: u32,
    #[serde(
        default = "default_occlusion_strength",
        skip_serializing_if = "is_default_occlusion_strength"
    )]
    pub occlusion_strength: f32,
    #[serde(
        default = "default_roughness",
        skip_serializing_if = "is_default_roughness"
    )]
    pub roughness: f32,
    #[serde(default, skip_serializing_if = "is_zero_f32")]
    pub metallic: f32,
    #[serde(default, skip_serializing_if = "is_false")]
    pub unlit: bool,
    #[serde(default, skip_serializing_if = "is_false")]
    pub double_sided: bool,
    #[serde(
        default = "default_uv_scale",
        skip_serializing_if = "is_default_uv_scale"
    )]
    pub uv_scale: [f32; 2],
    #[serde(default, skip_serializing_if = "is_zero_f32")]
    pub transmission_factor: f32,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub transmission_texture_uuid: Option<AssetUuid>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub transmission_texture_name: Option<String>,
    #[serde(default, skip_serializing_if = "is_zero_u32")]
    pub transmission_texture_uv_set: u32,
    #[serde(default, skip_serializing_if = "is_zero_f32")]
    pub thickness: f32,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub thickness_texture_uuid: Option<AssetUuid>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub thickness_texture_name: Option<String>,
    #[serde(default, skip_serializing_if = "is_zero_u32")]
    pub thickness_texture_uv_set: u32,
    #[serde(
        default = "default_attenuation_color",
        skip_serializing_if = "is_default_attenuation_color"
    )]
    pub attenuation_color: [f32; 3],
    #[serde(
        default = "default_attenuation_distance",
        skip_serializing_if = "is_default_attenuation_distance"
    )]
    pub attenuation_distance: f32,
    #[serde(default = "default_ior", skip_serializing_if = "is_default_ior")]
    pub ior: f32,
    #[serde(
        default = "default_specular_factor",
        skip_serializing_if = "is_default_specular_factor"
    )]
    pub specular_factor: f32,
    #[serde(
        default = "default_specular_color_factor",
        skip_serializing_if = "is_default_specular_color_factor"
    )]
    pub specular_color_factor: [f32; 3],
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub specular_texture_uuid: Option<AssetUuid>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub specular_texture_name: Option<String>,
    #[serde(default, skip_serializing_if = "is_zero_u32")]
    pub specular_texture_uv_set: u32,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub specular_color_texture_uuid: Option<AssetUuid>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub specular_color_texture_name: Option<String>,
    #[serde(default, skip_serializing_if = "is_zero_u32")]
    pub specular_color_texture_uv_set: u32,
    #[serde(
        default = "default_emissive_strength",
        skip_serializing_if = "is_default_emissive_strength"
    )]
    pub emissive_strength: f32,
}

fn default_alpha_cutoff() -> f32 {
    0.5
}

fn default_roughness() -> f32 {
    0.5
}

fn default_uv_scale() -> [f32; 2] {
    [1.0, 1.0]
}

fn default_normal_scale() -> f32 {
    1.0
}

fn default_occlusion_strength() -> f32 {
    1.0
}

fn default_attenuation_color() -> [f32; 3] {
    [1.0, 1.0, 1.0]
}

fn default_attenuation_distance() -> f32 {
    f32::INFINITY
}

fn default_ior() -> f32 {
    1.5
}

fn default_specular_factor() -> f32 {
    1.0
}

fn default_specular_color_factor() -> [f32; 3] {
    [1.0, 1.0, 1.0]
}

fn default_emissive_strength() -> f32 {
    1.0
}

fn is_default_alpha_mode(value: &SceneAlphaMode) -> bool {
    *value == SceneAlphaMode::Opaque
}

fn is_default_alpha_cutoff(value: &f32) -> bool {
    (*value - 0.5).abs() < f32::EPSILON
}

pub(crate) fn is_zero_u32(value: &u32) -> bool {
    *value == 0
}

fn is_default_normal_scale(value: &f32) -> bool {
    (*value - 1.0).abs() < f32::EPSILON
}

pub(crate) fn is_false(value: &bool) -> bool {
    !*value
}

fn is_default_occlusion_strength(value: &f32) -> bool {
    (*value - 1.0).abs() < f32::EPSILON
}

fn is_default_roughness(value: &f32) -> bool {
    (*value - 0.5).abs() < f32::EPSILON
}

pub(crate) fn is_zero_f32(value: &f32) -> bool {
    value.abs() < f32::EPSILON
}

fn is_default_uv_scale(value: &[f32; 2]) -> bool {
    (value[0] - 1.0).abs() < f32::EPSILON && (value[1] - 1.0).abs() < f32::EPSILON
}

fn is_default_attenuation_color(value: &[f32; 3]) -> bool {
    (value[0] - 1.0).abs() < f32::EPSILON
        && (value[1] - 1.0).abs() < f32::EPSILON
        && (value[2] - 1.0).abs() < f32::EPSILON
}

fn is_default_attenuation_distance(value: &f32) -> bool {
    value.is_infinite() && value.is_sign_positive()
}

fn is_default_ior(value: &f32) -> bool {
    (*value - 1.5).abs() < f32::EPSILON
}

fn is_default_specular_factor(value: &f32) -> bool {
    (*value - 1.0).abs() < f32::EPSILON
}

fn is_default_specular_color_factor(value: &[f32; 3]) -> bool {
    (value[0] - 1.0).abs() < f32::EPSILON
        && (value[1] - 1.0).abs() < f32::EPSILON
        && (value[2] - 1.0).abs() < f32::EPSILON
}

fn is_default_emissive_strength(value: &f32) -> bool {
    (*value - 1.0).abs() < f32::EPSILON
}

impl Default for SceneMaterial {
    fn default() -> Self {
        Self {
            base_color: [1.0, 1.0, 1.0, 1.0],
            emissive_factor: [0.0, 0.0, 0.0],
            alpha_mode: SceneAlphaMode::Opaque,
            alpha_cutoff: 0.5,
            base_texture_uuid: None,
            base_texture_name: None,
            base_texture_uv_set: 0,
            emissive_texture_uuid: None,
            emissive_texture_name: None,
            emissive_texture_uv_set: 0,
            normal_texture_uuid: None,
            normal_texture_name: None,
            normal_texture_uv_set: 0,
            normal_scale: 1.0,
            normal_map_flip_y: false,
            normal_map_two_component: false,
            metallic_roughness_texture_uuid: None,
            metallic_roughness_texture_name: None,
            metallic_roughness_texture_uv_set: 0,
            occlusion_texture_uuid: None,
            occlusion_texture_name: None,
            occlusion_texture_uv_set: 0,
            occlusion_strength: 1.0,
            roughness: 0.5,
            metallic: 0.0,
            unlit: false,
            double_sided: false,
            uv_scale: [1.0, 1.0],
            transmission_factor: 0.0,
            transmission_texture_uuid: None,
            transmission_texture_name: None,
            transmission_texture_uv_set: 0,
            thickness: 0.0,
            thickness_texture_uuid: None,
            thickness_texture_name: None,
            thickness_texture_uv_set: 0,
            attenuation_color: [1.0, 1.0, 1.0],
            attenuation_distance: f32::INFINITY,
            ior: 1.5,
            specular_factor: 1.0,
            specular_color_factor: [1.0, 1.0, 1.0],
            specular_texture_uuid: None,
            specular_texture_name: None,
            specular_texture_uv_set: 0,
            specular_color_texture_uuid: None,
            specular_color_texture_name: None,
            specular_color_texture_uv_set: 0,
            emissive_strength: 1.0,
        }
    }
}

impl SceneMaterial {
    pub fn to_material(&self) -> Material {
        Material {
            base_color: self.base_color,
            emissive_factor: self.emissive_factor,
            alpha_mode: self.alpha_mode.into(),
            alpha_cutoff: self.alpha_cutoff,
            base_texture: self.base_texture_name.clone(),
            base_texture_uv_set: self.base_texture_uv_set,
            emissive_texture: self.emissive_texture_name.clone(),
            emissive_texture_uv_set: self.emissive_texture_uv_set,
            normal_texture: self.normal_texture_name.clone(),
            normal_texture_uv_set: self.normal_texture_uv_set,
            normal_scale: self.normal_scale,
            normal_map_flip_y: self.normal_map_flip_y,
            normal_map_two_component: self.normal_map_two_component,
            metallic_roughness_texture: self.metallic_roughness_texture_name.clone(),
            metallic_roughness_texture_uv_set: self.metallic_roughness_texture_uv_set,
            occlusion_texture: self.occlusion_texture_name.clone(),
            occlusion_texture_uv_set: self.occlusion_texture_uv_set,
            occlusion_strength: self.occlusion_strength,
            roughness: self.roughness,
            metallic: self.metallic,
            unlit: self.unlit,
            double_sided: self.double_sided,
            uv_scale: self.uv_scale,
            transmission_factor: self.transmission_factor,
            transmission_texture: self.transmission_texture_name.clone(),
            transmission_texture_uv_set: self.transmission_texture_uv_set,
            thickness: self.thickness,
            thickness_texture: self.thickness_texture_name.clone(),
            thickness_texture_uv_set: self.thickness_texture_uv_set,
            attenuation_color: self.attenuation_color,
            attenuation_distance: self.attenuation_distance,
            ior: self.ior,
            specular_factor: self.specular_factor,
            specular_color_factor: self.specular_color_factor,
            specular_texture: self.specular_texture_name.clone(),
            specular_texture_uv_set: self.specular_texture_uv_set,
            specular_color_texture: self.specular_color_texture_name.clone(),
            specular_color_texture_uv_set: self.specular_color_texture_uv_set,
            emissive_strength: self.emissive_strength,
        }
    }
}

impl From<&Material> for SceneMaterial {
    fn from(material: &Material) -> Self {
        Self {
            base_color: material.base_color,
            emissive_factor: material.emissive_factor,
            alpha_mode: material.alpha_mode.into(),
            alpha_cutoff: material.alpha_cutoff,
            base_texture_uuid: None,
            base_texture_name: material.base_texture.clone(),
            base_texture_uv_set: material.base_texture_uv_set,
            emissive_texture_uuid: None,
            emissive_texture_name: material.emissive_texture.clone(),
            emissive_texture_uv_set: material.emissive_texture_uv_set,
            normal_texture_uuid: None,
            normal_texture_name: material.normal_texture.clone(),
            normal_texture_uv_set: material.normal_texture_uv_set,
            normal_scale: material.normal_scale,
            normal_map_flip_y: material.normal_map_flip_y,
            normal_map_two_component: material.normal_map_two_component,
            metallic_roughness_texture_uuid: None,
            metallic_roughness_texture_name: material.metallic_roughness_texture.clone(),
            metallic_roughness_texture_uv_set: material.metallic_roughness_texture_uv_set,
            occlusion_texture_uuid: None,
            occlusion_texture_name: material.occlusion_texture.clone(),
            occlusion_texture_uv_set: material.occlusion_texture_uv_set,
            occlusion_strength: material.occlusion_strength,
            roughness: material.roughness,
            metallic: material.metallic,
            unlit: material.unlit,
            double_sided: material.double_sided,
            uv_scale: material.uv_scale,
            transmission_factor: material.transmission_factor,
            transmission_texture_uuid: None,
            transmission_texture_name: material.transmission_texture.clone(),
            transmission_texture_uv_set: material.transmission_texture_uv_set,
            thickness: material.thickness,
            thickness_texture_uuid: None,
            thickness_texture_name: material.thickness_texture.clone(),
            thickness_texture_uv_set: material.thickness_texture_uv_set,
            attenuation_color: material.attenuation_color,
            attenuation_distance: material.attenuation_distance,
            ior: material.ior,
            specular_factor: material.specular_factor,
            specular_color_factor: material.specular_color_factor,
            specular_texture_uuid: None,
            specular_texture_name: material.specular_texture.clone(),
            specular_texture_uv_set: material.specular_texture_uv_set,
            specular_color_texture_uuid: None,
            specular_color_texture_name: material.specular_color_texture.clone(),
            specular_color_texture_uv_set: material.specular_color_texture_uv_set,
            emissive_strength: material.emissive_strength,
        }
    }
}

#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq)]
pub enum SceneAlphaMode {
    #[default]
    Opaque,
    Mask,
    Blend,
}

impl From<AlphaMode> for SceneAlphaMode {
    fn from(mode: AlphaMode) -> Self {
        match mode {
            AlphaMode::Opaque => SceneAlphaMode::Opaque,
            AlphaMode::Mask => SceneAlphaMode::Mask,
            AlphaMode::Blend => SceneAlphaMode::Blend,
        }
    }
}

impl From<SceneAlphaMode> for AlphaMode {
    fn from(mode: SceneAlphaMode) -> Self {
        match mode {
            SceneAlphaMode::Opaque => AlphaMode::Opaque,
            SceneAlphaMode::Mask => AlphaMode::Mask,
            SceneAlphaMode::Blend => AlphaMode::Blend,
        }
    }
}
