use crate::colors::Color;
use serde::Serialize;

#[derive(Debug, Clone, Serialize)]
pub struct Border {
	pub color: Color<'static>,
	pub width: Dimension,
	pub style: StrokeStyle,
}

impl Border {
	pub fn from(color: Color<'static>, width: Dimension, style: StrokeStyle) -> Self {
		Self { color, width, style }
	}
}

#[derive(Debug, Clone, Serialize)]
pub struct GradientStop {
	pub color: Color<'static>,
	pub position: f32,
}

impl GradientStop {
	pub fn set_position(&mut self, position: f32) {
		self.position = f32::clamp(position, 0.0, 1.0);
	}
	pub fn from(color: Color<'static>, position: f32) -> Self {
		Self { color, position }
	}
}

#[derive(Debug, Clone, Serialize)]
pub struct Gradient {
	pub stops: Vec<GradientStop>,
}

impl Gradient {
	pub fn from(stops: Vec<GradientStop>) -> Result<Self, String> {
		if stops.len() < 2 {
			return Err(String::from(
				"there need to be at least two 'GradientStop' to make a 'Gradient'; You have less than 2",
			));
		}
		Ok(Self { stops })
	}
}

#[derive(Debug, Clone, Serialize)]
pub struct Shadow {
	pub offset_x: Dimension,
	pub offset_y: Dimension,
	pub blur: Dimension,
	pub spread: Dimension,
	pub color: Color<'static>,
	pub inset: bool,
}

impl Shadow {
	pub fn from(
		offset_x: Dimension,
		offset_y: Dimension,
		blur: Dimension,
		spread: Dimension,
		color: Color<'static>,
		inset: Option<bool>,
	) -> Self {
		Self {
			offset_x,
			offset_y,
			blur,
			spread,
			color,
			inset: match inset {
				Some(value) => value,
				None => false,
			},
		}
	}
}

#[derive(Debug, Clone, Serialize)]
pub enum LineCap {
	Round,
	Butt,
	Square,
}

#[derive(Debug, Clone, Serialize)]
pub enum SimpleStokeStyle {
	Solid,
	Dashed,
	Double,
	Groove,
	Ridge,
	Outset,
	Inset,
}

#[derive(Debug, Clone, Serialize)]
pub struct ComplexStrokeStyle {
	pub dash_array: Vec<Dimension>,
	pub line_cap: LineCap,
}

#[derive(Debug, Clone, Serialize)]
pub enum StrokeStyle {
	Simple(SimpleStokeStyle),
	Complex(ComplexStrokeStyle),
}

impl StrokeStyle {
	pub fn from(dash_array: Vec<Dimension>, line_cap: LineCap) -> Self {
		Self::Complex(ComplexStrokeStyle { dash_array, line_cap })
	}
}

#[derive(Debug, Clone, Serialize)]
pub struct Transition {
	pub duration: Duration,
	pub delay: Duration,
	pub timing_function: CubicBezier,
}

impl Transition {
	pub fn from(duration: Duration, delay: Duration, timing_function: CubicBezier) -> Self {
		Self {
			duration,
			delay,
			timing_function,
		}
	}
}

#[derive(Debug, Clone, Serialize)]
pub struct Typography {
	pub font_family: FontFamily,
	pub font_size: Dimension,
	pub font_weight: FontWeight,
	pub letter_spacing: Dimension,
	pub line_height: f32,
}

impl Typography {
	pub fn from(
		font_family: FontFamily,
		font_size: Dimension,
		font_weight: FontWeight,
		letter_spacing: Dimension,
		line_height: f32,
	) -> Self {
		Self {
			font_family,
			font_size,
			font_weight,
			letter_spacing,
			line_height,
		}
	}
}

#[derive(Debug, Clone, Serialize)]
pub struct CubicBezier {
	pub p1x: f32,
	pub p1y: f32,
	pub p2x: f32,
	pub p2y: f32,
}

impl CubicBezier {
	pub fn from(p1x: f32, p1y: f32, p2x: f32, p2y: f32) -> Self {
		Self { p1x, p1y, p2x, p2y }
	}
	pub fn set_p1x(&mut self, p1x: f32) {
		self.p1x = f32::clamp(p1x, 0.0, 1.0);
	}
	pub fn set_p2x(&mut self, p2x: f32) {
		self.p2x = f32::clamp(p2x, 0.0, 1.0);
	}
}

#[derive(Debug, Clone, Serialize)]
pub enum DimensionalUnit {
	Px,
	Rem,
}

#[derive(Debug, Clone, Serialize)]
pub struct Dimension {
	pub value: Number,
	pub unit: DimensionalUnit,
}

impl Dimension {
	pub fn from(value: Number, unit: DimensionalUnit) -> Self {
		Self { value, unit }
	}
}

#[derive(Debug, Clone, Serialize)]
pub enum TemporalUnit {
	Seconds,
	MilliSeconds,
}

#[derive(Debug, Clone, Serialize)]
pub struct Duration {
	pub value: Number,
	pub unit: TemporalUnit,
}

impl Duration {
	pub fn from(value: Number, unit: TemporalUnit) -> Self {
		Self { value, unit }
	}
}

#[derive(Debug, Clone, Serialize)]
pub struct FontFamily {
	pub families: &'static [&'static str],
}

impl FontFamily {
	pub fn from(families: &'static [&str]) -> Self {
		Self { families }
	}
}

#[derive(Debug, Clone, Serialize)]
pub enum FontWeight {
	W100,
	Thin,
	Hairline,
	W200,
	ExtraLight,
	UltraLight,
	W300,
	Light,
	W400,
	Normal,
	Regular,
	Book,
	W500,
	Medium,
	W600,
	SemiBold,
	DemiBold,
	W700,
	Bold,
	W800,
	ExtraBold,
	UltraBold,
	W900,
	Black,
	Heavy,
	W950,
	ExtraBlack,
	UltraBlack,
}

#[derive(Debug, Clone, Serialize)]
pub enum Number {
	Integer8(i8),
	Unsigned8(u8),
	Integer16(i16),
	Unsigned16(u16),
	Float32(f32),
}

impl From<u8> for Number {
	fn from(input: u8) -> Self {
		Self::Unsigned8(input)
	}
}

impl From<i8> for Number {
	fn from(input: i8) -> Self {
		Self::Integer8(input)
	}
}

impl From<u16> for Number {
	fn from(input: u16) -> Self {
		Self::Unsigned16(input)
	}
}

impl From<i16> for Number {
	fn from(input: i16) -> Self {
		Self::Integer16(input)
	}
}

impl From<f32> for Number {
	fn from(input: f32) -> Self {
		Self::Float32(input)
	}
}

#[derive(Debug, Clone, Serialize)]
pub enum StylisticSetId {
	Ss01,
	Ss02,
	Ss03,
	Ss04,
	Ss05,
	Ss06,
	Ss07,
	Ss08,
	Ss09,
	Ss10,
	Ss11,
	Ss12,
	Ss13,
	Ss14,
	Ss15,
	Ss16,
	Ss17,
	Ss18,
	Ss19,
	Ss20,
}

#[derive(Debug, Clone, Serialize)]
pub struct StylisticSet {
	pub id: StylisticSetId,
	pub value: bool,
}

impl StylisticSet {
	pub fn from(id: StylisticSetId, value: bool) -> Self {
		Self { id, value }
	}
}

#[derive(Debug, Clone, Serialize)]
pub enum CharacterVariantId {
	Cv01,
	Cv02,
	Cv03,
	Cv04,
	Cv05,
	Cv06,
	Cv07,
	Cv08,
	Cv09,
	Cv10,
	Cv11,
	Cv12,
	Cv13,
	Cv14,
	Cv15,
	Cv16,
	Cv17,
	Cv18,
	Cv19,
	Cv20,
	Cv21,
	Cv22,
	Cv23,
	Cv24,
	Cv25,
	Cv26,
	Cv27,
	Cv28,
	Cv29,
	Cv30,
	Cv31,
	Cv32,
	Cv33,
	Cv34,
	Cv35,
	Cv36,
	Cv37,
	Cv38,
	Cv39,
	Cv40,
	Cv41,
	Cv42,
	Cv43,
	Cv44,
	Cv45,
	Cv46,
	Cv47,
	Cv48,
	Cv49,
	Cv50,
	Cv51,
	Cv52,
	Cv53,
	Cv54,
	Cv55,
	Cv56,
	Cv57,
	Cv58,
	Cv59,
	Cv60,
	Cv61,
	Cv62,
	Cv63,
	Cv64,
	Cv65,
	Cv66,
	Cv67,
	Cv68,
	Cv69,
	Cv70,
	Cv71,
	Cv72,
	Cv73,
	Cv74,
	Cv75,
	Cv76,
	Cv77,
	Cv78,
	Cv79,
	Cv80,
	Cv81,
	Cv82,
	Cv83,
	Cv84,
	Cv85,
	Cv86,
	Cv87,
	Cv88,
	Cv89,
	Cv90,
	Cv91,
	Cv92,
	Cv93,
	Cv94,
	Cv95,
	Cv96,
	Cv97,
	Cv98,
	Cv99,
}

#[derive(Debug, Clone, Serialize)]
pub struct CharacterVariant {
	pub id: CharacterVariantId,
	pub value: bool,
}

impl CharacterVariant {
	pub fn from(id: CharacterVariantId, value: bool) -> Self {
		Self { id, value }
	}
}

#[derive(Debug, Clone, Serialize)]
pub enum FontFeatureId {
	Aalt,
	Abvf,
	Abvm,
	Abvs,
	Afrc,
	Akhn,
	Apkn,
	Blwf,
	Blwm,
	Blws,
	Calt,
	Case,
	Ccmp,
	Cfar,
	Chws,
	Cjct,
	Clig,
	Cpct,
	Cpsp,
	Cswh,
	Curs,
	C2pc,
	C2sc,
	Dist,
	Dlig,
	Dnom,
	Dtls,
	Expt,
	Falt,
	Fin2,
	Fin3,
	Fina,
	Flac,
	Frac,
	Fwid,
	Half,
	Haln,
	Halt,
	Hist,
	Hkna,
	Hlig,
	Hngl,
	Hojo,
	Hwid,
	Init,
	Isol,
	Ital,
	Jalt,
	Jp78,
	Jp83,
	Jp90,
	Jp04,
	Kern,
	Lfbd,
	Liga,
	Ljmo,
	Lnum,
	Locl,
	Ltra,
	Ltrm,
	Mark,
	Med2,
	Medi,
	Mgrk,
	Mkmk,
	Mset,
	Nalt,
	Nlck,
	Nukt,
	Numr,
	Onum,
	Opbd,
	Ordn,
	Ornm,
	Palt,
	Pcap,
	Pkna,
	Pnum,
	Pref,
	Pres,
	Pstf,
	Psts,
	Pwid,
	Qwid,
	Rand,
	Rclt,
	Rkrf,
	Rlig,
	Rphf,
	Rtbd,
	Rtla,
	Rtlm,
	Ruby,
	Rvrn,
	Salt,
	Sinf,
	Size,
	Smcp,
	Smpl,
	Ssty,
	Stch,
	Subs,
	Sups,
	Swsh,
	Titl,
	Tjmo,
	Tnam,
	Tnum,
	Trad,
	Twid,
	Unic,
	Valt,
	Vapk,
	Vatu,
	Vchw,
	Vert,
	Vhal,
	Vjmo,
	Vkna,
	Vkrn,
	Vpal,
	Vrt2,
	Vrtr,
	Zero,
}

#[derive(Debug, Clone, Serialize)]
pub struct FontFeature {
	pub id: FontFeatureId,
	pub value: u8,
}

impl FontFeature {
	pub fn from(id: FontFeatureId, value: u8) -> Self {
		Self { id, value }
	}
}

#[derive(Debug, Clone, Serialize)]
pub struct FontFeatureSettings {
	/// Stylistic Set 1 – Stylistic Set 20
	#[serde(skip_serializing_if = "Option::is_none")]
	pub stylistic_sets: Option<Vec<StylisticSet>>,
	/// Character Variant 1 – Character Variant 99
	#[serde(skip_serializing_if = "Option::is_none")]
	pub character_variants: Option<Vec<CharacterVariant>>,
	/// Font Features
	#[serde(skip_serializing_if = "Option::is_none")]
	pub font_features: Option<Vec<FontFeature>>,
}

impl FontFeatureSettings {
	pub fn from(
		font_features: Option<Vec<FontFeature>>,
		stylistic_sets: Option<Vec<StylisticSet>>,
		character_variants: Option<Vec<CharacterVariant>>,
	) -> Self {
		Self {
			font_features,
			stylistic_sets,
			character_variants,
		}
	}
}

#[derive(Debug, Clone, Serialize)]
pub enum FontStyle {
	Normal,
	Italic,
}

#[derive(Debug, Clone, Serialize)]
pub enum FontVariantCaps {
	Normal,
	SmallCaps,
	AllSmallCaps,
	PetiteCaps,
	AllPetiteCaps,
	Unicase,
	TitlingCaps,
}

#[derive(Debug, Clone, Serialize)]
pub enum FontVariantEastAsian {
	Normal,
	Jis78,
	Jis83,
	Jis90,
	Jis04,
	Simplified,
	Traditional,
	FullWidth,
	ProportionalWidth,
	Ruby,
}

#[derive(Debug, Clone, Serialize)]
pub enum FontVariantLigatures {
	Normal,
	None,
	CommonLigatures,
	NoCommonLigatures,
	DiscretionaryLigatures,
	NoDiscretionaryLigatures,
	HistoricalLigatures,
	NoHistoricalLigatures,
	Contextual,
	NoContextual,
}

#[derive(Debug, Clone, Serialize)]
pub enum FontVariantNumeric {
	Normal,
	Ordinal,
	SlashedZero,
	LiningNums,
	OldstyleNums,
	ProportionalNums,
	TabularNum,
	DiagonalFractions,
	StackedFractions,
}

#[derive(Debug, Clone, Serialize)]
pub struct FontVariationSettings {
	/// Used to vary stroke thicknesses or other design details to give variation from lighter to blacker;
	/// behaves the same as the the CSS font-weight property.
	#[serde(skip_serializing_if = "Option::is_none")]
	pub weight: Option<u16>,
	/// Used to vary width of text from narrower to wider; Values can be interpreted as a percentage of whatever
	/// the font designer considers “normal width” for that font design.
	#[serde(skip_serializing_if = "Option::is_none")]
	pub width: Option<f32>,
	/// Used to vary between upright and slanted text; Values can be interpreted as the angle, in counter-clockwise
	/// degrees, of oblique slant from whatever the designer considers to be upright for that font design.
	#[serde(skip_serializing_if = "Option::is_none")]
	pub slant: Option<f32>,
	///  Used to vary between non-italic and italic;  A value of 0 can be interpreted as “Roman” (non-italic);
	/// a value of 1 can be interpreted as (fully) italic
	#[serde(skip_serializing_if = "Option::is_none")]
	pub italic: Option<bool>,
	/// Used to vary design to suit different text sizes; Values can be interpreted as text size, in typographic points,
	/// as defined in the OpenType specification: a physical unit equal to 1/72 of a standard physical inch
	#[serde(skip_serializing_if = "Option::is_none")]
	pub optical_sizing: Option<f32>,
}

impl FontVariationSettings {
	pub fn from(
		weight: Option<u16>,
		width: Option<f32>,
		slant: Option<f32>,
		italic: Option<bool>,
		optical_sizing: Option<f32>,
	) -> Self {
		Self {
			weight,
			width,
			slant,
			italic,
			optical_sizing,
		}
	}
}

#[derive(Debug, Clone, Serialize)]
pub enum TextAlign {
	Start,
	End,
	Left,
	Right,
	Center,
	Justify,
	MatchParent,
	JustifyAll,
}

#[derive(Debug, Clone, Serialize)]
pub enum TextDecorationLine {
	None,
	Underline,
	Overline,
	LineThrough,
}

#[derive(Debug, Clone, Serialize)]
pub enum TextDecorationStyle {
	Solid,
	Double,
	Dotted,
	Dashed,
	Wavy,
}

#[derive(Debug, Clone, Serialize)]
pub enum TextUnderlinePosition {
	Auto,
	Under,
	Left,
	Right,
}

#[derive(Debug, Clone, Serialize)]
pub enum ValueContainer {
	Border(Border),
	Gradient(Gradient),
	Shadow(Shadow),
	StrokeStyle(StrokeStyle),
	Transition(Transition),
	Typography(Typography),
	CubicBezier(CubicBezier),
	Dimension(Dimension),
	Duration(Duration),
	FontFamily(FontFamily),
	FontWeight(FontWeight),
	Number(Number),
	FontFeatureSettings(FontFeatureSettings),
	FontStyle(FontStyle),
	FontVariantCaps(FontVariantCaps),
	FontVariantEastAsian(FontVariantEastAsian),
	FontVariantLigatures(FontVariantLigatures),
	FontVariantNumeric(FontVariantNumeric),
	FontVariationSettings(FontVariationSettings),
	TextAlign(TextAlign),
	TextDecorationLine(TextDecorationLine),
	TextDecorationStyle(TextDecorationStyle),
	TextUnderlinePosition(TextUnderlinePosition),
	Color(Color<'static>),
}

#[derive(Debug, Clone, Default, Serialize)]
pub struct StateContainer {
	/// the state when the cursor hovers over the element
	#[serde(skip_serializing_if = "Option::is_none")]
	pub hover: Option<VariantContainer>,
	/// the state when the element is in focus
	#[serde(skip_serializing_if = "Option::is_none")]
	pub focus: Option<VariantContainer>,
	/// the state when the element is active, regarding plain text: it means it's selected
	#[serde(skip_serializing_if = "Option::is_none")]
	pub active: Option<VariantContainer>,
	/// the state when the element is inactive, but more distinct than normal
	#[serde(skip_serializing_if = "Option::is_none")]
	pub inactive: Option<VariantContainer>,
	/// the normal state of the element
	#[serde(skip_serializing_if = "Option::is_none")]
	pub normal: Option<VariantContainer>,
	/// the state when the element is loading
	#[serde(skip_serializing_if = "Option::is_none")]
	pub load: Option<VariantContainer>,
	/// the state when the element is being blocked or can not proceed
	#[serde(skip_serializing_if = "Option::is_none")]
	pub block: Option<VariantContainer>,
	/// the state when the element was successful in doing something
	#[serde(skip_serializing_if = "Option::is_none")]
	pub success: Option<VariantContainer>,
	/// the state when something is wrong with the element
	#[serde(skip_serializing_if = "Option::is_none")]
	pub error: Option<VariantContainer>,
	/// the state when the element wants to warn about something
	#[serde(skip_serializing_if = "Option::is_none")]
	pub warn: Option<VariantContainer>,
	/// the state when the element holds additional information
	#[serde(skip_serializing_if = "Option::is_none")]
	pub info: Option<VariantContainer>,
	/// the state when the element is was newly added
	#[serde(skip_serializing_if = "Option::is_none")]
	pub new: Option<VariantContainer>,
	/// the state when the element has been visited, or was dealt with in the past
	#[serde(skip_serializing_if = "Option::is_none")]
	pub visit: Option<VariantContainer>,
	/// the state when the elements needs immediate attention and want to alert about something, more distinct than warn
	#[serde(skip_serializing_if = "Option::is_none")]
	pub alert: Option<VariantContainer>,
	/// the state when the element is being clicked
	#[serde(skip_serializing_if = "Option::is_none")]
	pub click: Option<VariantContainer>,
	/// the state when the element is being dragged
	#[serde(skip_serializing_if = "Option::is_none")]
	pub drag: Option<VariantContainer>,
	/// volatile, looses it's highlight after a short while, if unattended.
	#[serde(skip_serializing_if = "Option::is_none")]
	pub highlight: Option<VariantContainer>,
}

#[derive(Debug, Clone, Default, Serialize)]
pub struct LayoutElementContainer {
	/// a icon element
	#[serde(skip_serializing_if = "Option::is_none")]
	pub icon: Option<StateContainer>,
	/// a box element, which is used the most. Think of the HTML `div` element
	#[serde(skip_serializing_if = "Option::is_none")]
	pub boxes: Option<StateContainer>,
	/// a side element, think of a sidebar, HTML `aside`, Androids `Drawer`. A vertical element to the left or right of the main content
	#[serde(skip_serializing_if = "Option::is_none")]
	pub side: Option<StateContainer>,
	/// an image element
	#[serde(skip_serializing_if = "Option::is_none")]
	pub image: Option<StateContainer>,
	/// a popover/popup element
	#[serde(skip_serializing_if = "Option::is_none")]
	pub popover: Option<StateContainer>,
	/// the list element, containing list items
	#[serde(skip_serializing_if = "Option::is_none")]
	pub list: Option<StateContainer>,
	/// a indicator element, can be something like a dot besides text, a colored border,...
	#[serde(skip_serializing_if = "Option::is_none")]
	pub indicator: Option<StateContainer>,
	/// a panel element. more distinct than the box element
	#[serde(skip_serializing_if = "Option::is_none")]
	pub panel: Option<StateContainer>,
	/// a row element
	#[serde(skip_serializing_if = "Option::is_none")]
	pub row: Option<StateContainer>,
	/// the column element
	#[serde(skip_serializing_if = "Option::is_none")]
	pub column: Option<StateContainer>,
	/// the cell in a table. less distinct than the box element
	#[serde(skip_serializing_if = "Option::is_none")]
	pub cell: Option<StateContainer>,
	/// the window of an application
	#[serde(skip_serializing_if = "Option::is_none")]
	pub window: Option<StateContainer>,
}

#[derive(Debug, Clone, Default, Serialize)]
pub struct TextElementContainer {
	/// a button element
	#[serde(skip_serializing_if = "Option::is_none")]
	pub button: Option<StateContainer>,
	/// a badge element
	#[serde(skip_serializing_if = "Option::is_none")]
	pub badge: Option<StateContainer>,
	/// a link element
	#[serde(skip_serializing_if = "Option::is_none")]
	pub link: Option<StateContainer>,
	/// a label element
	#[serde(skip_serializing_if = "Option::is_none")]
	pub label: Option<StateContainer>,
	/// a header text element
	#[serde(skip_serializing_if = "Option::is_none")]
	pub header: Option<StateContainer>,
	/// a body text element, which is used the most. Think of HTML paragraph `p`
	#[serde(skip_serializing_if = "Option::is_none")]
	pub paragraph: Option<StateContainer>,
	/// a small text element
	#[serde(skip_serializing_if = "Option::is_none")]
	pub small: Option<StateContainer>,
	/// a code-text/preformatted-text element
	#[serde(skip_serializing_if = "Option::is_none")]
	pub code: Option<StateContainer>,
	/// a 'select one from many' element. Different from 'tab', which when pressed changes the content of the much of screen space
	#[serde(skip_serializing_if = "Option::is_none")]
	pub select: Option<StateContainer>,
	/// a text input element
	#[serde(skip_serializing_if = "Option::is_none")]
	pub input: Option<StateContainer>,
	/// a legend element
	#[serde(skip_serializing_if = "Option::is_none")]
	pub legend: Option<StateContainer>,
	/// a quoted text element
	#[serde(skip_serializing_if = "Option::is_none")]
	pub quote: Option<StateContainer>,
	/// a notification element
	#[serde(skip_serializing_if = "Option::is_none")]
	pub notification: Option<StateContainer>,
	/// element in a set of similar items, ordered horizontally; horizontal version of `entry`
	#[serde(skip_serializing_if = "Option::is_none")]
	pub tab: Option<StateContainer>,
	/// element in a set of similar items,  ordered vertically; vertical version of `tab`
	#[serde(skip_serializing_if = "Option::is_none")]
	pub entry: Option<StateContainer>,
	/// description for another element
	#[serde(skip_serializing_if = "Option::is_none")]
	pub caption: Option<StateContainer>,
}

#[derive(Debug, Clone, Default, Serialize)]
pub struct ElementContainer {
	/// a icon element
	#[serde(skip_serializing_if = "Option::is_none")]
	pub icon: Option<StateContainer>,
	/// a box element, which is used the most. Think of the HTML `div` element
	#[serde(skip_serializing_if = "Option::is_none")]
	pub boxes: Option<StateContainer>,
	/// a side element, think of a sidebar, HTML `aside`, Androids `Drawer`. A vertical element to the left or right of the main content
	#[serde(skip_serializing_if = "Option::is_none")]
	pub side: Option<StateContainer>,
	/// an image element
	#[serde(skip_serializing_if = "Option::is_none")]
	pub image: Option<StateContainer>,
	/// a popover/popup element
	#[serde(skip_serializing_if = "Option::is_none")]
	pub popover: Option<StateContainer>,
	/// the list element, containing list items
	#[serde(skip_serializing_if = "Option::is_none")]
	pub list: Option<StateContainer>,
	/// a indicator element, can be something like a dot besides text, a colored border,...
	#[serde(skip_serializing_if = "Option::is_none")]
	pub indicator: Option<StateContainer>,
	/// a panel element. more distinct than the box element
	#[serde(skip_serializing_if = "Option::is_none")]
	pub panel: Option<StateContainer>,
	/// a row element
	#[serde(skip_serializing_if = "Option::is_none")]
	pub row: Option<StateContainer>,
	/// the column element
	#[serde(skip_serializing_if = "Option::is_none")]
	pub column: Option<StateContainer>,
	/// the cell in a table. less distinct than the box element
	#[serde(skip_serializing_if = "Option::is_none")]
	pub cell: Option<StateContainer>,
	/// the window of an application
	#[serde(skip_serializing_if = "Option::is_none")]
	pub window: Option<StateContainer>,
	/// a button element
	#[serde(skip_serializing_if = "Option::is_none")]
	pub button: Option<StateContainer>,
	/// a badge element
	#[serde(skip_serializing_if = "Option::is_none")]
	pub badge: Option<StateContainer>,
	/// a link element
	#[serde(skip_serializing_if = "Option::is_none")]
	pub link: Option<StateContainer>,
	/// a label element
	#[serde(skip_serializing_if = "Option::is_none")]
	pub label: Option<StateContainer>,
	/// a header text element
	#[serde(skip_serializing_if = "Option::is_none")]
	pub header: Option<StateContainer>,
	/// a body text element, which is used the most. Think of HTML paragraph `p`
	#[serde(skip_serializing_if = "Option::is_none")]
	pub paragraph: Option<StateContainer>,
	/// a small text element
	#[serde(skip_serializing_if = "Option::is_none")]
	pub small: Option<StateContainer>,
	/// a code-text/preformatted-text element
	#[serde(skip_serializing_if = "Option::is_none")]
	pub code: Option<StateContainer>,
	/// a 'select one from many' element. Different from 'tab', which when pressed changes the content of the much of screen space
	#[serde(skip_serializing_if = "Option::is_none")]
	pub select: Option<StateContainer>,
	/// a text input element
	#[serde(skip_serializing_if = "Option::is_none")]
	pub input: Option<StateContainer>,
	/// a legend element
	#[serde(skip_serializing_if = "Option::is_none")]
	pub legend: Option<StateContainer>,
	/// a quoted text element
	#[serde(skip_serializing_if = "Option::is_none")]
	pub quote: Option<StateContainer>,
	/// a notification element
	#[serde(skip_serializing_if = "Option::is_none")]
	pub notification: Option<StateContainer>,
	/// element in a set of similar items, ordered horizontally; horizontal version of `entry`
	#[serde(skip_serializing_if = "Option::is_none")]
	pub tab: Option<StateContainer>,
	/// element in a set of similar items,  ordered vertically; vertical version of `tab`
	#[serde(skip_serializing_if = "Option::is_none")]
	pub entry: Option<StateContainer>,
	/// description for another element
	#[serde(skip_serializing_if = "Option::is_none")]
	pub caption: Option<StateContainer>,
}

#[derive(Debug, Clone, Serialize)]
pub struct VariantContainer {
	/// the default value variant for the state
	pub default: ValueContainer,
	/// Variant 1 (Primary)
	#[serde(skip_serializing_if = "Option::is_none")]
	pub v1: Option<ValueContainer>,
	/// Variant 2 (Secondary)
	#[serde(skip_serializing_if = "Option::is_none")]
	pub v2: Option<ValueContainer>,
	/// Variant 3 (Tertiary)
	#[serde(skip_serializing_if = "Option::is_none")]
	pub v3: Option<ValueContainer>,
	/// Variant 4 (Quaternary)
	#[serde(skip_serializing_if = "Option::is_none")]
	pub v4: Option<ValueContainer>,
	/// Variant 5 (Quintary)
	#[serde(skip_serializing_if = "Option::is_none")]
	pub v5: Option<ValueContainer>,
	/// Variant 6 (Senary)
	#[serde(skip_serializing_if = "Option::is_none")]
	pub v6: Option<ValueContainer>,
	/// Variant 7 (Septenary)
	#[serde(skip_serializing_if = "Option::is_none")]
	pub v7: Option<ValueContainer>,
	/// Variant 8 (Octonary)
	#[serde(skip_serializing_if = "Option::is_none")]
	pub v8: Option<ValueContainer>,
	/// Variant 9 (Nonary)
	#[serde(skip_serializing_if = "Option::is_none")]
	pub v9: Option<ValueContainer>,
}

/// everything related to color
#[derive(Debug, Clone, Default, Serialize)]
pub struct TokenColor {
	/// the background property for background-colors
	#[serde(skip_serializing_if = "Option::is_none")]
	pub background: Option<ElementContainer>,
	/// the border property
	#[serde(skip_serializing_if = "Option::is_none")]
	pub border: Option<ElementContainer>,
	/// the outline property
	#[serde(skip_serializing_if = "Option::is_none")]
	pub outline: Option<ElementContainer>,
	/// the text property contains the text colors, to be more precise, the coloring/fill of the glyphs
	#[serde(skip_serializing_if = "Option::is_none")]
	pub text: Option<TextElementContainer>,
	/// the text property contains the text-decoration colors, behaves just like the CSS property
	#[serde(skip_serializing_if = "Option::is_none")]
	pub text_decoration_color: Option<TextElementContainer>,
}

/// everything related to typography
#[derive(Debug, Clone, Default, Serialize)]
pub struct TokenTypo {
	/// the font family
	#[serde(skip_serializing_if = "Option::is_none")]
	pub font_family: Option<TextElementContainer>,
	/// set flags to change the appearance of the font
	#[serde(skip_serializing_if = "Option::is_none")]
	pub font_feature_settings: Option<TextElementContainer>,
	/// the font size
	#[serde(skip_serializing_if = "Option::is_none")]
	pub font_size: Option<TextElementContainer>,
	/// the font weight
	#[serde(skip_serializing_if = "Option::is_none")]
	pub font_weight: Option<TextElementContainer>,
	/// the ligature (joining of two characters into one shape) settings
	#[serde(skip_serializing_if = "Option::is_none")]
	pub font_variant_ligatures: Option<TextElementContainer>,
	/// the caps (alternate glyphs used for small or petite capitals or for titling) settings
	#[serde(skip_serializing_if = "Option::is_none")]
	pub font_variant_caps: Option<TextElementContainer>,
	/// the numeric (usage of alternate glyphs for numbers, fractions, and ordinal markers) settings
	#[serde(skip_serializing_if = "Option::is_none")]
	pub font_variant_numeric: Option<TextElementContainer>,
	/// the asian (the use of alternate glyphs for East Asian scripts, like Japanese and Chinese) settings
	#[serde(skip_serializing_if = "Option::is_none")]
	pub font_variant_east_asian: Option<TextElementContainer>,
	/// font settings for variable fonts
	#[serde(skip_serializing_if = "Option::is_none")]
	pub font_variation_settings: Option<TextElementContainer>,
	/// whether a font should be styled with a `normal`, `italic`, or `oblique` face from its font-family
	#[serde(skip_serializing_if = "Option::is_none")]
	pub font_style: Option<TextElementContainer>,
	/// the spacing between letters
	#[serde(skip_serializing_if = "Option::is_none")]
	pub letter_spacing: Option<TextElementContainer>,
	/// sets the length of space between words and between tags.
	#[serde(skip_serializing_if = "Option::is_none")]
	pub word_spacing: Option<TextElementContainer>,
	/// the text alignment
	#[serde(skip_serializing_if = "Option::is_none")]
	pub text_align: Option<TextElementContainer>,
	/// the kind of line that is used on text in an element, such as an underline or overline
	#[serde(skip_serializing_if = "Option::is_none")]
	pub text_decoration_line: Option<TextElementContainer>,
	/// the kind of style for the textDecorationLine that is used on text in an element
	#[serde(skip_serializing_if = "Option::is_none")]
	pub text_decoration_style: Option<TextElementContainer>,
	/// sets the stroke thickness of the decoration line
	#[serde(skip_serializing_if = "Option::is_none")]
	pub text_decoration_thickness: Option<TextElementContainer>,
	/// where the underline is positioned relative to the letters
	#[serde(skip_serializing_if = "Option::is_none")]
	pub text_underline_position: Option<TextElementContainer>,
}

/// everything related to spacings, margins, and predefined shapes and dimensions
#[derive(Debug, Clone, Default, Serialize)]
pub struct TokenSpace {
	/// even margin all around
	#[serde(skip_serializing_if = "Option::is_none")]
	pub margin: Option<ElementContainer>,
	/// the top margin
	#[serde(skip_serializing_if = "Option::is_none")]
	pub margin_top: Option<ElementContainer>,
	/// the right margin
	#[serde(skip_serializing_if = "Option::is_none")]
	pub margin_right: Option<ElementContainer>,
	/// the bottom margin
	#[serde(skip_serializing_if = "Option::is_none")]
	pub margin_bottom: Option<ElementContainer>,
	/// the left margin
	#[serde(skip_serializing_if = "Option::is_none")]
	pub margin_left: Option<ElementContainer>,
	/// the top and bottom margin
	#[serde(skip_serializing_if = "Option::is_none")]
	pub margin_block: Option<ElementContainer>,
	/// even padding all around
	#[serde(skip_serializing_if = "Option::is_none")]
	pub padding: Option<ElementContainer>,
	/// the top padding
	#[serde(skip_serializing_if = "Option::is_none")]
	pub padding_top: Option<ElementContainer>,
	/// the right padding
	#[serde(skip_serializing_if = "Option::is_none")]
	pub padding_right: Option<ElementContainer>,
	/// the bottom padding
	#[serde(skip_serializing_if = "Option::is_none")]
	pub padding_bottom: Option<ElementContainer>,
	/// the left padding
	#[serde(skip_serializing_if = "Option::is_none")]
	pub padding_left: Option<ElementContainer>,
	/// the border/corner radius of all corners
	#[serde(skip_serializing_if = "Option::is_none")]
	pub border_radius: Option<ElementContainer>,
	/// the top left border/corner radius
	#[serde(skip_serializing_if = "Option::is_none")]
	pub border_top_left_radius: Option<ElementContainer>,
	/// the the top right border/corner radius
	#[serde(skip_serializing_if = "Option::is_none")]
	pub border_top_right_radius: Option<ElementContainer>,
	/// the the bottom right border/corner radius
	#[serde(skip_serializing_if = "Option::is_none")]
	pub border_bottom_right_radius: Option<ElementContainer>,
	/// the bottom left border/corner radius
	#[serde(skip_serializing_if = "Option::is_none")]
	pub border_bottom_left_radius: Option<ElementContainer>,
	/// predefined max widths
	#[serde(skip_serializing_if = "Option::is_none")]
	pub max_width: Option<ElementContainer>,
	/// predefined max heights
	#[serde(skip_serializing_if = "Option::is_none")]
	pub max_height: Option<ElementContainer>,
	/// the thickens of a border
	#[serde(skip_serializing_if = "Option::is_none")]
	pub border_width: Option<ElementContainer>,
}

/// everything related to animation/motion and effects, shadows, filters and sound
#[derive(Debug, Clone, Default, Serialize)]
pub struct TokenFx {
	/// the duration a an animation
	#[serde(skip_serializing_if = "Option::is_none")]
	pub animation_duration: Option<ElementContainer>,
	/// the duration of a transition
	#[serde(skip_serializing_if = "Option::is_none")]
	pub transition_duration: Option<ElementContainer>,
	/// animation easing functions
	#[serde(skip_serializing_if = "Option::is_none")]
	pub animation_timing_function: Option<ElementContainer>,
	/// transition easing functions
	#[serde(skip_serializing_if = "Option::is_none")]
	pub transition_timing_function: Option<ElementContainer>,
	/// the delay before the start of an animation
	#[serde(skip_serializing_if = "Option::is_none")]
	pub animation_delay: Option<ElementContainer>,
	/// the delay before the start of a transition
	#[serde(skip_serializing_if = "Option::is_none")]
	pub transition_delay: Option<ElementContainer>,
	/// box shadows
	#[serde(skip_serializing_if = "Option::is_none")]
	pub box_shadow: Option<ElementContainer>,
}

impl Default for VariantContainer {
	fn default() -> Self {
		Self {
			default: ValueContainer::Number(Number::from(0_u8)),
			v1: None,
			v2: None,
			v3: None,
			v4: None,
			v5: None,
			v6: None,
			v7: None,
			v8: None,
			v9: None,
		}
	}
}

/// # Tokens
///
///  Euma Design Tokens for UI elements
#[derive(Debug, Clone, Default, Serialize)]
pub struct Tokens {
	#[serde(skip_serializing_if = "Option::is_none")]
	pub color: Option<TokenColor>,
	#[serde(skip_serializing_if = "Option::is_none")]
	pub typo: Option<TokenTypo>,
	#[serde(skip_serializing_if = "Option::is_none")]
	pub space: Option<TokenSpace>,
	#[serde(skip_serializing_if = "Option::is_none")]
	pub fx: Option<TokenFx>,
}
