///|
/// IP addresses appearing in the host subcomponent.

///|
/// An IPv4 address.
pub struct Ipv4Addr(Byte, Byte, Byte, Byte) derive(Eq, Compare, Hash)

///|
/// Creates an IPv4 address from its four octets.
pub fn Ipv4Addr::new(a : Byte, b : Byte, c : Byte, d : Byte) -> Ipv4Addr {
  Ipv4Addr(a, b, c, d)
}

///|
/// The address `127.0.0.1`.
pub let ipv4_localhost : Ipv4Addr = Ipv4Addr(127, 0, 0, 1)

///|
/// The address `0.0.0.0`.
pub let ipv4_unspecified : Ipv4Addr = Ipv4Addr(0, 0, 0, 0)

///|
/// Returns the four octets of the address.
pub fn Ipv4Addr::octets(self : Ipv4Addr) -> (Byte, Byte, Byte, Byte) {
  (self.0, self.1, self.2, self.3)
}

///|
pub impl Show for Ipv4Addr with fn output(self, logger) {
  logger.write_string(
    "\{self.0.to_int()}.\{self.1.to_int()}.\{self.2.to_int()}.\{self.3.to_int()}",
  )
}

///|
pub impl Debug for Ipv4Addr with fn to_repr(self) {
  Repr(self.to_string())
}

///|
/// An IPv6 address, stored as its sixteen octets.
pub struct Ipv6Addr(Bytes) derive(Eq, Compare, Hash)

///|
/// Creates an IPv6 address from its eight 16-bit segments.
pub fn Ipv6Addr::new(
  a : Int,
  b : Int,
  c : Int,
  d : Int,
  e : Int,
  f : Int,
  g : Int,
  h : Int,
) -> Ipv6Addr {
  let octets : Array[Byte] = []
  for seg in [a, b, c, d, e, f, g, h] {
    octets.push(((seg >> 8) & 0xff).to_byte())
    octets.push((seg & 0xff).to_byte())
  }
  Ipv6Addr(Bytes::from_array(octets))
}

///|
/// The address `::1`.
pub let ipv6_localhost : Ipv6Addr = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)

///|
/// The address `::`.
pub let ipv6_unspecified : Ipv6Addr = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)

///|
/// Returns the `i`th 16-bit segment of the address.
fn Ipv6Addr::seg(self : Ipv6Addr, i : Int) -> Int {
  (self.0[i * 2].to_int() << 8) | self.0[i * 2 + 1].to_int()
}

///|
/// Returns the eight 16-bit segments of the address.
pub fn Ipv6Addr::segments(self : Ipv6Addr) -> Array[Int] {
  let out = []
  for i in 0..<8 {
    out.push(self.seg(i))
  }
  out
}

///|
/// Returns the sixteen octets of the address.
pub fn Ipv6Addr::octets(self : Ipv6Addr) -> Bytes {
  self.0
}

///|
/// Formats the address in the canonical form of
/// [RFC 5952](https://datatracker.ietf.org/doc/html/rfc5952),
/// as `Ipv6Addr` in Rust does.
pub impl Show for Ipv6Addr with fn output(self, logger) {
  if self.seg(0) == 0 &&
    self.seg(1) == 0 &&
    self.seg(2) == 0 &&
    self.seg(3) == 0 &&
    self.seg(4) == 0 &&
    self.seg(5) == 0xffff {
    let a = self.0[12].to_int()
    let b = self.0[13].to_int()
    let c = self.0[14].to_int()
    let d = self.0[15].to_int()
    logger.write_string("::ffff:\{a}.\{b}.\{c}.\{d}")
    return
  }
  // Find the longest run of zero segments.
  let mut best_start = 0
  let mut best_len = 0
  let mut cur_start = 0
  let mut cur_len = 0
  for i in 0..<8 {
    if self.seg(i) == 0 {
      if cur_len == 0 {
        cur_start = i
      }
      cur_len += 1
      if cur_len > best_len {
        best_start = cur_start
        best_len = cur_len
      }
    } else {
      cur_len = 0
    }
  }

  ///|
  fn write_range(from : Int, to : Int) -> Unit {
    for i in from.. from {
        logger.write_char(':')
      }
      logger.write_string(to_hex_lower(self.seg(i)))
    }
  }

  if best_len > 1 {
    write_range(0, best_start)
    logger.write_string("::")
    write_range(best_start + best_len, 8)
  } else {
    write_range(0, 8)
  }
}

///|
pub impl Debug for Ipv6Addr with fn to_repr(self) {
  Repr(self.to_string())
}

///|
/// Formats `x` as lowercase hexadecimal without leading zeros.
fn to_hex_lower(x : Int) -> String {
  if x == 0 {
    return "0"
  }
  let digits = "0123456789abcdef"
  let sb = StringBuilder::new(size_hint=4)
  let mut started = false
  for shift in [12, 8, 4, 0] {
    let d = (x >> shift) & 0xf
    if d != 0 || started {
      started = true
      sb.write_char(digits.get_char(d).unwrap())
    }
  }
  sb.to_string()
}