use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
pub enum TonemapAlgorithm {
    #[default]
    Aces,
    Reinhard,
    ReinhardExtended,
    Uncharted2,
    AgX,
    Neutral,
    None,
}

impl TonemapAlgorithm {
    pub const ALL: &'static [TonemapAlgorithm] = &[
        TonemapAlgorithm::Aces,
        TonemapAlgorithm::Reinhard,
        TonemapAlgorithm::ReinhardExtended,
        TonemapAlgorithm::Uncharted2,
        TonemapAlgorithm::AgX,
        TonemapAlgorithm::Neutral,
        TonemapAlgorithm::None,
    ];

    pub fn as_u32(&self) -> u32 {
        match self {
            TonemapAlgorithm::Aces => 0,
            TonemapAlgorithm::Reinhard => 1,
            TonemapAlgorithm::ReinhardExtended => 2,
            TonemapAlgorithm::Uncharted2 => 3,
            TonemapAlgorithm::AgX => 4,
            TonemapAlgorithm::Neutral => 5,
            TonemapAlgorithm::None => 6,
        }
    }

    pub fn name(&self) -> &'static str {
        match self {
            TonemapAlgorithm::Aces => "ACES",
            TonemapAlgorithm::Reinhard => "Reinhard",
            TonemapAlgorithm::ReinhardExtended => "Reinhard Extended",
            TonemapAlgorithm::Uncharted2 => "Uncharted 2",
            TonemapAlgorithm::AgX => "AgX",
            TonemapAlgorithm::Neutral => "Neutral",
            TonemapAlgorithm::None => "None",
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
pub enum ColorGradingPreset {
    #[default]
    Default,
    Vibrant,
    Cinematic,
    Muted,
    HighContrast,
    Warm,
    Cool,
    Retro,
    Desaturated,
    Custom,
}

impl ColorGradingPreset {
    pub const ALL: &'static [ColorGradingPreset] = &[
        ColorGradingPreset::Default,
        ColorGradingPreset::Vibrant,
        ColorGradingPreset::Cinematic,
        ColorGradingPreset::Muted,
        ColorGradingPreset::HighContrast,
        ColorGradingPreset::Warm,
        ColorGradingPreset::Cool,
        ColorGradingPreset::Retro,
        ColorGradingPreset::Desaturated,
        ColorGradingPreset::Custom,
    ];

    pub fn name(&self) -> &'static str {
        match self {
            ColorGradingPreset::Default => "Default",
            ColorGradingPreset::Vibrant => "Vibrant",
            ColorGradingPreset::Cinematic => "Cinematic",
            ColorGradingPreset::Muted => "Muted",
            ColorGradingPreset::HighContrast => "High Contrast",
            ColorGradingPreset::Warm => "Warm",
            ColorGradingPreset::Cool => "Cool",
            ColorGradingPreset::Retro => "Retro",
            ColorGradingPreset::Desaturated => "Desaturated",
            ColorGradingPreset::Custom => "Custom",
        }
    }

    pub fn to_color_grading(&self) -> ColorGrading {
        match self {
            ColorGradingPreset::Default => ColorGrading {
                gamma: 2.2,
                saturation: 1.0,
                brightness: 0.0,
                contrast: 1.0,
                tonemap_algorithm: TonemapAlgorithm::Aces,
                preset: *self,
            },
            ColorGradingPreset::Vibrant => ColorGrading {
                gamma: 2.2,
                saturation: 1.3,
                brightness: 0.02,
                contrast: 1.1,
                tonemap_algorithm: TonemapAlgorithm::Aces,
                preset: *self,
            },
            ColorGradingPreset::Cinematic => ColorGrading {
                gamma: 2.4,
                saturation: 0.9,
                brightness: -0.02,
                contrast: 1.15,
                tonemap_algorithm: TonemapAlgorithm::Aces,
                preset: *self,
            },
            ColorGradingPreset::Muted => ColorGrading {
                gamma: 2.2,
                saturation: 0.7,
                brightness: 0.0,
                contrast: 0.9,
                tonemap_algorithm: TonemapAlgorithm::Reinhard,
                preset: *self,
            },
            ColorGradingPreset::HighContrast => ColorGrading {
                gamma: 2.2,
                saturation: 1.1,
                brightness: 0.0,
                contrast: 1.4,
                tonemap_algorithm: TonemapAlgorithm::Aces,
                preset: *self,
            },
            ColorGradingPreset::Warm => ColorGrading {
                gamma: 2.1,
                saturation: 1.1,
                brightness: 0.03,
                contrast: 1.05,
                tonemap_algorithm: TonemapAlgorithm::Aces,
                preset: *self,
            },
            ColorGradingPreset::Cool => ColorGrading {
                gamma: 2.3,
                saturation: 0.95,
                brightness: -0.01,
                contrast: 1.05,
                tonemap_algorithm: TonemapAlgorithm::Neutral,
                preset: *self,
            },
            ColorGradingPreset::Retro => ColorGrading {
                gamma: 2.0,
                saturation: 0.8,
                brightness: 0.05,
                contrast: 1.2,
                tonemap_algorithm: TonemapAlgorithm::Reinhard,
                preset: *self,
            },
            ColorGradingPreset::Desaturated => ColorGrading {
                gamma: 2.2,
                saturation: 0.3,
                brightness: 0.0,
                contrast: 1.0,
                tonemap_algorithm: TonemapAlgorithm::Aces,
                preset: *self,
            },
            ColorGradingPreset::Custom => ColorGrading::default(),
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub struct ColorGrading {
    pub gamma: f32,
    pub saturation: f32,
    pub brightness: f32,
    pub contrast: f32,
    pub tonemap_algorithm: TonemapAlgorithm,
    pub preset: ColorGradingPreset,
}

impl Default for ColorGrading {
    fn default() -> Self {
        Self {
            gamma: 2.2,
            saturation: 1.0,
            brightness: 0.0,
            contrast: 1.0,
            tonemap_algorithm: TonemapAlgorithm::Aces,
            preset: ColorGradingPreset::Default,
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub struct VertexSnap {
    pub resolution: [f32; 2],
}

impl Default for VertexSnap {
    fn default() -> Self {
        Self {
            resolution: [160.0, 120.0],
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub struct Fog {
    pub color: [f32; 3],
    pub start: f32,
    pub end: f32,
}

impl Default for Fog {
    fn default() -> Self {
        Self {
            color: [0.5, 0.5, 0.55],
            start: 2.0,
            end: 15.0,
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
pub enum Atmosphere {
    #[default]
    None,
    Sky,
    CloudySky,
    Space,
    Nebula,
    Sunset,
    Nighttime,
    Hdr,
    HdrMip0,
    HdrMip1,
    HdrMip2,
    HdrMip3,
    HdrMip4,
    Irradiance,
    PrefilterMip0,
    PrefilterMip1,
    PrefilterMip2,
    PrefilterMip3,
    PrefilterMip4,
}

impl Atmosphere {
    pub const ALL: &'static [Atmosphere] = &[
        Atmosphere::None,
        Atmosphere::Sky,
        Atmosphere::CloudySky,
        Atmosphere::Space,
        Atmosphere::Nebula,
        Atmosphere::Sunset,
        Atmosphere::Nighttime,
        Atmosphere::Hdr,
        Atmosphere::HdrMip0,
        Atmosphere::HdrMip1,
        Atmosphere::HdrMip2,
        Atmosphere::HdrMip3,
        Atmosphere::HdrMip4,
        Atmosphere::Irradiance,
        Atmosphere::PrefilterMip0,
        Atmosphere::PrefilterMip1,
        Atmosphere::PrefilterMip2,
        Atmosphere::PrefilterMip3,
        Atmosphere::PrefilterMip4,
    ];

    pub fn mip_level(&self) -> f32 {
        match self {
            Atmosphere::HdrMip0 | Atmosphere::PrefilterMip0 => 0.0,
            Atmosphere::HdrMip1 | Atmosphere::PrefilterMip1 => 1.0,
            Atmosphere::HdrMip2 | Atmosphere::PrefilterMip2 => 2.0,
            Atmosphere::HdrMip3 | Atmosphere::PrefilterMip3 => 3.0,
            Atmosphere::HdrMip4 | Atmosphere::PrefilterMip4 => 4.0,
            _ => 0.0,
        }
    }

    pub fn next(self) -> Self {
        let all = Self::ALL;
        let current_index = all.iter().position(|&a| a == self).unwrap_or(0);
        let next_index = (current_index + 1) % all.len();
        all[next_index]
    }

    pub fn previous(self) -> Self {
        let all = Self::ALL;
        let current_index = all.iter().position(|&a| a == self).unwrap_or(0);
        let prev_index = if current_index == 0 {
            all.len() - 1
        } else {
            current_index - 1
        };
        all[prev_index]
    }
}

pub struct Graphics {
    pub compute_grayscale_enabled: bool,
    pub gpu_culling_enabled: bool,
    pub show_grid: bool,
    pub show_bounding_volumes: bool,
    pub show_selected_bounding_volume: bool,
    pub bounding_volume_selected_entity: Option<freecs::Entity>,
    pub show_normals: bool,
    pub normal_line_length: f32,
    pub normal_line_color: [f32; 4],
    pub atmosphere: Atmosphere,
    pub render_layer_world_enabled: bool,
    pub render_layer_overlay_enabled: bool,
    pub use_fullscreen: bool,
    pub show_cursor: bool,
    pub clear_color: [f32; 4],
    pub ui_scale: Option<f32>,
    pub letterbox_amount: f32,
    pub letterbox_target: f32,
    pub unlit_mode: bool,
    pub selection_outline_enabled: bool,
    pub selection_outline_color: [f32; 4],
    pub vertex_snap: Option<VertexSnap>,
    pub affine_texture_mapping: bool,
    pub fog: Option<Fog>,
    pub color_grading: ColorGrading,
    pub bloom_enabled: bool,
    pub bloom_intensity: f32,
}

impl Default for Graphics {
    fn default() -> Self {
        Self {
            compute_grayscale_enabled: false,
            gpu_culling_enabled: true,
            show_grid: false,
            show_bounding_volumes: false,
            show_selected_bounding_volume: false,
            bounding_volume_selected_entity: None,
            show_normals: false,
            normal_line_length: 0.1,
            normal_line_color: [0.0, 1.0, 0.0, 1.0],
            atmosphere: Atmosphere::None,
            render_layer_world_enabled: true,
            render_layer_overlay_enabled: true,
            use_fullscreen: false,
            show_cursor: true,
            clear_color: [0.0, 0.0, 0.0, 1.0],
            ui_scale: None,
            letterbox_amount: 0.0,
            letterbox_target: 0.0,
            unlit_mode: false,
            selection_outline_enabled: false,
            selection_outline_color: [1.0, 0.45, 0.0, 1.0],
            vertex_snap: None,
            affine_texture_mapping: false,
            fog: None,
            color_grading: ColorGrading::default(),
            bloom_enabled: false,
            bloom_intensity: 0.3,
        }
    }
}
