///|
pub fn decode_body(input : String, encoding : TransferEncoding) -> String {
  match encoding {
    QuotedPrintable => decode_quoted_printable(input)
    Base64 => decode_base64_text(input)
    _ => input
  }
}

///|
pub fn decode_quoted_printable(input : String) -> String {
  let chars : Array[Char] = []
  for ch in input {
    chars.push(ch)
  }
  let mut out = ""
  let mut i = 0
  while i < chars.length() {
    if chars[i] == '=' && i + 2 < chars.length() {
      if chars[i + 1] == '\r' && chars[i + 2] == '\n' {
        i = i + 3
      } else if chars[i + 1] == '\n' {
        i = i + 2
      } else {
        let hi = hex_value(chars[i + 1])
        let lo = hex_value(chars[i + 2])
        if hi >= 0 && lo >= 0 {
          out = out + Int::unsafe_to_char(hi * 16 + lo).to_string()
          i = i + 3
        } else {
          out = out + chars[i].to_string()
          i = i + 1
        }
      }
    } else {
      out = out + chars[i].to_string()
      i = i + 1
    }
  }
  out
}

///|
pub fn hex_value(ch : Char) -> Int {
  if ch >= '0' && ch <= '9' {
    ch.to_int() - '0'.to_int()
  } else if ch >= 'A' && ch <= 'F' {
    ch.to_int() - 'A'.to_int() + 10
  } else if ch >= 'a' && ch <= 'f' {
    ch.to_int() - 'a'.to_int() + 10
  } else {
    -1
  }
}

///|
pub fn decode_base64_text(input : String) -> String {
  let cleaned = clean_base64(input)
  let chars : Array[Char] = []
  for ch in cleaned {
    chars.push(ch)
  }
  let bytes : Array[Int] = []
  let mut i = 0
  while i < chars.length() {
    let a = b64_value(chars[i])
    let b = if i + 1 < chars.length() { b64_value(chars[i + 1]) } else { -1 }
    let c = if i + 2 < chars.length() { b64_value(chars[i + 2]) } else { -1 }
    let d = if i + 3 < chars.length() { b64_value(chars[i + 3]) } else { -1 }
    if a >= 0 && b >= 0 {
      bytes.push(a * 4 + b / 16)
      if c >= 0 {
        bytes.push(b % 16 * 16 + c / 4)
      }
      if d >= 0 {
        bytes.push(c % 4 * 64 + d)
      }
    }
    i = i + 4
  }
  let mut out = ""
  for byte in bytes {
    out = out + Int::unsafe_to_char(byte).to_string()
  }
  out
}

///|
fn clean_base64(input : String) -> String {
  let mut out = ""
  for ch in input {
    if ch == '=' || b64_value(ch) >= 0 {
      out = out + ch.to_string()
    }
  }
  out
}

///|
fn b64_value(ch : Char) -> Int {
  if ch >= 'A' && ch <= 'Z' {
    ch.to_int() - 'A'.to_int()
  } else if ch >= 'a' && ch <= 'z' {
    ch.to_int() - 'a'.to_int() + 26
  } else if ch >= '0' && ch <= '9' {
    ch.to_int() - '0'.to_int() + 52
  } else if ch == '+' {
    62
  } else if ch == '/' {
    63
  } else {
    -1
  }
}