use crate::Parse;
use freya_engine::prelude::*;
use std::fmt;
pub trait DisplayColor {
    fn fmt_rgb(&self, f: &mut fmt::Formatter) -> fmt::Result;
    fn fmt_hsl(&self, f: &mut fmt::Formatter) -> fmt::Result;
}
#[derive(Debug, PartialEq, Eq)]
pub struct ParseColorError;
impl Parse for Color {
    type Err = ParseColorError;
    fn parse(value: &str) -> Result<Self, Self::Err> {
        match value {
            "red" => Ok(Color::RED),
            "green" => Ok(Color::GREEN),
            "blue" => Ok(Color::BLUE),
            "yellow" => Ok(Color::YELLOW),
            "black" => Ok(Color::BLACK),
            "gray" => Ok(Color::GRAY),
            "white" => Ok(Color::WHITE),
            "orange" => Ok(Color::from_rgb(255, 165, 0)),
            "transparent" => Ok(Color::TRANSPARENT),
            _ => {
                if value.starts_with("hsl(") {
                    parse_hsl(value)
                } else if value.starts_with("rgb(") {
                    parse_rgb(value)
                } else {
                    Err(ParseColorError)
                }
            }
        }
    }
}
impl DisplayColor for Color {
    fn fmt_rgb(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "rgb({}, {}, {}, {})",
            self.r(),
            self.g(),
            self.b(),
            self.a()
        )
    }
    fn fmt_hsl(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let hsv = self.to_hsv();
        let l = hsv.v - (hsv.v * hsv.s / 2.0);
        let s = if l == 1.0 || l == 0.0 {
            0.0
        } else {
            (hsv.v - l) / f32::min(l, 1.0 - l)
        };
        write!(
            f,
            "hsl({}deg, {}%, {}%, {}%)",
            hsv.h,
            s * 100.0,
            l * 100.0,
            (self.a() as f32 / 128.0) * 100.0
        )
    }
}
fn parse_rgb(color: &str) -> Result<Color, ParseColorError> {
    if !color.ends_with(')') {
        return Err(ParseColorError);
    }
    let color = color.replacen("rgb(", "", 1).replacen(')', "", 1);
    let mut colors = color.split(',');
    let r = colors
        .next()
        .ok_or(ParseColorError)?
        .trim()
        .parse::<u8>()
        .map_err(|_| ParseColorError)?;
    let g = colors
        .next()
        .ok_or(ParseColorError)?
        .trim()
        .parse::<u8>()
        .map_err(|_| ParseColorError)?;
    let b = colors
        .next()
        .ok_or(ParseColorError)?
        .trim()
        .parse::<u8>()
        .map_err(|_| ParseColorError)?;
    let a: Option<&str> = colors.next();
    if colors.next().is_some() {
        return Err(ParseColorError);
    }
    if let Some(a) = a {
        let a = a.trim().parse::<u8>().map_err(|_| ParseColorError)?;
        Ok(Color::from_argb(a, r, g, b))
    } else {
        Ok(Color::from_rgb(r, g, b))
    }
}
fn parse_hsl(color: &str) -> Result<Color, ParseColorError> {
    if !color.ends_with(')') {
        return Err(ParseColorError);
    }
    let color = color.replacen("hsl(", "", 1).replacen(')', "", 1);
    let mut colors = color.split(',');
    let h_str = colors.next().ok_or(ParseColorError)?.trim();
    let s_str = colors.next().ok_or(ParseColorError)?.trim();
    let l_str = colors.next().ok_or(ParseColorError)?.trim();
    let a_str: Option<&str> = colors.next();
    if colors.next().is_some()
        || !h_str.ends_with("deg")
        || !s_str.ends_with('%')
        || !l_str.ends_with('%')
    {
        return Err(ParseColorError);
    }
    let h = h_str
        .replacen("deg", "", 1)
        .parse::<f32>()
        .map_err(|_| ParseColorError)?;
    let mut s = s_str
        .replacen('%', "", 1)
        .parse::<f32>()
        .map_err(|_| ParseColorError)?
        / 100.0;
    let mut l = l_str
        .replacen('%', "", 1)
        .parse::<f32>()
        .map_err(|_| ParseColorError)?
        / 100.0;
    l *= 2.0;
    s *= if l <= 1.0 { l } else { 2.0 - l };
    let v = (l + s) / 2.0;
    s = (2.0 * s) / (l + s);
    let hsv = HSV::from((h, s, v));
    if let Some(a_str) = a_str {
        if !s_str.ends_with('%') {
            return Err(ParseColorError);
        }
        let a = a_str
            .trim()
            .replace('%', "")
            .parse::<f32>()
            .map_err(|_| ParseColorError)?
            / 100.0;
        Ok(hsv.to_color((a * 255.0).round() as u8))
    } else {
        Ok(hsv.to_color(255))
    }
}