///|
pub struct ThreadInfo {
  message_id : String
  in_reply_to : String
  references : Array[String]
  root_id : String
} derive(Eq, Debug)

///|
pub fn thread_info(message : MailMessage) -> ThreadInfo {
  let refs = parse_message_id_list(header_value(message.headers, "References"))
  let in_reply_to = first_message_id(
    header_value(message.headers, "In-Reply-To"),
  )
  let root = if refs.length() > 0 {
    refs[0]
  } else if in_reply_to != "" {
    in_reply_to
  } else {
    message.message_id
  }
  {
    message_id: message.message_id,
    in_reply_to,
    references: refs,
    root_id: root,
  }
}

///|
pub fn parse_message_id_list(value : String) -> Array[String] {
  let out : Array[String] = []
  let chars : Array[Char] = []
  for ch in value {
    chars.push(ch)
  }
  let mut i = 0
  while i < chars.length() {
    if chars[i] == '<' {
      let end = find_message_id_end(chars, i + 1)
      if end > i {
        out.push("<" + chars_range_for_thread(chars, i + 1, end) + ">")
        i = end + 1
      } else {
        i = i + 1
      }
    } else {
      i = i + 1
    }
  }
  out
}

///|
fn first_message_id(value : String) -> String {
  let ids = parse_message_id_list(value)
  if ids.length() > 0 {
    ids[0]
  } else {
    trim_ascii(value)
  }
}

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

///|
fn chars_range_for_thread(
  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
}

///|
pub fn same_thread(a : MailMessage, b : MailMessage) -> Bool {
  let ta = thread_info(a)
  let tb = thread_info(b)
  ta.root_id != "" && ta.root_id == tb.root_id
}

///|
pub fn group_thread_roots(
  messages : Array[MailMessage],
) -> Array[(String, Int)] {
  let roots : Array[(String, Int)] = []
  for message in messages {
    let root = thread_info(message).root_id
    increment_root(roots, root)
  }
  roots
}

///|
fn increment_root(roots : Array[(String, Int)], root : String) -> Unit {
  let key = if root == "" { "(missing)" } else { root }
  let mut i = 0
  while i < roots.length() {
    if roots[i].0 == key {
      roots[i] = (roots[i].0, roots[i].1 + 1)
      return
    }
    i = i + 1
  }
  roots.push((key, 1))
}