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

package rtcp

// Author: adwpc

import (
	
	
	
	
)

// https://tools.ietf.org/html/draft-holmer-rmcat-transport-wide-cc-extensions-01#page-5
// 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
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |V=2|P|  FMT=15 |    PT=205     |           length              |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |                     SSRC of packet sender                     |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |                      SSRC of media source                     |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |      base sequence number     |      packet status count      |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |                 reference time                | fb pkt. count |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |          packet chunk         |         packet chunk          |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// .                                                               .
// .                                                               .
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |         packet chunk          |  recv delta   |  recv delta   |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// .                                                               .
// .                                                               .
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |           recv delta          |  recv delta   | zero padding  |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

// for packet status chunk
const (
	// type of packet status chunk
	TypeTCCRunLengthChunk    = 0
	TypeTCCStatusVectorChunk = 1

	// len of packet status chunk
	packetStatusChunkLength = 2
)

// type of packet status symbol and recv delta
const (
	// https://tools.ietf.org/html/draft-holmer-rmcat-transport-wide-cc-extensions-01#section-3.1.1
	TypeTCCPacketNotReceived = uint16(iota)
	TypeTCCPacketReceivedSmallDelta
	TypeTCCPacketReceivedLargeDelta
	// https://tools.ietf.org/html/draft-holmer-rmcat-transport-wide-cc-extensions-01#page-7
	// see Example 2: "packet received, w/o recv delta"
	TypeTCCPacketReceivedWithoutDelta
)

// for status vector chunk
const (
	// https://tools.ietf.org/html/draft-holmer-rmcat-transport-wide-cc-extensions-01#section-3.1.4
	TypeTCCSymbolSizeOneBit = 0
	TypeTCCSymbolSizeTwoBit = 1

	// Notice: RFC is wrong: "packet received" (0) and "packet not received" (1)
	// if S == TypeTCCSymbolSizeOneBit, symbol list will be: TypeTCCPacketNotReceived TypeTCCPacketReceivedSmallDelta
	// if S == TypeTCCSymbolSizeTwoBit, symbol list will be same as above:
)

func numOfBitsOfSymbolSize() map[uint16]uint16 {
	return map[uint16]uint16{
		TypeTCCSymbolSizeOneBit: 1,
		TypeTCCSymbolSizeTwoBit: 2,
	}
}

var (
	errPacketStatusChunkLength = errors.New("packet status chunk must be 2 bytes")
	errDeltaExceedLimit        = errors.New("delta exceed limit")
)

// PacketStatusChunk has two kinds:
// RunLengthChunk and StatusVectorChunk
type PacketStatusChunk interface {
	Marshal() ([]byte, error)
	Unmarshal(rawPacket []byte) error
}

// RunLengthChunk T=TypeTCCRunLengthChunk
// 0                   1
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |T| S |       Run Length        |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
type RunLengthChunk struct {
	PacketStatusChunk

	// T = TypeTCCRunLengthChunk
	Type uint16

	// S: type of packet status
	// kind: TypeTCCPacketNotReceived or...
	PacketStatusSymbol uint16

	// RunLength: count of S
	RunLength uint16
}

// Marshal ..
func ( RunLengthChunk) () ([]byte, error) {
	 := make([]byte, 2)

	// append 1 bit '0'
	,  := setNBitsOfUint16(0, 1, 0, 0)
	if  != nil {
		return nil, 
	}

	// append 2 bit PacketStatusSymbol
	,  = setNBitsOfUint16(, 2, 1, .PacketStatusSymbol)
	if  != nil {
		return nil, 
	}

	// append 13 bit RunLength
	,  = setNBitsOfUint16(, 13, 3, .RunLength)
	if  != nil {
		return nil, 
	}

	binary.BigEndian.PutUint16(, )
	return , nil
}

// Unmarshal ..
func ( *RunLengthChunk) ( []byte) error {
	if len() != packetStatusChunkLength {
		return errPacketStatusChunkLength
	}

	// record type
	.Type = TypeTCCRunLengthChunk

	// get PacketStatusSymbol
	// r.PacketStatusSymbol = uint16(rawPacket[0] >> 5 & 0x03)
	.PacketStatusSymbol = getNBitsFromByte([0], 1, 2)

	// get RunLength
	// r.RunLength = uint16(rawPacket[0]&0x1F)*256 + uint16(rawPacket[1])
	.RunLength = getNBitsFromByte([0], 3, 5)<<8 + uint16([1])
	return nil
}

// StatusVectorChunk T=typeStatusVecotrChunk
// 0                   1
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |T|S|       symbol list         |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
type StatusVectorChunk struct {
	PacketStatusChunk
	// T = TypeTCCRunLengthChunk
	Type uint16

	// TypeTCCSymbolSizeOneBit or TypeTCCSymbolSizeTwoBit
	SymbolSize uint16

	// when SymbolSize = TypeTCCSymbolSizeOneBit, SymbolList is 14*1bit:
	// TypeTCCSymbolListPacketReceived or TypeTCCSymbolListPacketNotReceived
	// when SymbolSize = TypeTCCSymbolSizeTwoBit, SymbolList is 7*2bit:
	// TypeTCCPacketNotReceived TypeTCCPacketReceivedSmallDelta TypeTCCPacketReceivedLargeDelta or typePacketReserved
	SymbolList []uint16
}

// Marshal ..
func ( StatusVectorChunk) () ([]byte, error) {
	 := make([]byte, 2)

	// set first bit '1'
	,  := setNBitsOfUint16(0, 1, 0, 1)
	if  != nil {
		return nil, 
	}

	// set second bit SymbolSize
	,  = setNBitsOfUint16(, 1, 1, .SymbolSize)
	if  != nil {
		return nil, 
	}

	 := numOfBitsOfSymbolSize()[.SymbolSize]
	// append 14 bit SymbolList
	for ,  := range .SymbolList {
		 := *uint16() + 2
		,  = setNBitsOfUint16(, , , )
		if  != nil {
			return nil, 
		}
	}

	binary.BigEndian.PutUint16(, )
	// set SymbolList(bit8-15)
	// chunk[1] = uint8(r.SymbolList) & 0x0f
	return , nil
}

// Unmarshal ..
func ( *StatusVectorChunk) ( []byte) error {
	if len() != packetStatusChunkLength {
		return errPacketStatusChunkLength
	}

	.Type = TypeTCCStatusVectorChunk
	.SymbolSize = getNBitsFromByte([0], 1, 1)

	if .SymbolSize == TypeTCCSymbolSizeOneBit {
		for  := uint16(0);  < 6; ++ {
			.SymbolList = append(.SymbolList, getNBitsFromByte([0], 2+, 1))
		}
		for  := uint16(0);  < 8; ++ {
			.SymbolList = append(.SymbolList, getNBitsFromByte([1], , 1))
		}
		return nil
	}
	if .SymbolSize == TypeTCCSymbolSizeTwoBit {
		for  := uint16(0);  < 3; ++ {
			.SymbolList = append(.SymbolList, getNBitsFromByte([0], 2+*2, 2))
		}
		for  := uint16(0);  < 4; ++ {
			.SymbolList = append(.SymbolList, getNBitsFromByte([1], *2, 2))
		}
		return nil
	}

	.SymbolSize = getNBitsFromByte([0], 2, 6)<<8 + uint16([1])
	return nil
}

const (
	// TypeTCCDeltaScaleFactor https://tools.ietf.org/html/draft-holmer-rmcat-transport-wide-cc-extensions-01#section-3.1.5
	TypeTCCDeltaScaleFactor = 250
)

// RecvDelta are represented as multiples of 250us
// small delta is 1 byte: [0,63.75]ms = [0, 63750]us = [0, 255]*250us
// big delta is 2 bytes: [-8192.0, 8191.75]ms = [-8192000, 8191750]us = [-32768, 32767]*250us
// https://tools.ietf.org/html/draft-holmer-rmcat-transport-wide-cc-extensions-01#section-3.1.5
type RecvDelta struct {
	Type uint16
	// us
	Delta int64
}

// Marshal ..
func ( RecvDelta) () ([]byte, error) {
	 := .Delta / TypeTCCDeltaScaleFactor

	// small delta
	if .Type == TypeTCCPacketReceivedSmallDelta &&  >= 0 &&  <= math.MaxUint8 {
		 := make([]byte, 1)
		[0] = byte()
		return , nil
	}

	// big delta
	if .Type == TypeTCCPacketReceivedLargeDelta &&  >= math.MinInt16 &&  <= math.MaxInt16 {
		 := make([]byte, 2)
		binary.BigEndian.PutUint16(, uint16())
		return , nil
	}

	// overflow
	return nil, errDeltaExceedLimit
}

// Unmarshal ..
func ( *RecvDelta) ( []byte) error {
	 := len()

	// must be 1 or 2 bytes
	if  != 1 &&  != 2 {
		return errDeltaExceedLimit
	}

	if  == 1 {
		.Type = TypeTCCPacketReceivedSmallDelta
		.Delta = TypeTCCDeltaScaleFactor * int64([0])
		return nil
	}

	.Type = TypeTCCPacketReceivedLargeDelta
	.Delta = TypeTCCDeltaScaleFactor * int64(int16(binary.BigEndian.Uint16()))
	return nil
}

const (
	// the offset after header
	baseSequenceNumberOffset = 8
	packetStatusCountOffset  = 10
	referenceTimeOffset      = 12
	fbPktCountOffset         = 15
	packetChunkOffset        = 16
)

// TransportLayerCC for sender-BWE
// https://tools.ietf.org/html/draft-holmer-rmcat-transport-wide-cc-extensions-01#page-5
type TransportLayerCC struct {
	// header
	Header Header

	// SSRC of sender
	SenderSSRC uint32

	// SSRC of the media source
	MediaSSRC uint32

	// Transport wide sequence of rtp extension
	BaseSequenceNumber uint16

	// PacketStatusCount
	PacketStatusCount uint16

	// ReferenceTime
	ReferenceTime uint32

	// FbPktCount
	FbPktCount uint8

	// PacketChunks
	PacketChunks []PacketStatusChunk

	// RecvDeltas
	RecvDeltas []*RecvDelta
}

// Header returns the Header associated with this packet.
// func (t *TransportLayerCC) Header() Header {
// return t.Header
// return Header{
// Padding: true,
// Count:   FormatTCC,
// Type:    TypeTCCTransportSpecificFeedback,
// // https://tools.ietf.org/html/rfc4585#page-33
// Length: uint16((t.len() / 4) - 1),
// }
// }

func ( *TransportLayerCC) () uint16 {
	 := uint16(headerLength + packetChunkOffset + len(.PacketChunks)*2)
	for ,  := range .RecvDeltas {
		if .Type == TypeTCCPacketReceivedSmallDelta {
			++
		} else {
			 += 2
		}
	}
	return 
}

// Len return total bytes with padding
func ( *TransportLayerCC) () uint16 {
	return uint16(.MarshalSize())
}

// MarshalSize returns the size of the packet once marshaled
func ( *TransportLayerCC) () int {
	 := .packetLen()
	// has padding
	if %4 != 0 {
		 = (/4 + 1) * 4
	}

	return int()
}

func ( TransportLayerCC) () string {
	 := fmt.Sprintf("TransportLayerCC:\n\tHeader %v\n", .Header)
	 += fmt.Sprintf("TransportLayerCC:\n\tSender Ssrc %d\n", .SenderSSRC)
	 += fmt.Sprintf("\tMedia Ssrc %d\n", .MediaSSRC)
	 += fmt.Sprintf("\tBase Sequence Number %d\n", .BaseSequenceNumber)
	 += fmt.Sprintf("\tStatus Count %d\n", .PacketStatusCount)
	 += fmt.Sprintf("\tReference Time %d\n", .ReferenceTime)
	 += fmt.Sprintf("\tFeedback Packet Count %d\n", .FbPktCount)
	 += "\tPacketChunks "
	for ,  := range .PacketChunks {
		 += fmt.Sprintf("%+v ", )
	}
	 += "\n\tRecvDeltas "
	for ,  := range .RecvDeltas {
		 += fmt.Sprintf("%+v ", )
	}
	 += "\n"
	return 
}

// Marshal encodes the TransportLayerCC in binary
func ( TransportLayerCC) () ([]byte, error) {
	,  := .Header.Marshal()
	if  != nil {
		return nil, 
	}

	 := make([]byte, .MarshalSize()-headerLength)
	binary.BigEndian.PutUint32(, .SenderSSRC)
	binary.BigEndian.PutUint32([4:], .MediaSSRC)
	binary.BigEndian.PutUint16([baseSequenceNumberOffset:], .BaseSequenceNumber)
	binary.BigEndian.PutUint16([packetStatusCountOffset:], .PacketStatusCount)
	 := appendNBitsToUint32(0, 24, .ReferenceTime)
	 = appendNBitsToUint32(, 8, uint32(.FbPktCount))
	binary.BigEndian.PutUint32([referenceTimeOffset:], )

	for ,  := range .PacketChunks {
		,  := .Marshal()
		if  != nil {
			return nil, 
		}
		copy([packetChunkOffset+*2:], )
	}

	 := packetChunkOffset + len(.PacketChunks)*2
	var  int
	for ,  := range .RecvDeltas {
		,  := .Marshal()
		if  == nil {
			copy([+:], )
			++
			if .Type == TypeTCCPacketReceivedLargeDelta {
				++
			}
		}
	}

	if .Header.Padding {
		[len()-1] = uint8(.MarshalSize() - int(.packetLen()))
	}

	return append(, ...), nil
}

// Unmarshal ..
func ( *TransportLayerCC) ( []byte) error { //nolint:gocognit
	if len() < (headerLength + ssrcLength) {
		return errPacketTooShort
	}

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

	// https://tools.ietf.org/html/rfc4585#page-33
	// header's length + payload's length
	 := 4 * (.Header.Length + 1)

	if  < headerLength+packetChunkOffset {
		return errPacketTooShort
	}

	if len() < int() {
		return errPacketTooShort
	}

	if .Header.Type != TypeTransportSpecificFeedback || .Header.Count != FormatTCC {
		return errWrongType
	}

	.SenderSSRC = binary.BigEndian.Uint32([headerLength:])
	.MediaSSRC = binary.BigEndian.Uint32([headerLength+ssrcLength:])
	.BaseSequenceNumber = binary.BigEndian.Uint16([headerLength+baseSequenceNumberOffset:])
	.PacketStatusCount = binary.BigEndian.Uint16([headerLength+packetStatusCountOffset:])
	.ReferenceTime = get24BitsFromBytes([headerLength+referenceTimeOffset : headerLength+referenceTimeOffset+3])
	.FbPktCount = [headerLength+fbPktCountOffset]

	 := uint16(headerLength + packetChunkOffset)
	var  uint16
	for  < .PacketStatusCount {
		if +packetStatusChunkLength >=  {
			return errPacketTooShort
		}
		 := getNBitsFromByte([ : +1][0], 0, 1)
		var  PacketStatusChunk
		switch  {
		case TypeTCCRunLengthChunk:
			 := &RunLengthChunk{Type: }
			 = 
			 := .Unmarshal([ : +2])
			if  != nil {
				return 
			}

			 := localMin(.PacketStatusCount-, .RunLength)
			if .PacketStatusSymbol == TypeTCCPacketReceivedSmallDelta ||
				.PacketStatusSymbol == TypeTCCPacketReceivedLargeDelta {
				for  := uint16(0);  < ; ++ {
					.RecvDeltas = append(.RecvDeltas, &RecvDelta{Type: .PacketStatusSymbol})
				}
			}
			 += 
		case TypeTCCStatusVectorChunk:
			 := &StatusVectorChunk{Type: }
			 = 
			 := .Unmarshal([ : +2])
			if  != nil {
				return 
			}
			if .SymbolSize == TypeTCCSymbolSizeOneBit {
				for  := 0;  < len(.SymbolList); ++ {
					if .SymbolList[] == TypeTCCPacketReceivedSmallDelta {
						.RecvDeltas = append(.RecvDeltas, &RecvDelta{Type: TypeTCCPacketReceivedSmallDelta})
					}
				}
			}
			if .SymbolSize == TypeTCCSymbolSizeTwoBit {
				for  := 0;  < len(.SymbolList); ++ {
					if .SymbolList[] == TypeTCCPacketReceivedSmallDelta || .SymbolList[] == TypeTCCPacketReceivedLargeDelta {
						.RecvDeltas = append(.RecvDeltas, &RecvDelta{Type: .SymbolList[]})
					}
				}
			}
			 += uint16(len(.SymbolList))
		}
		 += packetStatusChunkLength
		.PacketChunks = append(.PacketChunks, )
	}

	 := 
	for ,  := range .RecvDeltas {
		if .Type == TypeTCCPacketReceivedSmallDelta {
			if +1 >  {
				return errPacketTooShort
			}
			 := .Unmarshal([ : +1])
			if  != nil {
				return 
			}
			++
		}
		if .Type == TypeTCCPacketReceivedLargeDelta {
			if +2 >  {
				return errPacketTooShort
			}
			 := .Unmarshal([ : +2])
			if  != nil {
				return 
			}
			 += 2
		}
	}

	return nil
}

// DestinationSSRC returns an array of SSRC values that this packet refers to.
func ( TransportLayerCC) () []uint32 {
	return []uint32{.MediaSSRC}
}

func localMin(,  uint16) uint16 {
	if  <  {
		return 
	}
	return 
}