//! Hue rotation for palette-wide color transforms.

use super::palette::{Palette, Rgb};
use super::build::build;
use super::ThemeColors;

fn rgb_to_hsv(r: u8, g: u8, b: u8) -> (f32, f32, f32) {
    let r = r as f32 / 255.0;
    let g = g as f32 / 255.0;
    let b = b as f32 / 255.0;
    let max = r.max(g).max(b);
    let min = r.min(g).min(b);
    let delta = max - min;
    let h = if delta == 0.0 {
        0.0
    } else if max == r {
        60.0 * (((g - b) / delta) % 6.0)
    } else if max == g {
        60.0 * (((b - r) / delta) + 2.0)
    } else {
        60.0 * (((r - g) / delta) + 4.0)
    };
    let h = if h < 0.0 { h + 360.0 } else { h };
    let s = if max == 0.0 { 0.0 } else { delta / max };
    (h, s, max)
}

fn hsv_to_rgb(h: f32, s: f32, v: f32) -> (u8, u8, u8) {
    let c = v * s;
    let x = c * (1.0 - ((h / 60.0) % 2.0 - 1.0).abs());
    let m = v - c;
    let (r, g, b) = if h < 60.0 {
        (c, x, 0.0)
    } else if h < 120.0 {
        (x, c, 0.0)
    } else if h < 180.0 {
        (0.0, c, x)
    } else if h < 240.0 {
        (0.0, x, c)
    } else if h < 300.0 {
        (x, 0.0, c)
    } else {
        (c, 0.0, x)
    };
    (
        ((r + m) * 255.0) as u8,
        ((g + m) * 255.0) as u8,
        ((b + m) * 255.0) as u8,
    )
}

fn rotate(c: Rgb, degrees: f32) -> Rgb {
    let (h, s, v) = rgb_to_hsv(c.0, c.1, c.2);
    let new_h = (h + degrees) % 360.0;
    let new_h = if new_h < 0.0 { new_h + 360.0 } else { new_h };
    hsv_to_rgb(new_h, s, v)
}

fn rotate5(arr: [Rgb; 5], d: f32) -> [Rgb; 5] {
    [rotate(arr[0], d), rotate(arr[1], d), rotate(arr[2], d), rotate(arr[3], d), rotate(arr[4], d)]
}

fn rotate3(arr: [Rgb; 3], d: f32) -> [Rgb; 3] {
    [rotate(arr[0], d), rotate(arr[1], d), rotate(arr[2], d)]
}

/// Build a [`ThemeColors`] with all palette hues rotated by `degrees`.
pub fn rotate_palette(palette: &Palette, degrees: f32) -> ThemeColors {
    if degrees == 0.0 {
        return build(palette);
    }
    let d = degrees;
    build(&Palette {
        bg: rotate(palette.bg, d),
        surface: rotate(palette.surface, d),
        surface2: rotate(palette.surface2, d),
        fg: rotate(palette.fg, d),
        fg_dim: rotate(palette.fg_dim, d),
        fg_muted: rotate(palette.fg_muted, d),
        accent: rotate(palette.accent, d),
        red: rotate(palette.red, d),
        green: rotate(palette.green, d),
        yellow: rotate(palette.yellow, d),
        blue: rotate(palette.blue, d),
        purple: rotate(palette.purple, d),
        cyan: rotate(palette.cyan, d),
        orange: rotate(palette.orange, d),
        tempo_color: rotate(palette.tempo_color, d),
        bank_color: rotate(palette.bank_color, d),
        pattern_color: rotate(palette.pattern_color, d),
        title_accent: rotate(palette.title_accent, d),
        title_author: rotate(palette.title_author, d),
        secondary: rotate(palette.secondary, d),
        link_bright: rotate5(palette.link_bright, d),
        link_dim: rotate5(palette.link_dim, d),
        sparkle: rotate5(palette.sparkle, d),
        meter: rotate3(palette.meter, d),
    })
}
