///|
/// A half-open address range in the Modbus 16-bit address space.
pub(all) struct AddressRange {
  start : UInt16
  length : Int
}

///|
pub fn AddressRange::new(
  start : UInt16,
  length : Int,
) -> Result[AddressRange, ModbusError] {
  if length < 1 || !valid_address_range(start, length) {
    Err(InvalidAddress)
  } else {
    Ok({ start, length })
  }
}

///|
pub fn AddressRange::start(self : AddressRange) -> UInt16 {
  self.start
}

///|
pub fn AddressRange::length(self : AddressRange) -> Int {
  self.length
}

///|
pub fn AddressRange::end_exclusive(self : AddressRange) -> Int {
  self.start.to_int() + self.length
}

///|
pub fn AddressRange::contains(self : AddressRange, address : UInt16) -> Bool {
  address.to_int() >= self.start.to_int() &&
  address.to_int() < self.end_exclusive()
}

///|
pub fn AddressRange::contains_range(
  self : AddressRange,
  other : AddressRange,
) -> Bool {
  other.start.to_int() >= self.start.to_int() &&
  other.end_exclusive() <= self.end_exclusive()
}

///|
pub fn AddressRange::overlaps(
  self : AddressRange,
  other : AddressRange,
) -> Bool {
  self.start.to_int() < other.end_exclusive() &&
  other.start.to_int() < self.end_exclusive()
}

///|
pub fn AddressRange::offset(
  self : AddressRange,
  address : UInt16,
) -> Result[Int, ModbusError] {
  if self.contains(address) {
    Ok(address.to_int() - self.start.to_int())
  } else {
    Err(InvalidAddress)
  }
}

///|
pub fn AddressRange::slice(
  self : AddressRange,
  offset : Int,
  length : Int,
) -> Result[AddressRange, ModbusError] {
  if offset < 0 || length < 1 || offset + length > self.length {
    Err(InvalidAddress)
  } else {
    AddressRange::new((self.start.to_int() + offset).to_uint16(), length)
  }
}

///|
/// Split a range into non-overlapping segments of at most `segment_length`.
pub fn AddressRange::split(
  self : AddressRange,
  segment_length : Int,
) -> Result[Array[AddressRange], ModbusError] {
  if segment_length < 1 {
    return Err(InvalidQuantity)
  }
  let out : Array[AddressRange] = []
  let mut offset = 0
  while offset < self.length {
    let size = if offset + segment_length < self.length {
      segment_length
    } else {
      self.length - offset
    }
    match self.slice(offset, size) {
      Ok(value) => out.push(value)
      Err(error) => return Err(error)
    }
    offset += size
  }
  Ok(out)
}

///|
/// A first-fit allocator for contiguous register or coil ranges.
pub struct AddressAllocator {
  capacity : Int
  used : Array[AddressRange]
}

///|
pub fn AddressAllocator::new(
  capacity : Int,
) -> Result[AddressAllocator, ModbusError] {
  if capacity < 1 || capacity > 65536 {
    Err(CapacityExceeded)
  } else {
    Ok({ capacity, used: [] })
  }
}

///|
pub fn AddressAllocator::allocate(
  self : AddressAllocator,
  length : Int,
) -> Result[AddressRange, ModbusError] {
  if length < 1 || length > self.capacity {
    return Err(CapacityExceeded)
  }
  let mut candidate = 0
  while candidate + length <= self.capacity {
    let range = AddressRange::new(candidate.to_uint16(), length).unwrap()
    let mut conflict = false
    for current in self.used {
      if current.overlaps(range) {
        conflict = true
      }
    }
    if !conflict {
      self.used.push(range)
      return Ok(range)
    }
    candidate += 1
  }
  Err(CapacityExceeded)
}

///|
pub fn AddressAllocator::release(
  self : AddressAllocator,
  range : AddressRange,
) -> Result[Unit, ModbusError] {
  for index in 0.. Int {
  self.used.length()
}

///|
pub fn AddressAllocator::used_capacity(self : AddressAllocator) -> Int {
  let mut total = 0
  for range in self.used {
    total += range.length
  }
  total
}

///|
pub fn AddressAllocator::free_capacity(self : AddressAllocator) -> Int {
  self.capacity - self.used_capacity()
}

///|
pub fn AddressAllocator::snapshot(
  self : AddressAllocator,
) -> Array[AddressRange] {
  let out : Array[AddressRange] = []
  for range in self.used {
    out.push(range)
  }
  out
}