///|
pub fn decode_header_value(value : String) -> String {
  let mut out = ""
  let chars : Array[Char] = []
  for ch in value {
    chars.push(ch)
  }
  let mut i = 0
  while i < chars.length() {
    if i + 1 < chars.length() && chars[i] == '=' && chars[i + 1] == '?' {
      let parsed = try_decode_word(chars, i)
      match parsed {
        Some((text, next)) => {
          out = out + text
          i = next
        }
        None => {
          out = out + chars[i].to_string()
          i = i + 1
        }
      }
    } else {
      out = out + chars[i].to_string()
      i = i + 1
    }
  }
  out
}

///|
fn try_decode_word(chars : Array[Char], start : Int) -> (String, Int)? {
  let first_q = find_char(chars, start + 2, '?')
  if first_q < 0 {
    return None
  }
  let second_q = find_char(chars, first_q + 1, '?')
  if second_q < 0 {
    return None
  }
  let end = find_encoded_end(chars, second_q + 1)
  if end < 0 {
    return None
  }
  let charset = lower_ascii(chars_to_string(chars, start + 2, first_q))
  let encoding = lower_ascii(chars_to_string(chars, first_q + 1, second_q))
  let payload = chars_to_string(chars, second_q + 1, end)
  let decoded = if encoding == "b" {
    decode_base64_text(payload)
  } else if encoding == "q" {
    decode_rfc2047_q(payload)
  } else {
    payload
  }
  let final_text = if charset == "utf-8" || charset == "us-ascii" {
    decoded
  } else {
    decoded
  }
  Some((final_text, end + 2))
}

///|
fn find_char(chars : Array[Char], start : Int, target : Char) -> Int {
  let mut i = start
  while i < chars.length() {
    if chars[i] == target {
      return i
    }
    i = i + 1
  }
  -1
}

///|
fn find_encoded_end(chars : Array[Char], start : Int) -> Int {
  let mut i = start
  while i + 1 < chars.length() {
    if chars[i] == '?' && chars[i + 1] == '=' {
      return i
    }
    i = i + 1
  }
  -1
}

///|
fn chars_to_string(chars : Array[Char], start : Int, end : Int) -> String {
  let mut out = ""
  let mut i = start
  while i < end {
    out = out + chars[i].to_string()
    i = i + 1
  }
  out
}

///|
fn decode_rfc2047_q(input : String) -> String {
  let normalized = replace_underscores(input)
  decode_quoted_printable(normalized)
}

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

///|
pub fn normalize_subject(value : String) -> String {
  trim_ascii(decode_header_value(value))
}

///|
pub fn normalize_display_name(value : String) -> String {
  remove_quotes(trim_ascii(decode_header_value(value)))
}