///|
pub struct RecordTypeCount {
  type_value : String
  count_value : Int
} derive(Eq, Debug)

///|
pub struct ZoneStatistics {
  record_count_value : Int
  owner_count_value : Int
  type_count_values : Array[RecordTypeCount]
  minimum_ttl_value : Ttl?
  maximum_ttl_value : Ttl?
  average_ttl_seconds_value : UInt64?
  apex_record_count_value : Int
  delegation_owner_count_value : Int
  explicit_ttl_count_value : Int
} derive(Eq, Debug)

///|
fn statistics_contains_owner(
  owners : Array[DomainName],
  candidate : DomainName,
) -> Bool {
  for owner in owners {
    if owner == candidate {
      return true
    }
  }
  false
}

///|
fn increment_type_count(
  counts : Array[RecordTypeCount],
  record_type : String,
) -> Unit {
  for index, item in counts {
    if item.type_value == record_type {
      counts[index] = {
        type_value: item.type_value,
        count_value: item.count_value + 1,
      }
      return
    }
  }
  counts.push({ type_value: record_type, count_value: 1 })
}

///|
fn statistics_text_after(left : String, right : String) -> Bool {
  let limit = if left.length() < right.length() {
    left.length()
  } else {
    right.length()
  }
  for index = 0; index < limit; index = index + 1 {
    let left_code = left[index].to_int()
    let right_code = right[index].to_int()
    if left_code > right_code {
      return true
    }
    if left_code < right_code {
      return false
    }
  }
  left.length() > right.length()
}

///|
fn sort_type_counts(counts : Array[RecordTypeCount]) -> Unit {
  for index = 1; index < counts.length(); index = index + 1 {
    let current = counts[index]
    let mut position = index
    while position > 0 &&
          statistics_text_after(
            counts[position - 1].type_value,
            current.type_value,
          ) {
      counts[position] = counts[position - 1]
      position = position - 1
    }
    counts[position] = current
  }
}

///|
/// Calculate deterministic aggregate evidence for a parsed zone.
pub fn calculate_zone_statistics(zone : ZoneDocument) -> ZoneStatistics {
  let records = zone.records()
  let owners : Array[DomainName] = []
  let delegation_owners : Array[DomainName] = []
  let type_counts : Array[RecordTypeCount] = []
  let mut minimum_ttl : Ttl? = None
  let mut maximum_ttl : Ttl? = None
  let mut ttl_total = 0UL
  let mut apex_records = 0
  let mut explicit_ttls = 0
  let origin = zone.origin()
  for record in records {
    let owner = record.owner()
    if !statistics_contains_owner(owners, owner) {
      owners.push(owner)
    }
    increment_type_count(type_counts, record.record_type())
    let seconds = record.ttl().seconds()
    ttl_total = ttl_total + seconds
    match minimum_ttl {
      None => minimum_ttl = Some(record.ttl())
      Some(value) if seconds < value.seconds() =>
        minimum_ttl = Some(record.ttl())
      _ => ()
    }
    match maximum_ttl {
      None => maximum_ttl = Some(record.ttl())
      Some(value) if seconds > value.seconds() =>
        maximum_ttl = Some(record.ttl())
      _ => ()
    }
    if origin == Some(owner) {
      apex_records = apex_records + 1
    }
    if record.ttl_was_explicit() {
      explicit_ttls = explicit_ttls + 1
    }
    if record.record_type() == "NS" &&
      origin != Some(owner) &&
      !statistics_contains_owner(delegation_owners, owner) {
      delegation_owners.push(owner)
    }
  }
  sort_type_counts(type_counts)
  let average = if records.length() == 0 {
    None
  } else {
    Some(ttl_total / records.length().to_uint64())
  }
  {
    record_count_value: records.length(),
    owner_count_value: owners.length(),
    type_count_values: type_counts,
    minimum_ttl_value: minimum_ttl,
    maximum_ttl_value: maximum_ttl,
    average_ttl_seconds_value: average,
    apex_record_count_value: apex_records,
    delegation_owner_count_value: delegation_owners.length(),
    explicit_ttl_count_value: explicit_ttls,
  }
}

///|
pub fn RecordTypeCount::record_type(self : RecordTypeCount) -> String {
  self.type_value
}

///|
pub fn RecordTypeCount::count(self : RecordTypeCount) -> Int {
  self.count_value
}

///|
pub fn ZoneStatistics::record_count(self : ZoneStatistics) -> Int {
  self.record_count_value
}

///|
pub fn ZoneStatistics::owner_count(self : ZoneStatistics) -> Int {
  self.owner_count_value
}

///|
pub fn ZoneStatistics::type_counts(
  self : ZoneStatistics,
) -> Array[RecordTypeCount] {
  self.type_count_values.copy()
}

///|
pub fn ZoneStatistics::count_for_type(
  self : ZoneStatistics,
  record_type : String,
) -> Int {
  let expected = uppercase_ascii(record_type)
  for item in self.type_count_values {
    if item.record_type() == expected {
      return item.count()
    }
  }
  0
}

///|
pub fn ZoneStatistics::minimum_ttl(self : ZoneStatistics) -> Ttl? {
  self.minimum_ttl_value
}

///|
pub fn ZoneStatistics::maximum_ttl(self : ZoneStatistics) -> Ttl? {
  self.maximum_ttl_value
}

///|
pub fn ZoneStatistics::average_ttl_seconds(self : ZoneStatistics) -> UInt64? {
  self.average_ttl_seconds_value
}

///|
pub fn ZoneStatistics::apex_record_count(self : ZoneStatistics) -> Int {
  self.apex_record_count_value
}

///|
pub fn ZoneStatistics::delegation_owner_count(self : ZoneStatistics) -> Int {
  self.delegation_owner_count_value
}

///|
pub fn ZoneStatistics::explicit_ttl_count(self : ZoneStatistics) -> Int {
  self.explicit_ttl_count_value
}