///|
let additive_tokens : Array[(Int, String)] = [
  (1000, "M"),
  (500, "D"),
  (100, "C"),
  (50, "L"),
  (10, "X"),
  (5, "V"),
  (1, "I"),
]

///|
fn apply_roman_letter_case(text : String, letter_case : LetterCase) -> String {
  match letter_case {
    Uppercase => text
    Lowercase => text.to_lower()
  }
}

///|
fn format_additive_upper(value : Int) -> Result[String, FormatError] {
  if value < 1 || value > 3999 {
    return Err(FormatOutOfRange(value))
  }
  let mut remaining = value
  let mut output = ""
  for token in additive_tokens {
    while remaining >= token.0 {
      output = output + token.1
      remaining = remaining - token.0
    }
  }
  Ok(output)
}

///|
fn format_extended_upper(value : Int) -> Result[String, FormatError] {
  if value < 1 || value > 3999999 {
    return Err(FormatOutOfRange(value))
  }
  if value <= 3999 {
    return format(value)
  }
  let thousands = value / 1000
  let remainder = value % 1000
  let group = match format(thousands) {
    Ok(text) => text
    Err(_) => return Err(FormatOutOfRange(value))
  }
  let suffix = if remainder == 0 {
    ""
  } else {
    match format(remainder) {
      Ok(text) => text
      Err(_) => return Err(FormatOutOfRange(value))
    }
  }
  Ok("(" + group + ")" + suffix)
}

///|
/// Format one value under an explicit notation mode and letter-case policy.
pub fn format_with_config(
  value : Int,
  config : FormatConfig,
) -> Result[String, FormatError] {
  let upper = match config.mode {
    ModernCanonical => format(value)
    AdditiveHistorical => format_additive_upper(value)
    ClockFace => format_clock_hour(value)
    ParenthesizedThousands => format_extended_upper(value)
  }
  match upper {
    Ok(text) => Ok(apply_roman_letter_case(text, config.letter_case))
    Err(error) => Err(error)
  }
}