#![allow(unused_imports)]
#![recursion_limit = "1000"]
use std::fmt;

use yew::prelude::*;
pub mod button;
pub mod canvas;
pub mod container;
pub mod text;

pub type Lvl = u8;
macro_rules! unpack_unit {
    ( $x:ident) => {
        let $x = match $x {
            Unit::Pixel(value) => value,
            Unit::Float(value) => value,
            Unit::Percent(value) => value,
            Unit::Rem(value)=> value,
            Unit::Em(value) => value,
        };
    };
}
#[derive(Clone, Debug, PartialEq)]
pub enum Elem {
    Div,
    Button,
    Anchor(String),
    Paragraph,
    Heading(Lvl),
    Footer,
    Header,
    Nav,
    Span,
    Input,
    OrderedList,
    UnorderedList,
    ListItem,
}
impl Default for Elem {
    fn default() -> Self {
        Self::Div
    }
}
#[derive(Clone, Debug, PartialEq)]
pub enum Positional {
    Single(Unit),
    Double((Unit, Unit)),
    Quad((Unit, Unit, Unit, Unit)),
}
impl Default for Positional {
    fn default() -> Self {
        Self::Single(Unit::default())
    }
}
impl fmt::Display for Positional {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Self::Single(value) => {
                write!(f, "{}", format!("{}", value))
            }
            Self::Double((x, y)) => {
                unpack_unit!(x);
                unpack_unit!(y);
                write!(f, "{}, {}", x, y)
            }
            Self::Quad((x, y, z, w)) => {
                unpack_unit!(x);
                unpack_unit!(y);
                unpack_unit!(z);
                unpack_unit!(w);
                write!(f, "{:}, {:}, {:}, {:}", x, y, z, w)
            }
        }
    }
}
#[derive(Clone, Debug, PartialEq)]
pub enum CssString{
    Value(String)
} 
impl CssString {
    fn from(value: String)->Self{
        Self::Value(value)
    }
}
impl Default for CssString {
    fn default() -> Self {
        CssString::Value(String::from("unset"))
    }
}
impl fmt::Display for CssString{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let value = match self { Self::Value(ref s) => s };
        write!(f, "{}", value)
    }
}
#[derive(Clone, Debug, PartialEq)]
pub enum Unit {
    Pixel(f32),
    Float(f32),
    Percent(f32),
    Rem(f32),
    Em(f32),
}
impl Default for Unit {
    fn default() -> Self {
        Self::Pixel(0.0)
    }
}
impl fmt::Display for Unit {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Self::Pixel(value) => {
                write!(f, "{:}px", value)
            }
            Self::Float(value) => {
                write!(f, "{:}fr", value)
            }
            Self::Percent(value) => {
                write!(f, "{:}%", value)
            }
            Self::Rem(value)=> {
              write!(f, "{:}rem", value)
            }
            Self::Em(value) => {
              write!(f, "{:}em", value)
            }
        }
    }
}
#[derive(Debug, Clone, PartialEq)]
pub enum Color {
    Named(CssString),
    Hex(CssString),
}
impl Color {
  fn yellow() -> Self {
    Self::Named(CssString::from(format!("yellow")))
  }
  fn black() -> Self {
    Self::Named(CssString::from(format!("black")))
  }
  fn white() -> Self {
    Self::Named(CssString::from(format!("white")))
  }
  fn red() -> Self {
    Self::Named(CssString::from(format!("red")))
  }
  fn blue() ->Self {
    Self::Named(CssString::from(format!("blue")))
  }
  fn green()-> Self {
    Self::Named(CssString::from(format!("green")))
  }
  fn orange()->Self {
    Self::Named(CssString::from(format!("orange")))
  }

}
impl Default for Color {
    fn default() -> Color {
        Self::Named(CssString::default())
    }
}
impl fmt::Display for Color {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Self::Named(value) => write!(f, "{:}", value),
            Self::Hex(value) => write!(f, "#{:}", value),
        }
    }
}

#[derive(Debug, Clone, PartialEq)]
pub enum MultiValue<T> {
    Single(T),
    Double((T, T)),
    Triple((T, T, T)),
    Quad((T, T, T, T)),
}
impl<T: Default> Default for MultiValue<T> {
    fn default() -> Self {
        Self::Single(T::default())
    }
}
impl<T: fmt::Display> fmt::Display for MultiValue<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let output = match self {
            Self::Single(value) => format!("{}", value),
            Self::Double((x, y)) => format!("{}, {}", x, y),
            Self::Triple((x, y, z)) => format!("{}, {}, {}", x, y, z),
            Self::Quad((x, y, z, w)) => format!("{}, {}, {}, {}", x, y, z, w),
        };
        write!(f, "{:}", output)
    }
}
#[cfg(test)]
mod test {
    use super::*;
    #[test]
    fn unit_formats_default() {
        let value = Unit::default();
        assert_eq!(format!("{}", value), format!("0px"));
    }
    #[test]
    fn unit_formats_pixel() {
        let value = Unit::Pixel(0.0);
        assert_eq!(format!("{}", value), format!("0px"));
    }
    #[test]
    fn unit_formats_percent() {
        let value = Unit::Percent(0.0);
        assert_eq!(format!("{}", value), format!("0%"));
    }
    #[test]
    fn multivalue_formats_default() {
        let value = MultiValue::<Unit>::default();
        assert_eq!(format!("{}", value), format!("0px"));
        let value = MultiValue::<CssString>::default();
        assert_eq!(format!("{}", value), format!("unset"));
    }
    #[test]
    fn multivalue_formats_percent() {
        let value = MultiValue::Single(Unit::Percent(0.0));
        assert_eq!(format!("{}", value), format!("0%"));
        let value = MultiValue::Double((Unit::Percent(0.0), Unit::Percent(0.0)));
        assert_eq!(format!("{}", value), format!("0%, 0%"));
        let value = MultiValue::Triple((Unit::Percent(0.1), Unit::Percent(0.0), Unit::Percent(0.0)));
        assert_eq!(format!("{}", value), format!("0.1%, 0%, 0%"));
        let value = MultiValue::Quad((Unit::Percent(0.0), Unit::Percent(0.0), Unit::Percent(0.0), Unit::Percent(0.0)));
        assert_eq!(format!("{}", value), format!("0%, 0%, 0%, 0%"));
    }
    #[test]
    fn color_formats_default() {
        let value = Color::default();
        assert_eq!(format!("{}", value), format!("unset"))
    }
    #[test]
    fn color_formats_named(){
        let value = Color::Named(CssString::default());
        assert_eq!(format!("{}", value), format!("unset"));
        let value = Color::red();
        assert_eq!(format!("{}", value), format!("red"));
    }
    #[test]
    fn color_formats_hex(){
        let value = Color::Hex(CssString::Value(format!("000000")));
        assert_eq!(format!("{}", value), format!("#000000"));
    }
    #[test]
    fn multivalue_formats_colors(){
      let value = MultiValue::Single(Color::red());
      assert_eq!(format!("{}", value), format!("red"));
      let value = MultiValue::Double((Color::green(), Color::blue()));
      assert_eq!(format!("{}", value), format!("green, blue"));
      let value = MultiValue::Triple((Color::green(), Color::blue(), Color::green()));
      assert_eq!(format!("{}", value), format!("green, blue, green"));
    }
}

