///|
/// `cp` as at least 4 upper-case hex digits.
fn hex_code(cp : Int) -> String {
  let digits = cp.to_string(radix=16).to_upper()
  if digits.length() < 4 {
    "0".repeat(4 - digits.length()) + digits
  } else {
    digits
  }
}

///|
/// `c` inside a literal quoted with `quote`, escaped as elm-format does.
fn escaped(c : Char, quote : Char) -> String {
  let cp = c.to_int()
  match c {
    '\n' => "\\n"
    '\t' => "\\t"
    '\\' => "\\\\"
    _ if c == quote => "\\" + quote.to_string()
    _ if cp < 0x20 ||
      (cp >= 0x7F && cp <= 0x9F) ||
      cp == 0x2028 ||
      cp == 0x2029 ||
      (cp >= 0xD800 && cp <= 0xDFFF) => "\\u{" + hex_code(cp) + "}"
    _ => c.to_string()
  }
}

///|
fn string_literal(s : String) -> String {
  let out = StringBuilder()
  out.write_char('"')
  for c in s {
    out.write_string(escaped(c, '"'))
  }
  out.write_char('"')
  out.to_string()
}

///|
fn char_literal(c : Char) -> String {
  "'" + escaped(c, '\'') + "'"
}

///|
/// Whether `x` is below zero or is negative zero.
fn is_negative(x : Double) -> Bool {
  x < 0.0 || (x == 0.0 && 1.0 / x < 0.0)
}

///|
/// The shortest text that reads back as `x` (finite and not negative), with
/// a `.` in the mantissa: `1.0`, `0.1`, `1.0e21`, `5.0e-324`.
fn float_text(x : Double) -> String {
  let parts = x.to_string().split("e").map(p => p.to_owned()).collect()
  let mantissa = if parts[0].contains(".") { parts[0] } else { parts[0] + ".0" }
  if parts.length() == 1 {
    return mantissa
  }
  let exponent = StringBuilder()
  for c in parts[1] {
    if c != '+' {
      exponent.write_char(c)
    }
  }
  mantissa + "e" + exponent.to_string()
}

///|
/// `0x` and the upper-case digits of `n` (not negative), padded with zeros to
/// 2, 4, 8 or 16 digits.
fn hex_text(n : Int64) -> String {
  let digits = n.to_string(radix=16).to_upper()
  let len = digits.length()
  let width = if len <= 2 {
    2
  } else if len <= 4 {
    4
  } else if len <= 8 {
    8
  } else {
    16
  }
  "0x" + "0".repeat(width - len) + digits
}

///|
/// Whether `n` is the smallest `Int64` (the only value whose negation is
/// itself, apart from zero).
fn is_min_int(n : Int64) -> Bool {
  n < 0L && -n < 0L
}