///|
pub(all) suberror RtcpError {
  InvalidPacket(String)
  InvalidCompoundPacket(String)
  PacketTooLarge(Int)
} derive(Debug, Eq)

///|
pub(all) enum Packet {
  SenderReport(Bytes)
  ReceiverReport(Bytes)
  SourceDescription(Bytes)
  Goodbye(Bytes)
  ApplicationDefined(Bytes)
  TransportFeedback(Bytes)
  PayloadFeedback(Bytes)
  ExtendedReport(Bytes)
  Unknown(Bytes)
} derive(Debug, Eq)

///|
pub(all) struct ReceptionReport {
  ssrc : UInt
  fraction_lost : Byte
  cumulative_lost : Int
  extended_sequence_number : UInt
  jitter : UInt
  last_sender_report : UInt
  delay_since_last_sender_report : UInt
} derive(Debug, Eq)

///|
pub fn ReceptionReport::new(
  ssrc~ : UInt,
  fraction_lost? : Byte = 0,
  cumulative_lost? : Int = 0,
  extended_sequence_number? : UInt = 0U,
  jitter? : UInt = 0U,
  last_sender_report? : UInt = 0U,
  delay_since_last_sender_report? : UInt = 0U,
) -> ReceptionReport raise RtcpError {
  if cumulative_lost < -8388608 || cumulative_lost > 8388607 {
    raise InvalidPacket("RTCP cumulative loss does not fit in signed 24 bits")
  }
  {
    ssrc,
    fraction_lost,
    cumulative_lost,
    extended_sequence_number,
    jitter,
    last_sender_report,
    delay_since_last_sender_report,
  }
}

///|
pub fn ReceptionReport::ssrc(self : ReceptionReport) -> UInt {
  self.ssrc
}

///|
pub fn ReceptionReport::fraction_lost(self : ReceptionReport) -> Byte {
  self.fraction_lost
}

///|
pub fn ReceptionReport::cumulative_lost(self : ReceptionReport) -> Int {
  self.cumulative_lost
}

///|
pub fn ReceptionReport::extended_sequence_number(
  self : ReceptionReport,
) -> UInt {
  self.extended_sequence_number
}

///|
pub fn ReceptionReport::jitter(self : ReceptionReport) -> UInt {
  self.jitter
}

///|
pub fn ReceptionReport::last_sender_report(self : ReceptionReport) -> UInt {
  self.last_sender_report
}

///|
pub fn ReceptionReport::delay_since_last_sender_report(
  self : ReceptionReport,
) -> UInt {
  self.delay_since_last_sender_report
}

///|
pub(all) struct SenderReportPacket {
  sender_ssrc : UInt
  ntp_timestamp : UInt64
  rtp_timestamp : UInt
  packet_count : UInt
  octet_count : UInt
  reports : Array[ReceptionReport]
  profile_extensions : Bytes
} derive(Debug, Eq)

///|
pub fn SenderReportPacket::new(
  sender_ssrc~ : UInt,
  ntp_timestamp~ : UInt64,
  rtp_timestamp~ : UInt,
  packet_count~ : UInt,
  octet_count~ : UInt,
  reports? : Array[ReceptionReport] = [],
  profile_extensions? : Bytes = b"",
) -> SenderReportPacket raise RtcpError {
  if reports.length() > 31 {
    raise InvalidPacket("RTCP sender report contains more than 31 reports")
  }
  if profile_extensions.length() % 4 != 0 {
    raise InvalidPacket("RTCP profile extension must be 32-bit aligned")
  }
  {
    sender_ssrc,
    ntp_timestamp,
    rtp_timestamp,
    packet_count,
    octet_count,
    reports: reports.copy(),
    profile_extensions,
  }
}

///|
pub(all) struct ReceiverReportPacket {
  sender_ssrc : UInt
  reports : Array[ReceptionReport]
  profile_extensions : Bytes
} derive(Debug, Eq)

///|
pub fn ReceiverReportPacket::new(
  sender_ssrc~ : UInt,
  reports? : Array[ReceptionReport] = [],
  profile_extensions? : Bytes = b"",
) -> ReceiverReportPacket raise RtcpError {
  if reports.length() > 31 {
    raise InvalidPacket("RTCP receiver report contains more than 31 reports")
  }
  if profile_extensions.length() % 4 != 0 {
    raise InvalidPacket("RTCP profile extension must be 32-bit aligned")
  }
  { sender_ssrc, reports: reports.copy(), profile_extensions, }
}

///|
pub(all) enum SourceDescriptionType {
  Cname
  Name
  Email
  Phone
  Location
  Tool
  Note
  Private
  Unknown(Byte)
} derive(Debug, Eq)

///|
pub(all) struct SourceDescriptionItem {
  item_type : SourceDescriptionType
  value : Bytes
} derive(Debug, Eq)

///|
pub fn SourceDescriptionItem::new(
  item_type~ : SourceDescriptionType,
  value~ : Bytes,
) -> SourceDescriptionItem raise RtcpError {
  if value.length() > 255 {
    raise InvalidPacket("RTCP SDES item exceeds 255 bytes")
  }
  { item_type, value, }
}

///|
pub(all) struct SourceDescriptionChunk {
  source : UInt
  items : Array[SourceDescriptionItem]
} derive(Debug, Eq)

///|
pub fn SourceDescriptionChunk::new(
  source~ : UInt,
  items? : Array[SourceDescriptionItem] = [],
) -> SourceDescriptionChunk {
  { source, items: items.copy(), }
}

///|
pub(all) struct SourceDescriptionPacket {
  chunks : Array[SourceDescriptionChunk]
} derive(Debug, Eq)

///|
pub fn SourceDescriptionPacket::new(
  chunks~ : Array[SourceDescriptionChunk],
) -> SourceDescriptionPacket raise RtcpError {
  if chunks.length() > 31 {
    raise InvalidPacket("RTCP SDES packet contains more than 31 chunks")
  }
  { chunks: chunks.copy(), }
}

///|
pub(all) struct GoodbyePacket {
  sources : Array[UInt]
  reason : Bytes?
} derive(Debug, Eq)

///|
pub fn GoodbyePacket::new(
  sources~ : Array[UInt],
  reason? : Bytes,
) -> GoodbyePacket raise RtcpError {
  if sources.is_empty() || sources.length() > 31 {
    raise InvalidPacket("RTCP BYE requires between one and 31 sources")
  }
  match reason {
    Some(value) if value.length() > 255 =>
      raise InvalidPacket("RTCP BYE reason exceeds 255 bytes")
    _ => ()
  }
  { sources: sources.copy(), reason, }
}

///|
pub(all) struct ApplicationDefinedPacket {
  subtype : Byte
  ssrc : UInt
  name : Bytes
  data : Bytes
} derive(Debug, Eq)

///|
pub fn ApplicationDefinedPacket::new(
  subtype? : Byte = 0,
  ssrc~ : UInt,
  name~ : Bytes,
  data? : Bytes = b"",
) -> ApplicationDefinedPacket raise RtcpError {
  if subtype > 31 {
    raise InvalidPacket("RTCP APP subtype exceeds five bits")
  }
  if name.length() != 4 {
    raise InvalidPacket("RTCP APP name must contain exactly four bytes")
  }
  if data.length() % 4 != 0 {
    raise InvalidPacket("RTCP APP data must be 32-bit aligned")
  }
  { subtype, ssrc, name, data, }
}

///|
pub(all) struct NackPair {
  packet_id : UInt16
  lost_packets : UInt16
} derive(Debug, Eq)

///|
pub fn NackPair::new(
  packet_id~ : UInt16,
  lost_packets? : UInt16 = 0,
) -> NackPair {
  { packet_id, lost_packets, }
}

///|
pub fn NackPair::lost_sequence_numbers(self : NackPair) -> Array[UInt16] {
  let result : Array[UInt16] = [self.packet_id]
  for bit = 0; bit < 16; bit = bit + 1 {
    if (self.lost_packets & (1U << bit).to_uint16()) != 0 {
      result.push(self.packet_id + (bit + 1).to_uint16())
    }
  }
  result
}

///|
pub(all) struct TransportLayerNackPacket {
  sender_ssrc : UInt
  media_ssrc : UInt
  nacks : Array[NackPair]
} derive(Debug, Eq)

///|
pub fn TransportLayerNackPacket::new(
  sender_ssrc~ : UInt,
  media_ssrc~ : UInt,
  nacks~ : Array[NackPair],
) -> TransportLayerNackPacket {
  { sender_ssrc, media_ssrc, nacks: nacks.copy(), }
}

///|
pub(all) enum TransportWideStatus {
  PacketNotReceived
  PacketReceivedSmallDelta(Int)
  PacketReceivedLargeDelta(Int)
  PacketReceivedWithoutDelta
} derive(Debug, Eq)

///|
pub(all) struct TransportWideCcPacket {
  sender_ssrc : UInt
  media_ssrc : UInt
  base_sequence_number : UInt16
  reference_time : UInt
  feedback_packet_count : Byte
  statuses : Array[TransportWideStatus]
} derive(Debug, Eq)

///|
pub fn TransportWideCcPacket::new(
  sender_ssrc~ : UInt,
  media_ssrc~ : UInt,
  base_sequence_number~ : UInt16,
  reference_time~ : UInt,
  feedback_packet_count? : Byte = 0,
  statuses~ : Array[TransportWideStatus],
) -> TransportWideCcPacket raise RtcpError {
  if reference_time > 0xffffffU {
    raise InvalidPacket("RTCP transport-cc reference time exceeds 24 bits")
  }
  if statuses.length() > 0xffff {
    raise InvalidPacket("RTCP transport-cc status count exceeds 65535")
  }
  for status in statuses {
    match status {
      PacketReceivedSmallDelta(delta) if delta < 0 ||
        delta > 63750 ||
        delta % 250 != 0 =>
        raise InvalidPacket("invalid transport-cc small receive delta")
      PacketReceivedLargeDelta(delta) if delta < -8192000 ||
        delta > 8191750 ||
        delta % 250 != 0 =>
        raise InvalidPacket("invalid transport-cc large receive delta")
      _ => ()
    }
  }
  {
    sender_ssrc,
    media_ssrc,
    base_sequence_number,
    reference_time,
    feedback_packet_count,
    statuses: statuses.copy(),
  }
}

///|
pub(all) struct PictureLossIndicationPacket {
  sender_ssrc : UInt
  media_ssrc : UInt
} derive(Debug, Eq)

///|
pub fn PictureLossIndicationPacket::new(
  sender_ssrc~ : UInt,
  media_ssrc~ : UInt,
) -> PictureLossIndicationPacket {
  { sender_ssrc, media_ssrc, }
}

///|
pub(all) struct FullIntraRequestEntry {
  ssrc : UInt
  sequence_number : Byte
} derive(Debug, Eq)

///|
pub(all) struct FullIntraRequestPacket {
  sender_ssrc : UInt
  media_ssrc : UInt
  entries : Array[FullIntraRequestEntry]
} derive(Debug, Eq)

///|
pub fn FullIntraRequestPacket::new(
  sender_ssrc~ : UInt,
  media_ssrc? : UInt = 0U,
  entries~ : Array[FullIntraRequestEntry],
) -> FullIntraRequestPacket {
  { sender_ssrc, media_ssrc, entries: entries.copy(), }
}

///|
pub(all) struct ReceiverEstimatedMaximumBitratePacket {
  sender_ssrc : UInt
  bitrate : UInt64
  ssrcs : Array[UInt]
} derive(Debug, Eq)

///|
pub fn ReceiverEstimatedMaximumBitratePacket::new(
  sender_ssrc~ : UInt,
  bitrate~ : UInt64,
  ssrcs~ : Array[UInt],
) -> ReceiverEstimatedMaximumBitratePacket raise RtcpError {
  if ssrcs.length() > 255 {
    raise InvalidPacket("RTCP REMB contains more than 255 SSRC values")
  }
  { sender_ssrc, bitrate, ssrcs: ssrcs.copy(), }
}

///|
pub(all) struct ExtendedReportBlock {
  block_type : Byte
  type_specific : Byte
  payload : Bytes
} derive(Debug, Eq)

///|
pub fn ExtendedReportBlock::new(
  block_type~ : Byte,
  type_specific? : Byte = 0,
  payload~ : Bytes,
) -> ExtendedReportBlock raise RtcpError {
  if payload.length() % 4 != 0 {
    raise InvalidPacket("RTCP XR block payload must be 32-bit aligned")
  }
  { block_type, type_specific, payload, }
}

///|
pub(all) struct ExtendedReportPacket {
  sender_ssrc : UInt
  blocks : Array[ExtendedReportBlock]
} derive(Debug, Eq)

///|
pub fn ExtendedReportPacket::new(
  sender_ssrc~ : UInt,
  blocks? : Array[ExtendedReportBlock] = [],
) -> ExtendedReportPacket {
  { sender_ssrc, blocks: blocks.copy(), }
}