///|
pub struct AddressBookEntry {
  mailbox : String
  display : String
  domain : String
  sent : Int
  received : Int
} derive(Eq, Debug)

///|
pub fn build_address_book(
  messages : Array[MailMessage],
) -> Array[AddressBookEntry] {
  let out : Array[AddressBookEntry] = []
  for message in messages {
    for addr in message.from {
      add_address_book_entry(out, addr, 1, 0)
    }
    for addr in message.to {
      add_address_book_entry(out, addr, 0, 1)
    }
    for addr in message.cc {
      add_address_book_entry(out, addr, 0, 1)
    }
  }
  out
}

///|
fn add_address_book_entry(
  entries : Array[AddressBookEntry],
  addr : Address,
  sent : Int,
  received : Int,
) -> Unit {
  let mailbox = lower_ascii(addr.mailbox)
  let mut i = 0
  while i < entries.length() {
    if entries[i].mailbox == mailbox {
      entries[i] = {
        mailbox: entries[i].mailbox,
        display: if entries[i].display == "" {
          addr.display
        } else {
          entries[i].display
        },
        domain: entries[i].domain,
        sent: entries[i].sent + sent,
        received: entries[i].received + received,
      }
      return
    }
    i = i + 1
  }
  entries.push({
    mailbox,
    display: addr.display,
    domain: mailbox_domain(mailbox),
    sent,
    received,
  })
}

///|
pub fn address_book_lookup(
  entries : Array[AddressBookEntry],
  mailbox : String,
) -> AddressBookEntry? {
  let target = lower_ascii(mailbox)
  for entry in entries {
    if entry.mailbox == target {
      return Some(entry)
    }
  }
  None
}

///|
pub fn frequent_senders(
  entries : Array[AddressBookEntry],
  min_sent : Int,
) -> Array[AddressBookEntry] {
  let out : Array[AddressBookEntry] = []
  for entry in entries {
    if entry.sent >= min_sent {
      out.push(entry)
    }
  }
  out
}

///|
pub fn domain_histogram(
  entries : Array[AddressBookEntry],
) -> Array[(String, Int)] {
  let out : Array[(String, Int)] = []
  for entry in entries {
    increment_domain(out, entry.domain)
  }
  out
}

///|
fn increment_domain(items : Array[(String, Int)], domain : String) -> Unit {
  if domain == "" {
    return
  }
  let mut i = 0
  while i < items.length() {
    if items[i].0 == domain {
      items[i] = (items[i].0, items[i].1 + 1)
      return
    }
    i = i + 1
  }
  items.push((domain, 1))
}

///|
pub fn address_book_summary(entries : Array[AddressBookEntry]) -> String {
  let mut out = "contacts=" + entries.length().to_string()
  for pair in domain_histogram(entries) {
    out = out + " " + pair.0 + "=" + pair.1.to_string()
  }
  out
}