// A QUIC packet payload (RFC 9000 §12.4) is a sequence of frames. This assembles a
// frame list into the payload that goes under AEAD protection, parses a decrypted
// payload back into its frames, pads a payload out to a minimum size with PADDING, and
// classifies whether a payload is ack-eliciting (RFC 9000 §13.2.1). It sits between the
// single-frame codec and the packet-protection layer; pure bytes in and out.

///|
/// A truncated or unparsable frame in a packet payload.
pub suberror QuicPayloadError {
  QuicPayloadError(String)
}

///|
/// Encode a list of frames into a packet payload — their wire encodings concatenated.
pub fn quic_encode_payload(frames : Array[QuicFrame]) -> Bytes {
  let buf = Buffer()
  for frame in frames {
    buf.write_bytes(encode_frame(frame))
  }
  buf.to_bytes()
}

///|
/// Parse a decrypted packet payload into its frames, consuming the whole payload. A
/// frame that does not parse, or that consumes no bytes, is a payload error.
pub fn quic_parse_payload(
  payload : Bytes,
) -> Array[QuicFrame] raise QuicPayloadError {
  let frames : Array[QuicFrame] = []
  let view = payload[:]
  let mut off = 0
  while off < view.length() {
    match parse_frame(view[off:]) {
      Some((frame, consumed)) => {
        if consumed <= 0 {
          raise QuicPayloadError("frame consumed no bytes")
        }
        frames.push(frame)
        off += consumed
      }
      None => raise QuicPayloadError("truncated or unknown frame in payload")
    }
  }
  frames
}

///|
/// Whether a single frame is ack-eliciting (RFC 9000 §13.2.1): every frame except
/// PADDING, ACK, and CONNECTION_CLOSE obliges the peer to acknowledge the packet.
pub fn quic_frame_is_ack_eliciting(frame : QuicFrame) -> Bool {
  match frame {
    Padding(_) | Ack(..) | ConnectionClose(..) => false
    _ => true
  }
}

///|
/// Whether a payload's frames make the packet ack-eliciting — true if any frame is.
pub fn quic_payload_is_ack_eliciting(frames : Array[QuicFrame]) -> Bool {
  for frame in frames {
    if quic_frame_is_ack_eliciting(frame) {
      return true
    }
  }
  false
}

///|
/// Pad `payload` out to at least `min_size` bytes with PADDING frames (zero octets); a
/// payload already that long is returned unchanged (RFC 9000 §14.1 — an Initial packet's
/// payload is padded so the datagram reaches the 1200-byte minimum).
pub fn quic_pad_payload(payload : Bytes, min_size : Int) -> Bytes {
  if payload.length() >= min_size {
    return payload
  }
  let buf = Buffer()
  buf.write_bytes(payload)
  for _i = payload.length(); _i < min_size; _i = _i + 1 {
    buf.write_byte(0)
  }
  buf.to_bytes()
}