///|
pub enum TokenKind {
  Color
  Number
  Text
  Boolean
} derive(Debug, Eq)

///|
pub enum ThemeValue {
  ColorValue(String)
  NumberValue(Int)
  TextValue(String)
  BooleanValue(Bool)
} derive(Debug, Eq)

///|
pub struct ThemeToken {
  path : TokenPath
  kind : TokenKind
  value : ThemeValue
  description : String
} derive(Debug, Eq)

///|
pub struct ThemeAlias {
  name : TokenPath
  target : TokenPath
} derive(Debug, Eq)

///|
pub struct ThemeDocument {
  tokens : Array[ThemeToken]
  aliases : Array[ThemeAlias]
  provenance : Array[ThemeProvenance]
} derive(Debug, Eq)

///|
pub fn ThemeToken::color(
  path : String,
  value : String,
  description : String,
) -> ThemeToken {
  {
    path: TokenPath::from_string(path),
    kind: Color,
    value: ColorValue(value),
    description,
  }
}

///|
pub fn ThemeToken::number(
  path : String,
  value : Int,
  description : String,
) -> ThemeToken {
  {
    path: TokenPath::from_string(path),
    kind: Number,
    value: NumberValue(value),
    description,
  }
}

///|
pub fn ThemeToken::text(
  path : String,
  value : String,
  description : String,
) -> ThemeToken {
  {
    path: TokenPath::from_string(path),
    kind: Text,
    value: TextValue(value),
    description,
  }
}

///|
pub fn ThemeToken::boolean(
  path : String,
  value : Bool,
  description : String,
) -> ThemeToken {
  {
    path: TokenPath::from_string(path),
    kind: Boolean,
    value: BooleanValue(value),
    description,
  }
}

///|
pub fn ThemeDocument::empty() -> ThemeDocument {
  { tokens: [], aliases: [], provenance: [] }
}

///|
pub fn ThemeDocument::add(
  self : ThemeDocument,
  token : ThemeToken,
) -> ThemeDocument {
  let tokens : Array[ThemeToken] = []
  for item in self.tokens {
    tokens.push(item)
  }
  tokens.push(token)
  { tokens, aliases: self.aliases, provenance: self.provenance }
}

///|
pub fn ThemeDocument::with_alias(
  self : ThemeDocument,
  name : String,
  target : String,
) -> ThemeDocument {
  let aliases : Array[ThemeAlias] = []
  for item in self.aliases {
    aliases.push(item)
  }
  aliases.push({
    name: TokenPath::from_string(name),
    target: TokenPath::from_string(target),
  })
  { tokens: self.tokens, aliases, provenance: self.provenance }
}

///|
pub fn ThemeDocument::find(self : ThemeDocument, path : String) -> ThemeToken? {
  let wanted = TokenPath::from_string(path).canonical()
  for token in self.tokens {
    if token.path.canonical() == wanted {
      return Some(token)
    }
  }
  None
}

///|
pub fn ThemeDocument::sorted(self : ThemeDocument) -> ThemeDocument {
  let sorted : Array[ThemeToken] = []
  for token in self.tokens {
    let mut position = sorted.length()
    for i, existing in sorted {
      if compare_stable_names(token.path.canonical(), existing.path.canonical()) <
        0 {
        position = i
        break
      }
    }
    sorted.push(token)
    let last = sorted.length() - 1
    let mut i = last
    while i > position {
      sorted[i] = sorted[i - 1]
      i -= 1
    }
    sorted[position] = token
  }
  { tokens: sorted, aliases: self.aliases, provenance: self.provenance }
}

///|
pub fn ThemeDocument::token_count(self : ThemeDocument) -> Int {
  self.tokens.length()
}

///|
pub fn BrandBook::theme_document(self : BrandBook) -> ThemeDocument {
  let document = ThemeDocument::empty()
  let mut current = document
  for color in self.palette.colors {
    current = current.add(
      ThemeToken::color("colors." + color.name, color.value, color.role),
    )
  }
  for family in self.typography.families {
    current = current.add(
      ThemeToken::text(
        "typography.font." + family.name,
        family.name,
        family.usage,
      ),
    )
  }
  for step in self.typography.scale {
    current = current.add(
      ThemeToken::number(
        "typography." + step.name + ".size",
        step.size_px,
        "font size in pixels",
      ),
    )
    current = current.add(
      ThemeToken::number(
        "typography." + step.name + ".line-height",
        step.line_height_px,
        "line height in pixels",
      ),
    )
  }
  current
}