use std::fmt;
use freya_engine::prelude::Color;
use torin::scaled::Scaled;
use crate::{
    ExtSplit,
    Fill,
    Parse,
    ParseError,
};
#[derive(Default, Clone, Debug, PartialEq)]
pub struct Border {
    pub fill: Fill,
    pub width: BorderWidth,
    pub alignment: BorderAlignment,
}
impl Border {
    #[inline]
    pub fn is_visible(&self) -> bool {
        !(self.width.top == 0.0
            && self.width.left == 0.0
            && self.width.bottom == 0.0
            && self.width.right == 0.0)
            && self.fill != Fill::Color(Color::TRANSPARENT)
    }
}
#[derive(Default, Clone, Copy, Debug, PartialEq)]
pub struct BorderWidth {
    pub top: f32,
    pub right: f32,
    pub bottom: f32,
    pub left: f32,
}
impl Scaled for BorderWidth {
    fn scale(&mut self, scale_factor: f32) {
        self.top *= scale_factor;
        self.left *= scale_factor;
        self.bottom *= scale_factor;
        self.right *= scale_factor;
    }
}
impl fmt::Display for BorderWidth {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "{} {} {} {}",
            self.top, self.right, self.bottom, self.left,
        )
    }
}
#[derive(Default, Clone, Copy, Debug, PartialEq)]
pub enum BorderAlignment {
    #[default]
    Inner,
    Outer,
    Center,
}
impl Parse for BorderAlignment {
    fn parse(value: &str) -> Result<Self, ParseError> {
        Ok(match value {
            "inner" => BorderAlignment::Inner,
            "outer" => BorderAlignment::Outer,
            "center" => BorderAlignment::Center,
            _ => BorderAlignment::default(),
        })
    }
}
impl fmt::Display for BorderAlignment {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str(match self {
            BorderAlignment::Inner => "inner",
            BorderAlignment::Outer => "outer",
            BorderAlignment::Center => "center",
        })
    }
}
impl Parse for Border {
    fn parse(value: &str) -> Result<Self, ParseError> {
        if value == "none" {
            return Ok(Self::default());
        }
        let mut border_values = value.split_ascii_whitespace_excluding_group('(', ')');
        Ok(match border_values.clone().count() {
            3 => {
                let width = border_values
                    .next()
                    .ok_or(ParseError)?
                    .parse::<f32>()
                    .map_err(|_| ParseError)?;
                Border {
                    width: BorderWidth {
                        top: width,
                        left: width,
                        bottom: width,
                        right: width,
                    },
                    alignment: BorderAlignment::parse(border_values.next().ok_or(ParseError)?)?,
                    fill: Fill::parse(&border_values.collect::<Vec<&str>>().join(" "))
                        .map_err(|_| ParseError)?,
                }
            }
            4 => {
                let vertical_width = border_values
                    .next()
                    .ok_or(ParseError)?
                    .parse::<f32>()
                    .map_err(|_| ParseError)?;
                let horizontal_width = border_values
                    .next()
                    .ok_or(ParseError)?
                    .parse::<f32>()
                    .map_err(|_| ParseError)?;
                Border {
                    width: BorderWidth {
                        top: vertical_width,
                        left: horizontal_width,
                        bottom: vertical_width,
                        right: horizontal_width,
                    },
                    alignment: BorderAlignment::parse(border_values.next().ok_or(ParseError)?)?,
                    fill: Fill::parse(&border_values.collect::<Vec<&str>>().join(" "))
                        .map_err(|_| ParseError)?,
                }
            }
            5 => {
                let top_width = border_values
                    .next()
                    .ok_or(ParseError)?
                    .parse::<f32>()
                    .map_err(|_| ParseError)?;
                let horizontal_width = border_values
                    .next()
                    .ok_or(ParseError)?
                    .parse::<f32>()
                    .map_err(|_| ParseError)?;
                let bottom_width = border_values
                    .next()
                    .ok_or(ParseError)?
                    .parse::<f32>()
                    .map_err(|_| ParseError)?;
                Border {
                    width: BorderWidth {
                        top: top_width,
                        left: horizontal_width,
                        bottom: bottom_width,
                        right: horizontal_width,
                    },
                    alignment: BorderAlignment::parse(border_values.next().ok_or(ParseError)?)?,
                    fill: Fill::parse(&border_values.collect::<Vec<&str>>().join(" "))
                        .map_err(|_| ParseError)?,
                }
            }
            6 => Border {
                width: BorderWidth {
                    top: border_values
                        .next()
                        .ok_or(ParseError)?
                        .parse::<f32>()
                        .map_err(|_| ParseError)?,
                    right: border_values
                        .next()
                        .ok_or(ParseError)?
                        .parse::<f32>()
                        .map_err(|_| ParseError)?,
                    bottom: border_values
                        .next()
                        .ok_or(ParseError)?
                        .parse::<f32>()
                        .map_err(|_| ParseError)?,
                    left: border_values
                        .next()
                        .ok_or(ParseError)?
                        .parse::<f32>()
                        .map_err(|_| ParseError)?,
                },
                alignment: BorderAlignment::parse(border_values.next().ok_or(ParseError)?)?,
                fill: Fill::parse(&border_values.collect::<Vec<&str>>().join(" "))
                    .map_err(|_| ParseError)?,
            },
            _ => return Err(ParseError),
        })
    }
}
impl fmt::Display for Border {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{} {} {}", self.width, self.alignment, self.fill,)
    }
}
impl Scaled for Border {
    fn scale(&mut self, scale_factor: f32) {
        self.width.scale(scale_factor);
    }
}