// A QUIC packet-number space (RFC 9000 §12.3): the Initial, Handshake, and Application
// spaces each number their packets independently, track which packet numbers arrived so
// they can be acknowledged, remember the largest number the peer has acknowledged, and
// note when an ack-eliciting packet is owed an ACK. This is the per-space bookkeeping
// the connection state machine composes; it holds no keys or sockets, only the numbering
// and acknowledgement state, so it is pure and testable on its own.

///|
/// One packet-number space's send/receive numbering and acknowledgement state.
pub struct PacketNumberSpace {
  mut next_pn : Int64
  received : AckRangeSet
  mut largest_acked : Int64?
  mut ack_eliciting_pending : Bool
}

///|
/// A fresh space: next packet number 0, nothing received or acknowledged.
pub fn PacketNumberSpace::new() -> PacketNumberSpace {
  {
    next_pn: 0,
    received: AckRangeSet::new(),
    largest_acked: None,
    ack_eliciting_pending: false,
  }
}

///|
/// Allocate the next packet number to send, advancing the counter.
pub fn PacketNumberSpace::next_packet_number(self : PacketNumberSpace) -> Int64 {
  let pn = self.next_pn
  self.next_pn = self.next_pn + 1L
  pn
}

///|
/// The largest packet number the peer has acknowledged, or `None`.
pub fn PacketNumberSpace::largest_acked(self : PacketNumberSpace) -> Int64? {
  self.largest_acked
}

///|
/// Record that packet number `pn` arrived. `ack_eliciting` marks a packet that must be
/// acknowledged (RFC 9000 §13.2.1); a pure-ACK packet is recorded but does not itself
/// oblige a new ACK.
pub fn PacketNumberSpace::on_packet_received(
  self : PacketNumberSpace,
  pn : Int64,
  ack_eliciting : Bool,
) -> Unit {
  self.received.add(pn.reinterpret_as_uint64())
  if ack_eliciting {
    self.ack_eliciting_pending = true
  }
}

///|
/// Record that the peer acknowledged up to `largest`, advancing the high-water mark
/// (an older ACK never lowers it).
pub fn PacketNumberSpace::on_ack_received(
  self : PacketNumberSpace,
  largest : Int64,
) -> Unit {
  match self.largest_acked {
    Some(current) => if largest > current { self.largest_acked = Some(largest) }
    None => self.largest_acked = Some(largest)
  }
}

///|
/// Whether an ACK is owed — an ack-eliciting packet has arrived since the last ACK was
/// built.
pub fn PacketNumberSpace::ack_pending(self : PacketNumberSpace) -> Bool {
  self.ack_eliciting_pending
}

///|
/// Build the ACK frame acknowledging everything received so far with the given
/// `ack_delay`, and clear the pending flag. `None` when nothing has been received.
pub fn PacketNumberSpace::build_ack(
  self : PacketNumberSpace,
  ack_delay : UInt64,
) -> QuicFrame? {
  match self.received.to_ack_fields() {
    Some((largest, first_range, pairs)) => {
      self.ack_eliciting_pending = false
      Some(Ack(largest~, delay=ack_delay, first_range~, ranges=pairs))
    }
    None => None
  }
}