// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT

package rtcp

// Packet represents an RTCP packet, a protocol used for out-of-band statistics and control information for an RTP session
type Packet interface {
	// DestinationSSRC returns an array of SSRC values that this packet refers to.
	DestinationSSRC() []uint32

	Marshal() ([]byte, error)
	Unmarshal(rawPacket []byte) error
	MarshalSize() int
}

// Unmarshal takes an entire udp datagram (which may consist of multiple RTCP packets) and
// returns the unmarshaled packets it contains.
//
// If this is a reduced-size RTCP packet a feedback packet (Goodbye, SliceLossIndication, etc)
// will be returned. Otherwise, the underlying type of the returned packet will be
// CompoundPacket.
func ( []byte) ([]Packet, error) {
	var  []Packet
	for len() != 0 {
		, ,  := unmarshal()
		if  != nil {
			return nil, 
		}

		 = append(, )
		 = [:]
	}

	switch len() {
	// Empty packet
	case 0:
		return nil, errInvalidHeader
	// Multiple Packets
	default:
		return , nil
	}
}

// Marshal takes an array of Packets and serializes them to a single buffer
func ( []Packet) ([]byte, error) {
	 := make([]byte, 0)
	for ,  := range  {
		,  := .Marshal()
		if  != nil {
			return nil, 
		}
		 = append(, ...)
	}
	return , nil
}

// unmarshal is a factory which pulls the first RTCP packet from a bytestream,
// and returns it's parsed representation, and the amount of data that was processed.
func unmarshal( []byte) ( Packet,  int,  error) {
	var  Header

	 = .Unmarshal()
	if  != nil {
		return nil, 0, 
	}

	 = int(.Length+1) * 4
	if  > len() {
		return nil, 0, errPacketTooShort
	}
	 := [:]

	switch .Type {
	case TypeSenderReport:
		 = new(SenderReport)

	case TypeReceiverReport:
		 = new(ReceiverReport)

	case TypeSourceDescription:
		 = new(SourceDescription)

	case TypeGoodbye:
		 = new(Goodbye)

	case TypeTransportSpecificFeedback:
		switch .Count {
		case FormatTLN:
			 = new(TransportLayerNack)
		case FormatRRR:
			 = new(RapidResynchronizationRequest)
		case FormatTCC:
			 = new(TransportLayerCC)
		case FormatCCFB:
			 = new(CCFeedbackReport)
		default:
			 = new(RawPacket)
		}

	case TypePayloadSpecificFeedback:
		switch .Count {
		case FormatPLI:
			 = new(PictureLossIndication)
		case FormatSLI:
			 = new(SliceLossIndication)
		case FormatREMB:
			 = new(ReceiverEstimatedMaximumBitrate)
		case FormatFIR:
			 = new(FullIntraRequest)
		default:
			 = new(RawPacket)
		}

	case TypeExtendedReport:
		 = new(ExtendedReport)

	case TypeApplicationDefined:
		 = new(ApplicationDefined)

	default:
		 = new(RawPacket)
	}

	 = .Unmarshal()

	return , , 
}