///|
/// QUIC streams: id classification and generation (RFC 9000 §2.1, Table 1), the
/// send and receive stream state machines (§3.1, §3.2), and stream reassembly
/// (§2.2) reordering out-of-order fragments and tracking the final size from the
/// fin bit.
///
///     moon run src/examples/25-quic-streams --target native
#coverage.skip
fn main {
  run() catch {
    e => println("stream error: \{e}")
  }
}

///|
fn run() -> Unit raise {
  println(
    "id_of(Client,Bidi,1)=\{@stream.id_of(Client, Bidi, 1UL).to_string()} (expect 4)",
  )
  println(
    "initiator(3)=\{sinit(@stream.initiator(3UL))} direction(3)=\{sdir(@stream.direction(3UL))}",
  )
  println(
    "is_bidi(4)=\{@stream.is_bidi(4UL)} client_initiated(2)=\{@stream.is_client(2UL)} sequence(23)=\{@stream.sequence(23UL).to_string()}",
  )
  println(
    "server writable on client-uni(2)=\{@stream.is_writable(2UL, server=true)} locally_initiated(1,server)=\{@stream.is_local(1UL, server=true)}",
  )
  let s1 = @stream.Sending::next(Ready, Write(fin=false))
  let s2 = @stream.Sending::next(s1, Write(fin=true))
  let s3 = @stream.Sending::next(s2, AllAcked)
  println("send path: \{sendst(s1)} -> \{sendst(s2)} -> \{sendst(s3)}")
  let r1 = @stream.Receiving::next(Recv, Receive(fin=true))
  let r2 = @stream.Receiving::next(r1, AllReceived)
  let r3 = @stream.Receiving::next(r2, AppRead)
  println("recv path: \{recvst(r1)} -> \{recvst(r2)} -> \{recvst(r3)}")
  let re = @stream.Assembler::new()
  re.insert(3UL, b"def", fin=false)
  println("read before gap filled = \"\{ascii(re.read())}\"")
  re.insert(0UL, b"abc", fin=true)
  println(
    "read after gap filled = \"\{ascii(re.read())}\" final_size=\{opt_u(re.final_size())} complete=\{re.complete()}",
  )
}

///|
fn sinit(s : @stream.Initiator) -> String {
  match s {
    Client => "Client"
    Server => "Server"
  }
}

///|
fn sdir(d : @stream.Direction) -> String {
  match d {
    Bidi => "Bidi"
    Uni => "Uni"
  }
}

///|
fn sendst(s : @stream.Sending) -> String {
  match s {
    Ready => "Ready"
    Send => "Send"
    DataSent => "DataSent"
    DataRecvd => "DataRecvd"
    ResetSent => "ResetSent"
    ResetRecvd => "ResetRecvd"
  }
}

///|
fn recvst(s : @stream.Receiving) -> String {
  match s {
    Recv => "Recv"
    SizeKnown => "SizeKnown"
    DataReceived => "DataReceived"
    DataRead => "DataRead"
    ResetReceived => "ResetReceived"
    ResetRead => "ResetRead"
  }
}

///|
fn ascii(b : Bytes) -> String {
  let sb = StringBuilder()
  for i = 0; i < b.length(); i = i + 1 {
    sb.write_char(b[i].to_int().unsafe_to_char())
  }
  sb.to_string()
}

///|
fn opt_u(o : UInt64?) -> String {
  match o {
    Some(n) => n.to_string()
    None => "(none)"
  }
}