///|
pub struct Ipv4Range {
  start : Ipv4Address
  end : Ipv4Address
} derive(Eq, Debug)

///|
pub fn Ipv4Range::new(
  start : Ipv4Address,
  end : Ipv4Address,
) -> Result[Ipv4Range, CidrError] {
  if start.to_uint() > end.to_uint() {
    Err(InvalidPrefix("range start is greater than range end"))
  } else {
    Ok({ start, end })
  }
}

///|
pub fn Ipv4Range::from_network(network : Ipv4Network) -> Ipv4Range {
  { start: network.first(), end: network.last() }
}

///|
pub fn Ipv4Range::parse(
  start : String,
  end : String,
) -> Result[Ipv4Range, CidrError] {
  match Ipv4Address::parse(start) {
    Err(error) => Err(error)
    Ok(left) =>
      match Ipv4Address::parse(end) {
        Err(error) => Err(error)
        Ok(right) => Ipv4Range::new(left, right)
      }
  }
}

///|
pub fn Ipv4Range::start(self : Ipv4Range) -> Ipv4Address {
  self.start
}

///|
pub fn Ipv4Range::end(self : Ipv4Range) -> Ipv4Address {
  self.end
}

///|
pub fn Ipv4Range::contains(self : Ipv4Range, address : Ipv4Address) -> Bool {
  self.start.to_uint() <= address.to_uint() &&
  address.to_uint() <= self.end.to_uint()
}

///|
pub fn Ipv4Range::contains_network(
  self : Ipv4Range,
  network : Ipv4Network,
) -> Bool {
  self.contains(network.first()) && self.contains(network.last())
}

///|
pub fn Ipv4Range::overlaps(self : Ipv4Range, other : Ipv4Range) -> Bool {
  self.start.to_uint() <= other.end.to_uint() &&
  other.start.to_uint() <= self.end.to_uint()
}

///|
pub fn Ipv4Range::is_adjacent_to(self : Ipv4Range, other : Ipv4Range) -> Bool {
  let self_before_other = match self.end.next() {
    Some(next) => next == other.start
    None => false
  }
  let other_before_self = match other.end.next() {
    Some(next) => next == self.start
    None => false
  }
  self_before_other || other_before_self
}

///|
pub fn Ipv4Range::merge(
  self : Ipv4Range,
  other : Ipv4Range,
) -> Result[Ipv4Range, CidrError] {
  if !(self.overlaps(other) || self.is_adjacent_to(other)) {
    Err(InvalidPrefix("ranges do not overlap or touch"))
  } else {
    let start = if self.start.to_uint() <= other.start.to_uint() {
      self.start
    } else {
      other.start
    }
    let end = if self.end.to_uint() >= other.end.to_uint() {
      self.end
    } else {
      other.end
    }
    Ok({ start, end })
  }
}

///|
pub fn Ipv4Range::to_string(self : Ipv4Range) -> String {
  "\{self.start.to_string()}-\{self.end.to_string()}"
}