///|
/// How a connection decides how much data it may have in flight.
///
/// The connection owns the mechanics — sequence numbers, timers, retransmission —
/// and asks this object only for a window and tells it about events. That split
/// is what makes Reno, CUBIC and Vegas interchangeable: they differ in how they
/// react to the same four events, not in how the connection works.
///
/// `flight_size` is how many bytes are sent but not yet acknowledged when the
/// event happens. Losss-based algorithms need it because the amount to cut is a
/// fraction of what is actually outstanding, not a fraction of the window the
/// algorithm previously believed it had.
pub(open) trait CongestionControl {
  /// Bytes this connection may have in flight right now.
  fn window(Self) -> Int
  /// The slow start threshold, for reports and tests.
  fn ssthresh(Self) -> Int
  /// A short name for reports.
  fn name(Self) -> String
  /// Which phase the algorithm is in, for reports.
  fn phase(Self) -> String
  /// New data was acknowledged. `rtt_ps` is zero when no usable round trip
  /// sample accompanied the acknowledgment, and `now_ps` is the virtual time at
  /// which it arrived — algorithms that grow as a function of time rather than
  /// of acknowledgments need it.
  fn on_ack(Self, acked_bytes : Int, mss : Int, rtt_ps : Int64, now_ps : Int64) -> Unit
  /// Duplicate acknowledgments revealed a hole.
  fn on_fast_retransmit(Self, flight_size : Int, mss : Int) -> Unit
  /// Another duplicate acknowledgment arrived while recovering. Acknowledging
  /// the same thing again is not progress, so this is the only signal that the
  /// packets after the hole are still arriving.
  fn on_duplicate_ack(Self, mss : Int) -> Unit
  /// The hole is repaired and everything that was in flight has been
  /// acknowledged.
  fn on_recovery_exit(Self, mss : Int) -> Unit
  /// An acknowledgment that repaired one hole and revealed another. The
  /// threshold has already been set for this window, so this must not cut it
  /// again.
  fn on_partial_ack(Self, mss : Int) -> Unit
  /// The retransmission timer expired: the most severe congestion signal,
  /// because it means not even the retransmission made it through.
  fn on_timeout(Self, flight_size : Int, mss : Int) -> Unit
}

///|
/// No congestion control at all: the window is effectively unbounded, so the
/// peer's advertised window is the only limit on the sending rate.
///
/// This is not a straw man. It is the control case that shows what congestion
/// control is buying: the same scenario run with and without it differs only in
/// how the sender behaves when a packet goes missing.
pub struct NoCongestionControl {
  limit : Int
}

///|
pub fn NoCongestionControl::new() -> NoCongestionControl {
  { limit: 1_000_000_000, }
}

///|
/// A control object that imposes no limit, ready to hand to
/// `TcpConnection::new_with_cc`.
pub fn without_congestion_control() -> &CongestionControl {
  NoCongestionControl::new() as &CongestionControl
}

///|
/// Reno, ready to hand to `TcpConnection::new_with_cc`.
pub fn reno(mss : Int) -> &CongestionControl {
  Reno::new(mss) as &CongestionControl
}

///|
/// CUBIC, ready to hand to `TcpConnection::new_with_cc`.
pub fn cubic(mss : Int) -> &CongestionControl {
  Cubic::new(mss) as &CongestionControl
}

///|
impl CongestionControl for NoCongestionControl with fn window(self) {
  self.limit
}

///|
impl CongestionControl for NoCongestionControl with fn ssthresh(_self) {
  0
}

///|
impl CongestionControl for NoCongestionControl with fn name(_self) {
  "none"
}

///|
impl CongestionControl for NoCongestionControl with fn phase(_self) {
  "unlimited"
}

///|
impl CongestionControl for NoCongestionControl with fn on_ack(
  _self,
  _acked_bytes,
  _mss,
  _rtt_ps,
  _now_ps,
) {
  ()
}

///|
impl CongestionControl for NoCongestionControl with fn on_fast_retransmit(
  _self,
  _flight_size,
  _mss,
) {
  ()
}

///|
impl CongestionControl for NoCongestionControl with fn on_duplicate_ack(
  _self,
  _mss,
) {
  ()
}

///|
impl CongestionControl for NoCongestionControl with fn on_recovery_exit(
  _self,
  _mss,
) {
  ()
}

///|
impl CongestionControl for NoCongestionControl with fn on_partial_ack(
  _self,
  _mss,
) {
  ()
}

///|
impl CongestionControl for NoCongestionControl with fn on_timeout(
  _self,
  _flight_size,
  _mss,
) {
  ()
}