///|
/// The QUIC frame codec (RFC 9000 §19) and packet headers (§17). A range of
/// frame types encode to their bytes and parse back, reporting whether each is
/// ack-eliciting; a payload of several frames parses as a unit; and the long and
/// short packet headers round-trip.
///
///     moon run src/examples/19-quic-frames --target native
#coverage.skip
fn main {
  run() catch {
    e => println("quic frame error: \{e}")
  }
}

///|
fn run() -> Unit raise {
  let frames : Array[@frame.Frame] = [
    Ping,
    Crypto(offset=1UL, data=b"\xaa\xbb"),
    Stream(id=4UL, offset=0UL, fin=true, data=b"hello"),
    Ack(largest=10UL, delay=0UL, first_range=3UL, ranges=[]),
    MaxData(15293UL),
    ResetStream(id=4UL, error_code=0UL, final_size=100UL),
    ConnectionClose(error_code=7UL, frame_type=Some(8UL), reason=b"by"),
    HandshakeDone,
  ]
  for f in frames {
    let enc = @frame.encode(f)
    let ok = try {
      let (g, _) = @frame.decode(enc[:])
      g == f
    } catch {
      _ => false
    }
    println(
      "\{pad(hex(enc))} round-trip=\{ok} ack-eliciting=\{@frame.elicits_ack(f)}",
    )
  }
  let payload = @packet.payload([Ping, Crypto(offset=0UL, data=b"\x01\x02")])
  let parsed = @packet.read_payload(payload)
  println(
    "payload frames = \{parsed.length()} ack-eliciting=\{@packet.payload_elicits_ack(parsed)}",
  )
  let lh = @packet.Long::{
    packet_type: @packet.Kind::Initial,
    type_specific: 0,
    version: 1U,
    dcid: b"\x01\x02\x03",
    scid: b"",
  }
  let lh_wire = @packet.encode_long(lh)
  let lh_ok = match @packet.read_long(lh_wire[:]) {
    Some((h, _)) => h == lh
    None => false
  }
  println("long header  = \{hex(lh_wire)}  round-trip=\{lh_ok}")
  let sh = @packet.Short::{
    spin: false,
    key_phase: true,
    pn_length: 2,
    dcid: b"\xaa\xbb\xcc\xdd",
  }
  let sh_wire = @packet.encode_short(sh)
  let sh_ok = match @packet.read_short(sh_wire[:], 4) {
    Some((h, _)) => h == sh
    None => false
  }
  println("short header = \{hex(sh_wire)}  round-trip=\{sh_ok}")
}

///|
fn hex(b : Bytes) -> String {
  let digits = "0123456789abcdef"
  let sb = StringBuilder()
  for i = 0; i < b.length(); i = i + 1 {
    let x = b[i].to_int()
    sb.write_char(digits[x >> 4].unsafe_to_char())
    sb.write_char(digits[x & 0xf].unsafe_to_char())
  }
  sb.to_string()
}

///|
fn pad(s : String) -> String {
  let sb = StringBuilder()
  sb.write_string(s)
  for _i = s.length(); _i < 22; _i = _i + 1 {
    sb.write_char(' ')
  }
  sb.to_string()
}