use std::time::Instant;

use crate::scrin::Color;

pub struct Aisling {
    started: Instant,
}

impl Aisling {
    pub fn new() -> Self {
        Self {
            started: Instant::now(),
        }
    }

    pub fn phase(&self, speed: f32, offset: f32) -> f32 {
        let t = self.started.elapsed().as_secs_f32() * speed + offset;
        (t.sin() + 1.0) * 0.5
    }

    pub fn tick(&self, rate: f32, modulo: usize) -> usize {
        if modulo == 0 {
            return 0;
        }
        ((self.started.elapsed().as_secs_f32() * rate) as usize) % modulo
    }

    pub fn loader(&self, index: usize) -> Color {
        let p = self.phase(4.5, index as f32 * 0.55);
        blend(rgb(6, 182, 212), rgb(168, 85, 247), p)
    }

    pub fn title(&self, index: usize) -> Color {
        let p = self.phase(2.3, index as f32 * 0.65);
        blend(rgb(58, 189, 248), rgb(217, 70, 239), p)
    }

    pub fn accent(&self) -> Color {
        let p = self.phase(1.7, 0.0);
        blend(rgb(45, 212, 191), rgb(125, 92, 255), p)
    }

    pub fn selected_bg(&self) -> Color {
        let p = self.phase(3.0, 1.0);
        blend(rgb(31, 41, 55), rgb(49, 46, 129), p)
    }

    pub fn spotlight_bg(&self) -> Color {
        let p = self.phase(1.2, 0.4);
        blend(rgb(12, 20, 35), rgb(19, 28, 64), p)
    }

    pub fn matrix(&self, index: usize) -> Color {
        let p = self.phase(2.8, index as f32 * 0.37);
        blend(rgb(20, 184, 166), rgb(34, 197, 94), p)
    }

    pub fn glitch(&self, index: usize) -> Color {
        let p = self.phase(9.0, index as f32 * 1.9);
        blend(rgb(34, 211, 238), rgb(244, 114, 182), p)
    }

    pub fn burn(value: f32) -> Color {
        let value = value.clamp(0.0, 1.0);
        blend(rgb(251, 146, 60), rgb(239, 68, 68), value)
    }

    pub fn heat(value: f32) -> Color {
        let value = value.clamp(0.0, 1.0);
        if value < 0.50 {
            blend(rgb(52, 211, 153), rgb(250, 204, 21), value / 0.50)
        } else {
            blend(rgb(250, 204, 21), rgb(248, 113, 113), (value - 0.50) / 0.50)
        }
    }

    pub fn memory(value: f32) -> Color {
        let value = value.clamp(0.0, 1.0);
        blend(rgb(96, 165, 250), rgb(244, 114, 182), value)
    }

    pub fn dim() -> Color {
        rgb(75, 85, 99)
    }

    pub fn soft() -> Color {
        rgb(148, 163, 184)
    }

    pub fn ok() -> Color {
        rgb(52, 211, 153)
    }

    pub fn warn() -> Color {
        rgb(251, 191, 36)
    }

    pub fn danger() -> Color {
        rgb(248, 113, 113)
    }
}

fn rgb(r: u8, g: u8, b: u8) -> Color {
    Color::Rgb { r, g, b }
}

fn blend(a: Color, b: Color, amount: f32) -> Color {
    let amount = amount.clamp(0.0, 1.0);
    match (a, b) {
        (
            Color::Rgb {
                r: ar,
                g: ag,
                b: ab,
            },
            Color::Rgb {
                r: br,
                g: bg,
                b: bb,
            },
        ) => Color::Rgb {
            r: lerp(ar, br, amount),
            g: lerp(ag, bg, amount),
            b: lerp(ab, bb, amount),
        },
        _ => a,
    }
}

fn lerp(a: u8, b: u8, amount: f32) -> u8 {
    (a as f32 + (b as f32 - a as f32) * amount).round() as u8
}
