///|
pub struct Ipv4NetworkSet {
  networks : Array[Ipv4Network]
} derive(Debug)

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

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

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

///|
pub fn Ipv4NetworkSet::insert(
  self : Ipv4NetworkSet,
  network : Ipv4Network,
) -> Ipv4NetworkSet {
  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_networks(networks)
    { networks, }
  }
}

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

///|
pub fn Ipv4NetworkSet::is_empty(self : Ipv4NetworkSet) -> Bool {
  self.len() == 0
}

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

///|
pub fn Ipv4NetworkSet::match_network(
  self : Ipv4NetworkSet,
  address : Ipv4Address,
) -> Ipv4Network? {
  let mut best : Ipv4Network? = 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 Ipv4NetworkSet::is_covered(
  self : Ipv4NetworkSet,
  network : Ipv4Network,
) -> Bool {
  for existing in self.networks {
    if existing.contains_network(network) {
      return true
    }
  }
  false
}

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

///|
pub fn Ipv4NetworkSet::to_string(self : Ipv4NetworkSet) -> 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_networks(networks : Array[Ipv4Network]) -> Unit {
  let mut i = 0
  while i < networks.length() {
    let mut j = i + 1
    while j < networks.length() {
      if should_swap(networks[i], networks[j]) {
        let tmp = networks[i]
        networks[i] = networks[j]
        networks[j] = tmp
      }
      j = j + 1
    }
    i = i + 1
  }
}

///|
fn should_swap(left : Ipv4Network, right : Ipv4Network) -> Bool {
  if left.address().to_uint() > right.address().to_uint() {
    true
  } else if left.address().to_uint() == right.address().to_uint() {
    left.prefix() > right.prefix()
  } else {
    false
  }
}