// QUIC long packet headers (RFC 9000 §17.2). Long headers carry the handshake
// packets (Initial, 0-RTT, Handshake, Retry): they are fully self-describing —
// version, and length-prefixed destination and source connection IDs — which is
// the version-independent invariant RFC 8999 pins down. The third QUIC brick,
// on top of the varint and packet-number primitives.

///|
/// The four long-header packet types (RFC 9000 §17.2), in the encoding of the
/// first byte's type bits: Initial 0, 0-RTT 1, Handshake 2, Retry 3.
pub(all) enum QuicLongPacketType {
  Initial
  ZeroRtt
  Handshake
  Retry
} derive(Eq, Debug)

///|
/// A long packet header's invariant fields: its type, the type-specific low four
/// bits of the first byte (the packet-number length for Initial/Handshake/0-RTT,
/// unused by Retry), the 32-bit version, and the destination and source connection
/// IDs.
pub(all) struct QuicLongHeader {
  packet_type : QuicLongPacketType
  type_specific : Int
  version : UInt
  dcid : Bytes
  scid : Bytes
} derive(Eq, Debug)

///|
/// The first byte's 2-bit type code for a packet type.
fn long_type_code(t : QuicLongPacketType) -> Int {
  match t {
    Initial => 0
    ZeroRtt => 1
    Handshake => 2
    Retry => 3
  }
}

///|
/// Encode a long header: first byte (`1` header-form, `1` fixed bit, 2-bit type,
/// 4 type-specific bits), the version big-endian, then each connection ID prefixed
/// by its one-byte length.
pub fn encode_long_header(h : QuicLongHeader) -> Bytes {
  let first = 0xc0 |
    (long_type_code(h.packet_type) << 4) |
    (h.type_specific & 0x0f)
  let buf = Buffer()
  buf.write_byte(first.to_byte())
  buf.write_byte((h.version >> 24).to_byte())
  buf.write_byte((h.version >> 16).to_byte())
  buf.write_byte((h.version >> 8).to_byte())
  buf.write_byte(h.version.to_byte())
  buf.write_byte(h.dcid.length().to_byte())
  buf.write_bytes(h.dcid[:])
  buf.write_byte(h.scid.length().to_byte())
  buf.write_bytes(h.scid[:])
  buf.to_bytes()
}

///|
/// Parse a long header at the start of `b`, returning it and the number of bytes it
/// occupied, or `None` if the first byte is not a long header or `b` is truncated
/// before the header ends.
pub fn parse_long_header(b : BytesView) -> (QuicLongHeader, Int)? {
  if b.length() < 5 {
    return None
  }
  let first = b[0].to_int()
  // Header form is the high bit; a long header has it set.
  if (first & 0x80) == 0 {
    return None
  }
  let packet_type = match (first >> 4) & 0x03 {
    0 => QuicLongPacketType::Initial
    1 => ZeroRtt
    2 => QuicLongPacketType::Handshake
    _ => Retry
  }
  let type_specific = first & 0x0f
  let version = (b[1].to_int() << 24).reinterpret_as_uint() |
    (b[2].to_int() << 16).reinterpret_as_uint() |
    (b[3].to_int() << 8).reinterpret_as_uint() |
    b[4].to_int().reinterpret_as_uint()
  let mut off = 5
  if b.length() < off + 1 {
    return None
  }
  let dcil = b[off].to_int()
  off = off + 1
  if b.length() < off + dcil {
    return None
  }
  let dcid = b[off:off + dcil].to_owned()
  off = off + dcil
  if b.length() < off + 1 {
    return None
  }
  let scil = b[off].to_int()
  off = off + 1
  if b.length() < off + scil {
    return None
  }
  let scid = b[off:off + scil].to_owned()
  off = off + scil
  Some(({ packet_type, type_specific, version, dcid, scid, }, off))
}