use nalgebra_glm::{Vec2, Vec4};

use crate::ecs::ui::components::UiNodeShadow;

#[derive(Copy, Clone, Debug, Default)]
pub enum RectEffect {
    #[default]
    Solid,
    VerticalGradient {
        secondary: Vec4,
    },
    HorizontalGradient {
        secondary: Vec4,
    },
    RadialGlow {
        glow_color: Vec4,
        intensity: f32,
    },
    FrostedGlass {
        tint: Vec4,
        blur_amount: f32,
    },
    InsetEmboss {
        strength: f32,
        edge_softness: f32,
    },
    OutsetEmboss {
        strength: f32,
        edge_softness: f32,
    },
    ColorWheel {
        value: f32,
        alpha: f32,
    },
}

impl RectEffect {
    pub fn encode(&self) -> (u32, [f32; 4]) {
        match *self {
            Self::Solid => (0, [0.0; 4]),
            Self::VerticalGradient { secondary } => {
                (1, [secondary.x, secondary.y, secondary.z, secondary.w])
            }
            Self::HorizontalGradient { secondary } => {
                (2, [secondary.x, secondary.y, secondary.z, secondary.w])
            }
            Self::RadialGlow {
                glow_color,
                intensity,
            } => (3, [glow_color.x, glow_color.y, glow_color.z, intensity]),
            Self::InsetEmboss {
                strength,
                edge_softness,
            } => (4, [strength, edge_softness, 0.0, 0.0]),
            Self::OutsetEmboss {
                strength,
                edge_softness,
            } => (5, [strength, edge_softness, 0.0, 0.0]),
            Self::FrostedGlass { tint, blur_amount } => (6, [tint.x, tint.y, tint.z, blur_amount]),
            Self::ColorWheel { value, alpha } => (7, [value, alpha, 0.0, 0.0]),
        }
    }
}

fn default_panel_shadow() -> UiNodeShadow {
    UiNodeShadow {
        color: Vec4::new(0.0, 0.0, 0.0, 0.25),
        offset: Vec2::new(0.0, 2.0),
        blur: 8.0,
        spread: 0.0,
    }
}

fn default_button_shadow() -> UiNodeShadow {
    UiNodeShadow {
        color: Vec4::new(0.0, 0.0, 0.0, 0.18),
        offset: Vec2::new(0.0, 1.0),
        blur: 3.0,
        spread: 0.0,
    }
}

fn default_button_shadow_hover() -> UiNodeShadow {
    UiNodeShadow {
        color: Vec4::new(0.0, 0.0, 0.0, 0.28),
        offset: Vec2::new(0.0, 3.0),
        blur: 8.0,
        spread: 0.0,
    }
}

fn default_button_shadow_pressed() -> UiNodeShadow {
    UiNodeShadow {
        color: Vec4::new(0.0, 0.0, 0.0, 0.35),
        offset: Vec2::new(0.0, 1.0),
        blur: 2.0,
        spread: 0.0,
    }
}

fn default_modal_shadow() -> UiNodeShadow {
    UiNodeShadow {
        color: Vec4::new(0.0, 0.0, 0.0, 0.5),
        offset: Vec2::new(0.0, 8.0),
        blur: 32.0,
        spread: 4.0,
    }
}

#[derive(Clone, Debug)]
pub struct UiTheme {
    pub name: String,
    pub dark_mode: bool,

    pub text_color: Vec4,
    pub text_color_disabled: Vec4,
    pub text_color_accent: Vec4,

    pub background_color: Vec4,
    pub background_color_hovered: Vec4,
    pub background_color_active: Vec4,

    pub panel_color: Vec4,
    pub panel_header_color: Vec4,

    pub border_color: Vec4,
    pub border_color_focused: Vec4,

    pub accent_color: Vec4,
    pub accent_color_hovered: Vec4,
    pub accent_color_active: Vec4,

    pub success_color: Vec4,
    pub warning_color: Vec4,
    pub error_color: Vec4,

    pub slider_track_color: Vec4,
    pub slider_fill_color: Vec4,

    pub input_background_color: Vec4,
    pub input_background_focused: Vec4,

    pub selection_color: Vec4,

    pub scrollbar_color: Vec4,
    pub scrollbar_color_hovered: Vec4,

    pub border_width: f32,
    pub corner_radius: f32,
    pub padding: f32,
    pub spacing: f32,
    pub font_size: f32,
    pub button_height: f32,
    pub slider_height: f32,
    pub checkbox_size: f32,
    pub toggle_width: f32,
    pub toggle_height: f32,
    pub panel_header_height: f32,

    pub heading_font_size: f32,
    pub body_font_size: f32,
    pub caption_font_size: f32,
    pub monospace_font_size: f32,

    pub panel_shadow: UiNodeShadow,
    pub button_shadow: UiNodeShadow,
    pub button_shadow_hover: UiNodeShadow,
    pub button_shadow_pressed: UiNodeShadow,
    pub modal_shadow: UiNodeShadow,
    pub focus_glow: Option<UiNodeShadow>,

    pub panel_effect: RectEffect,
    pub button_effect: RectEffect,
    pub input_effect: RectEffect,

    pub transition_speed_enter: f32,
    pub transition_speed_exit: f32,
    pub use_spring: bool,
}

impl Default for UiTheme {
    fn default() -> Self {
        Self::dark()
    }
}

#[derive(Clone, Copy, Debug)]
pub struct ThemePalette {
    pub name: &'static str,
    pub dark_mode: bool,
    pub text_color: Vec4,
    pub text_color_disabled: Vec4,
    pub text_color_accent: Vec4,
    pub background_color: Vec4,
    pub background_color_hovered: Vec4,
    pub background_color_active: Vec4,
    pub panel_color: Vec4,
    pub panel_header_color: Vec4,
    pub border_color: Vec4,
    pub border_color_focused: Vec4,
    pub accent_color: Vec4,
    pub accent_color_hovered: Vec4,
    pub accent_color_active: Vec4,
    pub success_color: Vec4,
    pub warning_color: Vec4,
    pub error_color: Vec4,
    pub slider_track_color: Vec4,
    pub slider_fill_color: Vec4,
    pub input_background_color: Vec4,
    pub input_background_focused: Vec4,
    pub selection_color: Vec4,
    pub scrollbar_color: Vec4,
    pub scrollbar_color_hovered: Vec4,
}

fn nudge_brightness(color: Vec4, amount: f32) -> Vec4 {
    Vec4::new(
        (color.x + amount).clamp(0.0, 1.0),
        (color.y + amount).clamp(0.0, 1.0),
        (color.z + amount).clamp(0.0, 1.0),
        color.w,
    )
}

impl UiTheme {
    pub fn from_palette(palette: ThemePalette) -> Self {
        let panel_secondary = nudge_brightness(palette.panel_color, -0.04);
        let button_secondary = nudge_brightness(palette.background_color, -0.04);
        let focus_color = Vec4::new(
            palette.border_color_focused.x,
            palette.border_color_focused.y,
            palette.border_color_focused.z,
            0.7,
        );
        let focus_glow = Some(UiNodeShadow {
            color: focus_color,
            offset: Vec2::new(0.0, 0.0),
            blur: 4.0,
            spread: 1.5,
        });
        Self {
            name: palette.name.to_string(),
            dark_mode: palette.dark_mode,
            text_color: palette.text_color,
            text_color_disabled: palette.text_color_disabled,
            text_color_accent: palette.text_color_accent,
            background_color: palette.background_color,
            background_color_hovered: palette.background_color_hovered,
            background_color_active: palette.background_color_active,
            panel_color: palette.panel_color,
            panel_header_color: palette.panel_header_color,
            border_color: palette.border_color,
            border_color_focused: palette.border_color_focused,
            accent_color: palette.accent_color,
            accent_color_hovered: palette.accent_color_hovered,
            accent_color_active: palette.accent_color_active,
            success_color: palette.success_color,
            warning_color: palette.warning_color,
            error_color: palette.error_color,
            slider_track_color: palette.slider_track_color,
            slider_fill_color: palette.slider_fill_color,
            input_background_color: palette.input_background_color,
            input_background_focused: palette.input_background_focused,
            selection_color: palette.selection_color,
            scrollbar_color: palette.scrollbar_color,
            scrollbar_color_hovered: palette.scrollbar_color_hovered,

            border_width: 1.0,
            corner_radius: 4.0,
            padding: 8.0,
            spacing: 6.0,
            font_size: 18.0,
            button_height: 32.0,
            slider_height: 24.0,
            checkbox_size: 20.0,
            toggle_width: 40.0,
            toggle_height: 22.0,
            panel_header_height: 28.0,

            heading_font_size: 22.0,
            body_font_size: 16.0,
            caption_font_size: 12.0,
            monospace_font_size: 14.0,

            panel_shadow: default_panel_shadow(),
            button_shadow: default_button_shadow(),
            button_shadow_hover: default_button_shadow_hover(),
            button_shadow_pressed: default_button_shadow_pressed(),
            modal_shadow: default_modal_shadow(),
            focus_glow,

            panel_effect: RectEffect::VerticalGradient {
                secondary: panel_secondary,
            },
            button_effect: RectEffect::VerticalGradient {
                secondary: button_secondary,
            },
            input_effect: RectEffect::Solid,

            transition_speed_enter: 8.0,
            transition_speed_exit: 6.0,
            use_spring: false,
        }
    }
}

impl UiTheme {
    pub fn dark() -> Self {
        Self::from_palette(ThemePalette {
            name: "Dark",
            dark_mode: true,
            text_color: Vec4::new(1.0, 1.0, 1.0, 1.0),
            text_color_disabled: Vec4::new(0.5, 0.5, 0.5, 1.0),
            text_color_accent: Vec4::new(0.4, 0.8, 1.0, 1.0),
            background_color: Vec4::new(0.15, 0.15, 0.15, 1.0),
            background_color_hovered: Vec4::new(0.25, 0.25, 0.25, 1.0),
            background_color_active: Vec4::new(0.35, 0.35, 0.35, 1.0),
            panel_color: Vec4::new(0.12, 0.12, 0.12, 1.0),
            panel_header_color: Vec4::new(0.18, 0.18, 0.18, 1.0),
            border_color: Vec4::new(0.4, 0.4, 0.4, 1.0),
            border_color_focused: Vec4::new(0.5, 0.7, 1.0, 1.0),
            accent_color: Vec4::new(0.3, 0.5, 0.9, 1.0),
            accent_color_hovered: Vec4::new(0.4, 0.6, 1.0, 1.0),
            accent_color_active: Vec4::new(0.2, 0.4, 0.8, 1.0),
            success_color: Vec4::new(0.3, 0.8, 0.4, 1.0),
            warning_color: Vec4::new(1.0, 0.7, 0.2, 1.0),
            error_color: Vec4::new(0.9, 0.3, 0.3, 1.0),
            slider_track_color: Vec4::new(0.25, 0.25, 0.25, 1.0),
            slider_fill_color: Vec4::new(0.3, 0.5, 0.9, 1.0),
            input_background_color: Vec4::new(0.1, 0.1, 0.1, 1.0),
            input_background_focused: Vec4::new(0.12, 0.12, 0.15, 1.0),
            selection_color: Vec4::new(0.3, 0.5, 0.9, 0.4),
            scrollbar_color: Vec4::new(0.3, 0.3, 0.3, 0.6),
            scrollbar_color_hovered: Vec4::new(0.4, 0.4, 0.4, 0.8),
        })
    }

    pub fn dracula() -> Self {
        Self::from_palette(ThemePalette {
            name: "Dracula",
            dark_mode: true,
            text_color: Vec4::new(0.973, 0.973, 0.949, 1.0),
            text_color_disabled: Vec4::new(0.384, 0.447, 0.643, 1.0),
            text_color_accent: Vec4::new(0.545, 0.914, 0.992, 1.0),
            background_color: Vec4::new(0.157, 0.165, 0.212, 1.0),
            background_color_hovered: Vec4::new(0.267, 0.278, 0.353, 1.0),
            background_color_active: Vec4::new(0.337, 0.349, 0.424, 1.0),
            panel_color: Vec4::new(0.157, 0.165, 0.212, 1.0),
            panel_header_color: Vec4::new(0.267, 0.278, 0.353, 1.0),
            border_color: Vec4::new(0.384, 0.447, 0.643, 1.0),
            border_color_focused: Vec4::new(0.741, 0.576, 0.976, 1.0),
            accent_color: Vec4::new(0.741, 0.576, 0.976, 1.0),
            accent_color_hovered: Vec4::new(0.827, 0.686, 1.0, 1.0),
            accent_color_active: Vec4::new(0.639, 0.475, 0.855, 1.0),
            success_color: Vec4::new(0.314, 0.980, 0.482, 1.0),
            warning_color: Vec4::new(1.0, 0.722, 0.424, 1.0),
            error_color: Vec4::new(1.0, 0.333, 0.333, 1.0),
            slider_track_color: Vec4::new(0.267, 0.278, 0.353, 1.0),
            slider_fill_color: Vec4::new(0.741, 0.576, 0.976, 1.0),
            input_background_color: Vec4::new(0.118, 0.125, 0.165, 1.0),
            input_background_focused: Vec4::new(0.157, 0.165, 0.212, 1.0),
            selection_color: Vec4::new(0.384, 0.447, 0.643, 0.5),
            scrollbar_color: Vec4::new(0.384, 0.447, 0.643, 0.4),
            scrollbar_color_hovered: Vec4::new(0.384, 0.447, 0.643, 0.7),
        })
    }

    pub fn nord() -> Self {
        Self::from_palette(ThemePalette {
            name: "Nord",
            dark_mode: true,
            text_color: Vec4::new(0.847, 0.871, 0.914, 1.0),
            text_color_disabled: Vec4::new(0.369, 0.420, 0.506, 1.0),
            text_color_accent: Vec4::new(0.533, 0.753, 0.816, 1.0),
            background_color: Vec4::new(0.180, 0.204, 0.251, 1.0),
            background_color_hovered: Vec4::new(0.231, 0.259, 0.322, 1.0),
            background_color_active: Vec4::new(0.263, 0.298, 0.369, 1.0),
            panel_color: Vec4::new(0.180, 0.204, 0.251, 1.0),
            panel_header_color: Vec4::new(0.231, 0.259, 0.322, 1.0),
            border_color: Vec4::new(0.369, 0.420, 0.506, 1.0),
            border_color_focused: Vec4::new(0.533, 0.753, 0.816, 1.0),
            accent_color: Vec4::new(0.533, 0.753, 0.816, 1.0),
            accent_color_hovered: Vec4::new(0.565, 0.792, 0.863, 1.0),
            accent_color_active: Vec4::new(0.427, 0.671, 0.757, 1.0),
            success_color: Vec4::new(0.639, 0.745, 0.549, 1.0),
            warning_color: Vec4::new(0.922, 0.796, 0.545, 1.0),
            error_color: Vec4::new(0.749, 0.380, 0.416, 1.0),
            slider_track_color: Vec4::new(0.231, 0.259, 0.322, 1.0),
            slider_fill_color: Vec4::new(0.533, 0.753, 0.816, 1.0),
            input_background_color: Vec4::new(0.141, 0.165, 0.204, 1.0),
            input_background_focused: Vec4::new(0.180, 0.204, 0.251, 1.0),
            selection_color: Vec4::new(0.533, 0.753, 0.816, 0.4),
            scrollbar_color: Vec4::new(0.369, 0.420, 0.506, 0.4),
            scrollbar_color_hovered: Vec4::new(0.369, 0.420, 0.506, 0.7),
        })
    }

    pub fn gruvbox_dark() -> Self {
        Self::from_palette(ThemePalette {
            name: "Gruvbox Dark",
            dark_mode: true,
            text_color: Vec4::new(0.922, 0.859, 0.698, 1.0),
            text_color_disabled: Vec4::new(0.502, 0.502, 0.502, 1.0),
            text_color_accent: Vec4::new(0.514, 0.647, 0.596, 1.0),
            background_color: Vec4::new(0.157, 0.157, 0.157, 1.0),
            background_color_hovered: Vec4::new(0.235, 0.220, 0.212, 1.0),
            background_color_active: Vec4::new(0.314, 0.294, 0.278, 1.0),
            panel_color: Vec4::new(0.157, 0.157, 0.157, 1.0),
            panel_header_color: Vec4::new(0.235, 0.220, 0.212, 1.0),
            border_color: Vec4::new(0.400, 0.361, 0.329, 1.0),
            border_color_focused: Vec4::new(0.514, 0.647, 0.596, 1.0),
            accent_color: Vec4::new(0.514, 0.647, 0.596, 1.0),
            accent_color_hovered: Vec4::new(0.557, 0.694, 0.639, 1.0),
            accent_color_active: Vec4::new(0.427, 0.561, 0.518, 1.0),
            success_color: Vec4::new(0.722, 0.733, 0.149, 1.0),
            warning_color: Vec4::new(0.980, 0.741, 0.184, 1.0),
            error_color: Vec4::new(0.984, 0.286, 0.204, 1.0),
            slider_track_color: Vec4::new(0.235, 0.220, 0.212, 1.0),
            slider_fill_color: Vec4::new(0.514, 0.647, 0.596, 1.0),
            input_background_color: Vec4::new(0.114, 0.125, 0.129, 1.0),
            input_background_focused: Vec4::new(0.157, 0.157, 0.157, 1.0),
            selection_color: Vec4::new(0.400, 0.361, 0.329, 0.5),
            scrollbar_color: Vec4::new(0.400, 0.361, 0.329, 0.4),
            scrollbar_color_hovered: Vec4::new(0.400, 0.361, 0.329, 0.7),
        })
    }

    pub fn catppuccin_mocha() -> Self {
        Self::from_palette(ThemePalette {
            name: "Catppuccin Mocha",
            dark_mode: true,
            text_color: Vec4::new(0.804, 0.839, 0.957, 1.0),
            text_color_disabled: Vec4::new(0.345, 0.357, 0.439, 1.0),
            text_color_accent: Vec4::new(0.537, 0.706, 0.980, 1.0),
            background_color: Vec4::new(0.118, 0.118, 0.180, 1.0),
            background_color_hovered: Vec4::new(0.192, 0.196, 0.267, 1.0),
            background_color_active: Vec4::new(0.271, 0.278, 0.353, 1.0),
            panel_color: Vec4::new(0.118, 0.118, 0.180, 1.0),
            panel_header_color: Vec4::new(0.192, 0.196, 0.267, 1.0),
            border_color: Vec4::new(0.345, 0.357, 0.439, 1.0),
            border_color_focused: Vec4::new(0.537, 0.706, 0.980, 1.0),
            accent_color: Vec4::new(0.537, 0.706, 0.980, 1.0),
            accent_color_hovered: Vec4::new(0.608, 0.765, 1.0, 1.0),
            accent_color_active: Vec4::new(0.431, 0.612, 0.878, 1.0),
            success_color: Vec4::new(0.651, 0.890, 0.631, 1.0),
            warning_color: Vec4::new(0.976, 0.886, 0.686, 1.0),
            error_color: Vec4::new(0.953, 0.545, 0.659, 1.0),
            slider_track_color: Vec4::new(0.192, 0.196, 0.267, 1.0),
            slider_fill_color: Vec4::new(0.537, 0.706, 0.980, 1.0),
            input_background_color: Vec4::new(0.067, 0.067, 0.106, 1.0),
            input_background_focused: Vec4::new(0.118, 0.118, 0.180, 1.0),
            selection_color: Vec4::new(0.345, 0.357, 0.439, 0.5),
            scrollbar_color: Vec4::new(0.345, 0.357, 0.439, 0.4),
            scrollbar_color_hovered: Vec4::new(0.345, 0.357, 0.439, 0.7),
        })
    }

    pub fn all_presets() -> Vec<Self> {
        vec![
            Self::dark(),
            Self::dracula(),
            Self::nord(),
            Self::gruvbox_dark(),
            Self::catppuccin_mocha(),
        ]
    }
}

#[derive(Clone, Debug)]
pub struct ThemeState {
    pub current_theme: UiTheme,
    pub presets: Vec<UiTheme>,
    pub selected_preset_index: Option<usize>,
    pub preview_theme_index: Option<usize>,
    pub generation: u64,
    pub last_applied_generation: u64,
    pub transition_from: Option<UiTheme>,
    pub transition_progress: f32,
    pub transition_duration: f32,
    pub transition_theme: Option<UiTheme>,
}

impl Default for ThemeState {
    fn default() -> Self {
        let presets = UiTheme::all_presets();
        Self {
            current_theme: presets[0].clone(),
            presets,
            selected_preset_index: Some(0),
            preview_theme_index: None,
            generation: 0,
            last_applied_generation: 0,
            transition_from: None,
            transition_progress: 0.0,
            transition_duration: 0.25,
            transition_theme: None,
        }
    }
}

impl ThemeState {
    pub fn active_theme(&self) -> &UiTheme {
        if let Some(transition) = self.transition_theme.as_ref() {
            return transition;
        }
        self.preview_theme_index
            .and_then(|index| self.presets.get(index))
            .unwrap_or(&self.current_theme)
    }

    pub fn select_theme(&mut self, index: usize) {
        if let Some(preset) = self.presets.get(index) {
            let from = self.active_theme().clone();
            self.transition_from = Some(from);
            self.transition_progress = 0.0;
            self.transition_theme = None;
            self.current_theme = preset.clone();
            self.selected_preset_index = Some(index);
            self.preview_theme_index = None;
            self.generation += 1;
        }
    }

    pub fn set_preview(&mut self, index: Option<usize>) {
        if self.preview_theme_index != index {
            self.preview_theme_index = index;
            self.generation += 1;
        }
    }

    pub fn clear_preview(&mut self) {
        if self.preview_theme_index.is_some() {
            self.preview_theme_index = None;
            self.generation += 1;
        }
    }

    pub fn tick_transition(&mut self, delta_time: f32) {
        let Some(from) = self.transition_from.as_ref() else {
            return;
        };
        let duration = self.transition_duration.max(0.0001);
        self.transition_progress = (self.transition_progress + delta_time / duration).min(1.0);
        if self.transition_progress >= 1.0 {
            self.transition_from = None;
            self.transition_theme = None;
            self.generation += 1;
            return;
        }
        let t = ease_in_out_cubic(self.transition_progress);
        let to = &self.current_theme;
        self.transition_theme = Some(lerp_theme_visual(from, to, t));
        self.generation += 1;
    }
}

fn ease_in_out_cubic(t: f32) -> f32 {
    if t < 0.5 {
        4.0 * t * t * t
    } else {
        1.0 - (-2.0 * t + 2.0).powi(3) / 2.0
    }
}

fn lerp_vec4(a: Vec4, b: Vec4, t: f32) -> Vec4 {
    a + (b - a) * t
}

fn lerp_vec2(a: Vec2, b: Vec2, t: f32) -> Vec2 {
    a + (b - a) * t
}

fn lerp_shadow(a: UiNodeShadow, b: UiNodeShadow, t: f32) -> UiNodeShadow {
    UiNodeShadow {
        color: lerp_vec4(a.color, b.color, t),
        offset: lerp_vec2(a.offset, b.offset, t),
        blur: a.blur + (b.blur - a.blur) * t,
        spread: a.spread + (b.spread - a.spread) * t,
    }
}

fn lerp_optional_shadow(
    a: Option<UiNodeShadow>,
    b: Option<UiNodeShadow>,
    t: f32,
) -> Option<UiNodeShadow> {
    match (a, b) {
        (Some(a), Some(b)) => Some(lerp_shadow(a, b, t)),
        (Some(a), None) => {
            let mut out = a;
            out.color.w *= 1.0 - t;
            Some(out)
        }
        (None, Some(b)) => {
            let mut out = b;
            out.color.w *= t;
            Some(out)
        }
        (None, None) => None,
    }
}

fn lerp_theme_visual(from: &UiTheme, to: &UiTheme, t: f32) -> UiTheme {
    let mut out = to.clone();
    out.text_color = lerp_vec4(from.text_color, to.text_color, t);
    out.text_color_disabled = lerp_vec4(from.text_color_disabled, to.text_color_disabled, t);
    out.text_color_accent = lerp_vec4(from.text_color_accent, to.text_color_accent, t);
    out.background_color = lerp_vec4(from.background_color, to.background_color, t);
    out.background_color_hovered = lerp_vec4(
        from.background_color_hovered,
        to.background_color_hovered,
        t,
    );
    out.background_color_active =
        lerp_vec4(from.background_color_active, to.background_color_active, t);
    out.panel_color = lerp_vec4(from.panel_color, to.panel_color, t);
    out.panel_header_color = lerp_vec4(from.panel_header_color, to.panel_header_color, t);
    out.border_color = lerp_vec4(from.border_color, to.border_color, t);
    out.border_color_focused = lerp_vec4(from.border_color_focused, to.border_color_focused, t);
    out.accent_color = lerp_vec4(from.accent_color, to.accent_color, t);
    out.accent_color_hovered = lerp_vec4(from.accent_color_hovered, to.accent_color_hovered, t);
    out.accent_color_active = lerp_vec4(from.accent_color_active, to.accent_color_active, t);
    out.success_color = lerp_vec4(from.success_color, to.success_color, t);
    out.warning_color = lerp_vec4(from.warning_color, to.warning_color, t);
    out.error_color = lerp_vec4(from.error_color, to.error_color, t);
    out.slider_track_color = lerp_vec4(from.slider_track_color, to.slider_track_color, t);
    out.slider_fill_color = lerp_vec4(from.slider_fill_color, to.slider_fill_color, t);
    out.input_background_color =
        lerp_vec4(from.input_background_color, to.input_background_color, t);
    out.input_background_focused = lerp_vec4(
        from.input_background_focused,
        to.input_background_focused,
        t,
    );
    out.selection_color = lerp_vec4(from.selection_color, to.selection_color, t);
    out.scrollbar_color = lerp_vec4(from.scrollbar_color, to.scrollbar_color, t);
    out.scrollbar_color_hovered =
        lerp_vec4(from.scrollbar_color_hovered, to.scrollbar_color_hovered, t);
    out.panel_shadow = lerp_shadow(from.panel_shadow, to.panel_shadow, t);
    out.button_shadow = lerp_shadow(from.button_shadow, to.button_shadow, t);
    out.button_shadow_hover = lerp_shadow(from.button_shadow_hover, to.button_shadow_hover, t);
    out.button_shadow_pressed =
        lerp_shadow(from.button_shadow_pressed, to.button_shadow_pressed, t);
    out.modal_shadow = lerp_shadow(from.modal_shadow, to.modal_shadow, t);
    out.focus_glow = lerp_optional_shadow(from.focus_glow, to.focus_glow, t);
    out
}
