///|
pub struct PesHeader {
  stream_id : Int
  packet_length : Int
  pts : Int64?
  dts : Int64?
  header_bytes : Int
  opaque_optional : Bytes
  offset : Int64
} derive(Eq, Debug)

///|
fn has_optional_header(stream_id : Int) -> Bool {
  !(stream_id == 0xbc ||
  stream_id == 0xbe ||
  stream_id == 0xbf ||
  stream_id == 0xf0 ||
  stream_id == 0xf1 ||
  stream_id == 0xff ||
  stream_id == 0xf2 ||
  stream_id == 0xf8)
}

///|
fn timestamp33(
  b : Bytes,
  pos : Int,
  prefix : Int,
  offset : Int64,
) -> Result[Int64, Diagnostic] {
  if b[pos].to_int() >> 4 != prefix ||
    (b[pos].to_int() & 1) != 1 ||
    (b[pos + 2].to_int() & 1) != 1 ||
    (b[pos + 4].to_int() & 1) != 1 {
    return Err(fault("pes_timestamp_marker", offset + pos.to_int64(), None))
  }
  Ok(
    ((b[pos].to_int64() & 14L) << 29) |
    (b[pos + 1].to_int64() << 22) |
    ((b[pos + 2].to_int64() & 254L) << 14) |
    (b[pos + 3].to_int64() << 7) |
    (b[pos + 4].to_int64() >> 1),
  )
}

///|
/// Parse the bounded PES header prefix; elementary payload is neither consumed nor decoded.
/// Non-timestamp optional fields are retained as opaque bytes, not semantically validated.
pub fn parse_pes_header(
  bytes : Bytes,
  offset? : Int64 = 0L,
) -> Result[PesHeader, Diagnostic] {
  if offset < 0L || offset > 9223372036854775543L {
    return Err(fault("offset_range", offset, None))
  }
  if bytes.length() < 6 {
    return Err(fault("pes_header_incomplete", offset, None))
  }
  if bytes[0] != 0 || bytes[1] != 0 || bytes[2] != 1 {
    return Err(fault("pes_prefix", offset, None))
  }
  let stream_id = bytes[3].to_int()
  let packet_length = (bytes[4].to_int() << 8) | bytes[5].to_int()
  if !has_optional_header(stream_id) {
    return Ok({
      stream_id,
      packet_length,
      pts: None,
      dts: None,
      header_bytes: 6,
      opaque_optional: b"",
      offset,
    })
  }
  if bytes.length() < 9 {
    return Err(fault("pes_header_incomplete", offset, None))
  }
  if (bytes[6].to_int() & 0xc0) != 0x80 {
    return Err(fault("pes_marker", offset + 6L, None))
  }
  let length = bytes[8].to_int()
  if packet_length != 0 && packet_length < 3 + length {
    return Err(fault("pes_packet_length", offset + 4L, None))
  }
  let flags = bytes[7].to_int() >> 6
  if flags == 1 || (flags == 2 && length < 5) || (flags == 3 && length < 10) {
    return Err(fault("pes_timestamp_length", offset + 7L, None))
  }
  if bytes.length() < 9 + length {
    return Err(fault("pes_header_incomplete", offset, None))
  }
  let mut pts = None
  let mut dts = None
  let mut pos = 9
  if flags >= 2 {
    match timestamp33(bytes, pos, flags, offset) {
      Ok(t) => pts = Some(t)
      Err(e) => return Err(e)
    }
    pos = pos + 5
  }
  if flags == 3 {
    match timestamp33(bytes, pos, 1, offset) {
      Ok(t) => dts = Some(t)
      Err(e) => return Err(e)
    }
    pos = pos + 5
  }
  Ok({
    stream_id,
    packet_length,
    pts,
    dts,
    header_bytes: 9 + length,
    opaque_optional: bytes.view(start=pos, end=9 + length).to_owned(),
    offset,
  })
}