///|
pub struct Ipv6NetworkSet {
  networks : Array[Ipv6Network]
} derive(Debug)

///|
pub fn Ipv6NetworkSet::new() -> Ipv6NetworkSet {
  { networks: Array::new() }
}

///|
pub fn Ipv6NetworkSet::from_array(
  networks : Array[Ipv6Network],
) -> Ipv6NetworkSet {
  let mut set = Ipv6NetworkSet::new()
  for network in networks {
    set = set.insert(network)
  }
  set
}

///|
pub fn Ipv6NetworkSet::parse_all(
  items : Array[String],
) -> Result[Ipv6NetworkSet, CidrError] {
  let mut set = Ipv6NetworkSet::new()
  for item in items {
    match Ipv6Network::parse(item) {
      Err(error) => return Err(error)
      Ok(network) => set = set.insert(network)
    }
  }
  Ok(set)
}

///|
pub fn Ipv6NetworkSet::insert(
  self : Ipv6NetworkSet,
  network : Ipv6Network,
) -> Ipv6NetworkSet {
  if self.is_covered(network) {
    self
  } else {
    let networks = Array::new()
    for existing in self.networks {
      if !network.contains_network(existing) {
        networks.push(existing)
      }
    }
    networks.push(network)
    sort_ipv6_networks(networks)
    { networks, }
  }
}

///|
pub fn Ipv6Network::contains_network(
  self : Ipv6Network,
  other : Ipv6Network,
) -> Bool {
  self.prefix() <= other.prefix() && self.contains(other.address())
}

///|
pub fn Ipv6NetworkSet::len(self : Ipv6NetworkSet) -> Int {
  self.networks.length()
}

///|
pub fn Ipv6NetworkSet::contains(
  self : Ipv6NetworkSet,
  address : Ipv6Address,
) -> Bool {
  self.match_network(address) is Some(_)
}

///|
pub fn Ipv6NetworkSet::match_network(
  self : Ipv6NetworkSet,
  address : Ipv6Address,
) -> Ipv6Network? {
  let mut best : Ipv6Network? = None
  for network in self.networks {
    if network.contains(address) {
      match best {
        None => best = Some(network)
        Some(current) =>
          if network.prefix() > current.prefix() {
            best = Some(network)
          }
      }
    }
  }
  best
}

///|
pub fn Ipv6NetworkSet::is_covered(
  self : Ipv6NetworkSet,
  network : Ipv6Network,
) -> Bool {
  for existing in self.networks {
    if existing.contains_network(network) {
      return true
    }
  }
  false
}

///|
pub fn Ipv6NetworkSet::to_array(self : Ipv6NetworkSet) -> Array[Ipv6Network] {
  self.networks.copy()
}

///|
pub fn Ipv6NetworkSet::to_string(self : Ipv6NetworkSet) -> String {
  let mut text = ""
  let mut index = 0
  for network in self.networks {
    if index > 0 {
      text = text + ", "
    }
    text = text + network.to_string()
    index = index + 1
  }
  text
}

///|
fn sort_ipv6_networks(networks : Array[Ipv6Network]) -> Unit {
  let mut i = 0
  while i < networks.length() {
    let mut j = i + 1
    while j < networks.length() {
      if ipv6_should_swap(networks[i], networks[j]) {
        let tmp = networks[i]
        networks[i] = networks[j]
        networks[j] = tmp
      }
      j = j + 1
    }
    i = i + 1
  }
}

///|
fn ipv6_should_swap(left : Ipv6Network, right : Ipv6Network) -> Bool {
  let la = left.address()
  let ra = right.address()
  if la > ra {
    true
  } else if la == ra {
    left.prefix() > right.prefix()
  } else {
    false
  }
}