///|
/// Colour theme for terminal output.
///
/// `NoColor` emits no SGR escape sequences at all and is meant for output that
/// is piped into a file or another program.
pub(all) enum Theme {
  Dark
  Light
  NoColor
}

///|
/// An SGR style, stored as the parameter list of `ESC [  m`.
///
/// Styles compose by concatenating parameter lists, so `bold` then `cyan`
/// yields `ESC [ 1;36 m`.
priv struct Style {
  params : String
}

///|
fn sgr(params : String) -> Style {
  { params, }
}

///|
/// The style that adds nothing, used as the base for body text.
fn plain_style() -> Style {
  { params: "", }
}

///|
fn Style::then(self : Style, other : Style) -> Style {
  if self.params == "" {
    other
  } else if other.params == "" {
    self
  } else {
    { params: "\{self.params};\{other.params}", }
  }
}

///|
/// Wrap `text` in `style`, leaving it untouched when the style is empty.
fn paint(text : String, style : Style) -> String {
  if style.params == "" || text == "" {
    text
  } else {
    "\u001b[\{style.params}m\{text}\u001b[0m"
  }
}

///|
/// The styles used for one rendered document.
priv struct Palette {
  heading : Array[Style]
  heading_marker : Style
  code_span : Style
  code_text : Style
  code_bar : Style
  link : Style
  image : Style
  strong : Style
  emphasis : Style
  strike : Style
  quote_bar : Style
  list_marker : Style
  rule : Style
  border : Style
  dim : Style
}

///|
fn no_color_palette() -> Palette {
  {
    heading: Array::make(6, sgr("")),
    heading_marker: sgr(""),
    code_span: sgr(""),
    code_text: sgr(""),
    code_bar: sgr(""),
    link: sgr(""),
    image: sgr(""),
    strong: sgr(""),
    emphasis: sgr(""),
    strike: sgr(""),
    quote_bar: sgr(""),
    list_marker: sgr(""),
    rule: sgr(""),
    border: sgr(""),
    dim: sgr(""),
  }
}

///|
fn palette_of(theme : Theme) -> Palette {
  match theme {
    NoColor => no_color_palette()
    Dark =>
      {
        heading: [
          sgr("1;38;5;81"),
          sgr("1;38;5;75"),
          sgr("1;38;5;74"),
          sgr("1;38;5;110"),
          sgr("1;38;5;110"),
          sgr("1;38;5;110"),
        ],
        heading_marker: sgr("38;5;240"),
        code_span: sgr("38;5;180"),
        code_text: sgr("38;5;252"),
        code_bar: sgr("38;5;240"),
        link: sgr("4;38;5;45"),
        image: sgr("38;5;108"),
        strong: sgr("1"),
        emphasis: sgr("3"),
        strike: sgr("9;38;5;245"),
        quote_bar: sgr("38;5;240"),
        list_marker: sgr("38;5;214"),
        rule: sgr("38;5;240"),
        border: sgr("38;5;240"),
        dim: sgr("38;5;245"),
      }
    Light =>
      {
        heading: [
          sgr("1;38;5;25"),
          sgr("1;38;5;26"),
          sgr("1;38;5;31"),
          sgr("1;38;5;60"),
          sgr("1;38;5;60"),
          sgr("1;38;5;60"),
        ],
        heading_marker: sgr("38;5;250"),
        code_span: sgr("38;5;130"),
        code_text: sgr("38;5;238"),
        code_bar: sgr("38;5;250"),
        link: sgr("4;38;5;25"),
        image: sgr("38;5;100"),
        strong: sgr("1"),
        emphasis: sgr("3"),
        strike: sgr("9;38;5;245"),
        quote_bar: sgr("38;5;250"),
        list_marker: sgr("38;5;130"),
        rule: sgr("38;5;250"),
        border: sgr("38;5;250"),
        dim: sgr("38;5;243"),
      }
  }
}