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

package rtcp

import (
	
	
	
)

// PacketBitmap shouldn't be used like a normal integral,
// so it's type is masked here. Access it with PacketList().
type PacketBitmap uint16

// NackPair is a wire-representation of a collection of
// Lost RTP packets
type NackPair struct {
	// ID of lost packets
	PacketID uint16

	// Bitmask of following lost packets
	LostPackets PacketBitmap
}

// The TransportLayerNack packet informs the encoder about the loss of a transport packet
// IETF RFC 4585, Section 6.2.1
// https://tools.ietf.org/html/rfc4585#section-6.2.1
type TransportLayerNack struct {
	// SSRC of sender
	SenderSSRC uint32

	// SSRC of the media source
	MediaSSRC uint32

	Nacks []NackPair
}

// NackPairsFromSequenceNumbers generates a slice of NackPair from a list of SequenceNumbers
// This handles generating the proper values for PacketID/LostPackets
func ( []uint16) ( []NackPair) {
	if len() == 0 {
		return []NackPair{}
	}

	 := &NackPair{PacketID: [0]}
	for  := 1;  < len(); ++ {
		 := []

		if -.PacketID > 16 {
			 = append(, *)
			 = &NackPair{PacketID: }
			continue
		}

		.LostPackets |= 1 << ( - .PacketID - 1)
	}
	 = append(, *)
	return
}

// Range calls f sequentially for each sequence number covered by n.
// If f returns false, Range stops the iteration.
func ( *NackPair) ( func( uint16) bool) {
	 := (.PacketID)
	if ! {
		return
	}

	 := .LostPackets
	for  := uint16(0);  != 0; ++ {
		if ( & (1 << )) != 0 {
			 &^= (1 << )
			 = (.PacketID +  + 1)
			if ! {
				return
			}
		}
	}
}

// PacketList returns a list of Nack'd packets that's referenced by a NackPair
func ( *NackPair) () []uint16 {
	 := make([]uint16, 0, 17)
	.Range(func( uint16) bool {
		 = append(, )
		return true
	})
	return 
}

const (
	tlnLength  = 2
	nackOffset = 8
)

// Marshal encodes the TransportLayerNack in binary
func ( TransportLayerNack) () ([]byte, error) {
	if len(.Nacks)+tlnLength > math.MaxUint8 {
		return nil, errTooManyReports
	}

	 := make([]byte, nackOffset+(len(.Nacks)*4))
	binary.BigEndian.PutUint32(, .SenderSSRC)
	binary.BigEndian.PutUint32([4:], .MediaSSRC)
	for  := 0;  < len(.Nacks); ++ {
		binary.BigEndian.PutUint16([nackOffset+(4*):], .Nacks[].PacketID)
		binary.BigEndian.PutUint16([nackOffset+(4*)+2:], uint16(.Nacks[].LostPackets))
	}
	 := .Header()
	,  := .Marshal()
	if  != nil {
		return nil, 
	}

	return append(, ...), nil
}

// Unmarshal decodes the TransportLayerNack from binary
func ( *TransportLayerNack) ( []byte) error {
	if len() < (headerLength + ssrcLength) {
		return errPacketTooShort
	}

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

	if len() < (headerLength + int(4*.Length)) {
		return errPacketTooShort
	}

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

	// The FCI field MUST contain at least one and MAY contain more than one Generic NACK
	if 4*.Length <= nackOffset {
		return errBadLength
	}

	.SenderSSRC = binary.BigEndian.Uint32([headerLength:])
	.MediaSSRC = binary.BigEndian.Uint32([headerLength+ssrcLength:])
	for  := headerLength + nackOffset;  < (headerLength + int(.Length*4));  += 4 {
		.Nacks = append(.Nacks, NackPair{
			binary.BigEndian.Uint16([:]),
			PacketBitmap(binary.BigEndian.Uint16([+2:])),
		})
	}
	return nil
}

// MarshalSize returns the size of the packet once marshaled
func ( *TransportLayerNack) () int {
	return headerLength + nackOffset + (len(.Nacks) * 4)
}

// Header returns the Header associated with this packet.
func ( *TransportLayerNack) () Header {
	return Header{
		Count:  FormatTLN,
		Type:   TypeTransportSpecificFeedback,
		Length: uint16((.MarshalSize() / 4) - 1),
	}
}

func ( TransportLayerNack) () string {
	 := fmt.Sprintf("TransportLayerNack from %x\n", .SenderSSRC)
	 += fmt.Sprintf("\tMedia Ssrc %x\n", .MediaSSRC)
	 += "\tID\tLostPackets\n"
	for ,  := range .Nacks {
		 += fmt.Sprintf("\t%d\t%b\n", .PacketID, .LostPackets)
	}
	return 
}

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