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

package rtcp

import (
	
	
)

// A SenderReport (SR) packet provides reception quality feedback for an RTP stream
type SenderReport struct {
	// The synchronization source identifier for the originator of this SR packet.
	SSRC uint32
	// The wallclock time when this report was sent so that it may be used in
	// combination with timestamps returned in reception reports from other
	// receivers to measure round-trip propagation to those receivers.
	NTPTime uint64
	// Corresponds to the same time as the NTP timestamp (above), but in
	// the same units and with the same random offset as the RTP
	// timestamps in data packets. This correspondence may be used for
	// intra- and inter-media synchronization for sources whose NTP
	// timestamps are synchronized, and may be used by media-independent
	// receivers to estimate the nominal RTP clock frequency.
	RTPTime uint32
	// The total number of RTP data packets transmitted by the sender
	// since starting transmission up until the time this SR packet was
	// generated.
	PacketCount uint32
	// The total number of payload octets (i.e., not including header or
	// padding) transmitted in RTP data packets by the sender since
	// starting transmission up until the time this SR packet was
	// generated.
	OctetCount uint32
	// Zero or more reception report blocks depending on the number of other
	// sources heard by this sender since the last report. Each reception report
	// block conveys statistics on the reception of RTP packets from a
	// single synchronization source.
	Reports []ReceptionReport
	// ProfileExtensions contains additional, payload-specific information that needs to
	// be reported regularly about the sender.
	ProfileExtensions []byte
}

const (
	srHeaderLength      = 24
	srSSRCOffset        = 0
	srNTPOffset         = srSSRCOffset + ssrcLength
	ntpTimeLength       = 8
	srRTPOffset         = srNTPOffset + ntpTimeLength
	rtpTimeLength       = 4
	srPacketCountOffset = srRTPOffset + rtpTimeLength
	srPacketCountLength = 4
	srOctetCountOffset  = srPacketCountOffset + srPacketCountLength
	srOctetCountLength  = 4
	srReportOffset      = srOctetCountOffset + srOctetCountLength
)

// Marshal encodes the SenderReport in binary
func ( SenderReport) () ([]byte, error) {
	/*
	 *         0                   1                   2                   3
	 *         0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
	 *        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 * header |V=2|P|    RC   |   PT=SR=200   |             length            |
	 *        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 *        |                         SSRC of sender                        |
	 *        +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
	 * sender |              NTP timestamp, most significant word             |
	 * info   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 *        |             NTP timestamp, least significant word             |
	 *        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 *        |                         RTP timestamp                         |
	 *        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 *        |                     sender's packet count                     |
	 *        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 *        |                      sender's octet count                     |
	 *        +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
	 * report |                 SSRC_1 (SSRC of first source)                 |
	 * block  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 *   1    | fraction lost |       cumulative number of packets lost       |
	 *        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 *        |           extended highest sequence number received           |
	 *        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 *        |                      interarrival jitter                      |
	 *        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 *        |                         last SR (LSR)                         |
	 *        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 *        |                   delay since last SR (DLSR)                  |
	 *        +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
	 * report |                 SSRC_2 (SSRC of second source)                |
	 * block  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 *   2    :                               ...                             :
	 *        +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
	 *        |                  profile-specific extensions                  |
	 *        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 */

	 := make([]byte, .MarshalSize())
	 := [headerLength:]

	binary.BigEndian.PutUint32([srSSRCOffset:], .SSRC)
	binary.BigEndian.PutUint64([srNTPOffset:], .NTPTime)
	binary.BigEndian.PutUint32([srRTPOffset:], .RTPTime)
	binary.BigEndian.PutUint32([srPacketCountOffset:], .PacketCount)
	binary.BigEndian.PutUint32([srOctetCountOffset:], .OctetCount)

	 := srHeaderLength
	for ,  := range .Reports {
		,  := .Marshal()
		if  != nil {
			return nil, 
		}
		copy([:], )
		 += receptionReportLength
	}

	if len(.Reports) > countMax {
		return nil, errTooManyReports
	}

	copy([:], .ProfileExtensions)

	,  := .Header().Marshal()
	if  != nil {
		return nil, 
	}
	copy(, )

	return , nil
}

// Unmarshal decodes the SenderReport from binary
func ( *SenderReport) ( []byte) error {
	/*
	 *         0                   1                   2                   3
	 *         0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
	 *        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 * header |V=2|P|    RC   |   PT=SR=200   |             length            |
	 *        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 *        |                         SSRC of sender                        |
	 *        +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
	 * sender |              NTP timestamp, most significant word             |
	 * info   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 *        |             NTP timestamp, least significant word             |
	 *        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 *        |                         RTP timestamp                         |
	 *        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 *        |                     sender's packet count                     |
	 *        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 *        |                      sender's octet count                     |
	 *        +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
	 * report |                 SSRC_1 (SSRC of first source)                 |
	 * block  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 *   1    | fraction lost |       cumulative number of packets lost       |
	 *        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 *        |           extended highest sequence number received           |
	 *        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 *        |                      interarrival jitter                      |
	 *        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 *        |                         last SR (LSR)                         |
	 *        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 *        |                   delay since last SR (DLSR)                  |
	 *        +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
	 * report |                 SSRC_2 (SSRC of second source)                |
	 * block  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 *   2    :                               ...                             :
	 *        +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
	 *        |                  profile-specific extensions                  |
	 *        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 */

	if len() < (headerLength + srHeaderLength) {
		return errPacketTooShort
	}

	var  Header
	if  := .Unmarshal();  != nil {
		return 
	}

	if .Type != TypeSenderReport {
		return errWrongType
	}

	 := [headerLength:]

	.SSRC = binary.BigEndian.Uint32([srSSRCOffset:])
	.NTPTime = binary.BigEndian.Uint64([srNTPOffset:])
	.RTPTime = binary.BigEndian.Uint32([srRTPOffset:])
	.PacketCount = binary.BigEndian.Uint32([srPacketCountOffset:])
	.OctetCount = binary.BigEndian.Uint32([srOctetCountOffset:])

	 := srReportOffset
	for  := 0;  < int(.Count); ++ {
		 :=  + receptionReportLength
		if  > len() {
			return errPacketTooShort
		}
		 := [ : +receptionReportLength]
		 = 

		var  ReceptionReport
		if  := .Unmarshal();  != nil {
			return 
		}
		.Reports = append(.Reports, )
	}

	if  < len() {
		.ProfileExtensions = [:]
	}

	if uint8(len(.Reports)) != .Count {
		return errInvalidHeader
	}

	return nil
}

// DestinationSSRC returns an array of SSRC values that this packet refers to.
func ( *SenderReport) () []uint32 {
	 := make([]uint32, len(.Reports)+1)
	for ,  := range .Reports {
		[] = .SSRC
	}
	[len(.Reports)] = .SSRC
	return 
}

// MarshalSize returns the size of the packet once marshaled
func ( *SenderReport) () int {
	 := 0
	for ,  := range .Reports {
		 += .len()
	}
	return headerLength + srHeaderLength +  + len(.ProfileExtensions)
}

// Header returns the Header associated with this packet.
func ( *SenderReport) () Header {
	return Header{
		Count:  uint8(len(.Reports)),
		Type:   TypeSenderReport,
		Length: uint16((.MarshalSize() / 4) - 1),
	}
}

func ( SenderReport) () string {
	 := fmt.Sprintf("SenderReport from %x\n", .SSRC)
	 += fmt.Sprintf("\tNTPTime:\t%d\n", .NTPTime)
	 += fmt.Sprintf("\tRTPTIme:\t%d\n", .RTPTime)
	 += fmt.Sprintf("\tPacketCount:\t%d\n", .PacketCount)
	 += fmt.Sprintf("\tOctetCount:\t%d\n", .OctetCount)

	 += "\tSSRC    \tLost\tLastSequence\n"
	for ,  := range .Reports {
		 += fmt.Sprintf("\t%x\t%d/%d\t%d\n", .SSRC, .FractionLost, .TotalLost, .LastSequenceNumber)
	}
	 += fmt.Sprintf("\tProfile Extension Data: %v\n", .ProfileExtensions)
	return 
}