///|
/// Python's `irc.IRC_COLORS`: (light background, dark background) color
/// names per token type.
pub fn irc_colors() -> Map[@token.TokenType, (String, String)] {
  Map([
    (@token.token, ("", "")),
    (@token.whitespace, ("gray", "brightblack")),
    (@token.comment, ("gray", "brightblack")),
    (@token.comment_preproc, ("cyan", "brightcyan")),
    (@token.keyword, ("blue", "brightblue")),
    (@token.keyword_type, ("cyan", "brightcyan")),
    (@token.operator_word, ("magenta", "brightcyan")),
    (@token.name_builtin, ("cyan", "brightcyan")),
    (@token.name_function, ("green", "brightgreen")),
    (@token.name_namespace, ("_cyan_", "_brightcyan_")),
    (@token.name_class, ("_green_", "_brightgreen_")),
    (@token.name_exception, ("cyan", "brightcyan")),
    (@token.name_decorator, ("brightblack", "gray")),
    (@token.name_variable, ("red", "brightred")),
    (@token.name_constant, ("red", "brightred")),
    (@token.name_attribute, ("cyan", "brightcyan")),
    (@token.name_tag, ("brightblue", "brightblue")),
    (@token.string, ("yellow", "yellow")),
    (@token.number, ("blue", "brightblue")),
    (@token.generic_deleted, ("brightred", "brightred")),
    (@token.generic_inserted, ("green", "brightgreen")),
    (@token.generic_heading, ("**", "**")),
    (@token.generic_subheading, ("*magenta*", "*brightmagenta*")),
    (@token.generic_error, ("brightred", "brightred")),
    (@token.error, ("_brightred_", "_brightred_")),
  ])
}

///|
/// Python's `irc.IRC_COLOR_MAP`: mIRC color numbers.
let irc_color_map : Map[String, Int] = {
  "white": 0,
  "black": 1,
  "blue": 2,
  "brightgreen": 3,
  "brightred": 4,
  "yellow": 5,
  "magenta": 6,
  "orange": 7,
  "green": 7,
  "brightyellow": 8,
  "lightgreen": 9,
  "brightcyan": 9,
  "cyan": 10,
  "lightblue": 11,
  "red": 11,
  "brightblue": 12,
  "brightmagenta": 13,
  "brightblack": 14,
  "gray": 15,
}

///|
/// Python's `str.strip(ch)` for one character.
fn strip_char(s : String, ch : String) -> String {
  s.trim(chars=ch).to_owned()
}

///|
/// Python's `irc.ircformat`: `text` wrapped in IRC color/attribute codes.
pub fn ircformat(color : String, text : String) -> String raise FormatterError {
  if color == "" {
    return text
  }
  let mut color = color
  let mut add = ""
  let mut sub = ""
  if color.contains("_") {
    add += "\u{1d}"
    sub = "\u{1d}" + sub
    color = strip_char(color, "_")
  }
  if color.contains("*") {
    add += "\u{02}"
    sub = "\u{02}" + sub
    color = strip_char(color, "*")
  }
  if color != "" {
    match irc_color_map.get(color) {
      Some(n) => {
        add += "\u{03}" + @pystr.rjust(n.to_string(), 2, fill='0')
        sub = "\u{03}" + sub
      }
      None => raise FormatterError("KeyError: \{@pystr.repr(color)}")
    }
  }
  add + text + sub
}

///|
/// Metadata of Python's `IRCFormatter`.
pub let irc_info : FormatterInfo = {
  class_name: "IRCFormatter",
  name: "IRC",
  aliases: ["irc", "IRC"],
  filenames: [],
  description: "Format tokens with IRC color sequences",
}

///|
/// Python's `IRCFormatter` (options `bg`, `linenos`; `colorscheme` as a
/// map). As in Python, the line counter continues across `format` calls.
pub fn irc_formatter(
  options? : @lexer.Options = Map([]),
  style? : @styles.Style,
  colorscheme? : Map[@token.TokenType, (String, String)],
) -> Formatter raise {
  let base = BaseOptions::new(options, style?)
  let darkbg = @lexer.get_choice_opt(options, "bg", ["light", "dark"], "light") ==
    "dark"
  let scheme = match colorscheme {
    Some(s) if s.length() > 0 => s
    _ => irc_colors()
  }
  let linenos = truthy_opt(options, "linenos")
  let lineno = Ref(0)
  let write_lineno = (buf : StringBuilder) => {
    if linenos {
      lineno.val += 1
      buf.write_string(@pystr.rjust(lineno.val.to_string(), 4, fill='0'))
      buf.write_string(": ")
    }
  }
  Formatter::new(irc_info, base, tokens => {
    let buf = StringBuilder()
    write_lineno(buf)
    for tok in tokens {
      let (ttype, value) = tok
      let colors = scheme_color(scheme, ttype)
      let color = if darkbg { colors.1 } else { colors.0 }
      let spl = @pystr.split(value, "\n")
      for i in 0..<(spl.length() - 1) {
        if spl[i] != "" {
          buf.write_string(ircformat(color, spl[i]))
        }
        buf.write_string("\n")
        write_lineno(buf)
      }
      let last = spl[spl.length() - 1]
      if last != "" {
        buf.write_string(ircformat(color, last))
      }
    }
    buf.to_string()
  })
}