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

package rtpbuffer

import (
	
	
	

	
)

const rtxSsrcByteLength = 2

// PacketFactory allows custom logic around the handle of RTP Packets before they added to the RTPBuffer.
// The NoOpPacketFactory doesn't copy packets, while the RetainablePacket will take a copy before adding.
type PacketFactory interface {
	NewPacket(header *rtp.Header, payload []byte, rtxSsrc uint32, rtxPayloadType uint8) (*RetainablePacket, error)
}

// PacketFactoryCopy is PacketFactory that takes a copy of packets when added to the RTPBuffer.
type PacketFactoryCopy struct {
	headerPool   *sync.Pool
	payloadPool  *sync.Pool
	rtxSequencer rtp.Sequencer
}

// NewPacketFactoryCopy constructs a PacketFactory that takes a copy of packets when added to the RTPBuffer.
func () *PacketFactoryCopy {
	return &PacketFactoryCopy{
		headerPool: &sync.Pool{
			New: func() interface{} {
				return &rtp.Header{}
			},
		},
		payloadPool: &sync.Pool{
			New: func() interface{} {
				 := make([]byte, maxPayloadLen)

				return &
			},
		},
		rtxSequencer: rtp.NewRandomSequencer(),
	}
}

// NewPacket constructs a new RetainablePacket that can be added to the RTPBuffer.
//
//nolint:cyclop
func ( *PacketFactoryCopy) (
	 *rtp.Header,  []byte,  uint32,  uint8,
) (*RetainablePacket, error) {
	if len() > maxPayloadLen {
		return nil, io.ErrShortBuffer
	}

	 := &RetainablePacket{
		onRelease:      .releasePacket,
		sequenceNumber: .SequenceNumber,
		// new packets have retain count of 1
		count: 1,
	}

	var  bool
	.header,  = .headerPool.Get().(*rtp.Header)
	if ! {
		return nil, errFailedToCastHeaderPool
	}

	*.header = .Clone()

	if  != nil {
		.buffer,  = .payloadPool.Get().(*[]byte)
		if ! {
			return nil, errFailedToCastPayloadPool
		}
		if  != 0 &&  != 0 {
			 := copy((*.buffer)[rtxSsrcByteLength:], )
			.payload = (*.buffer)[:+rtxSsrcByteLength]
		} else {
			 := copy(*.buffer, )
			.payload = (*.buffer)[:]
		}
	}

	if  != 0 &&  != 0 { //nolint:nestif
		if  == nil {
			.buffer,  = .payloadPool.Get().(*[]byte)
			if ! {
				return nil, errFailedToCastPayloadPool
			}
			.payload = (*.buffer)[:rtxSsrcByteLength]
		}
		// Write the original sequence number at the beginning of the payload.
		binary.BigEndian.PutUint16(.payload, .header.SequenceNumber)

		// Rewrite the SSRC.
		.header.SSRC = 
		// Rewrite the payload type.
		.header.PayloadType = 
		// Rewrite the sequence number.
		.header.SequenceNumber = .rtxSequencer.NextSequenceNumber()
		// Remove padding if present.
		if .header.Padding {
			// Older versions of pion/rtp didn't have the Header.PaddingSize field and as a workaround
			// users had to add padding to the payload. We need to handle this case here.
			if .header.PaddingSize == 0 && len(.payload) > 0 {
				 := int(.payload[len(.payload)-1])
				if  > len(.payload) {
					return nil, errPaddingOverflow
				}
				.payload = (*.buffer)[:len(.payload)-]
			}

			.header.Padding = false
			.header.PaddingSize = 0
		}
	}

	return , nil
}

func ( *PacketFactoryCopy) ( *rtp.Header,  *[]byte) {
	.headerPool.Put()
	if  != nil {
		.payloadPool.Put()
	}
}

// PacketFactoryNoOp is a PacketFactory implementation that doesn't copy packets.
type PacketFactoryNoOp struct{}

// NewPacket constructs a new RetainablePacket that can be added to the RTPBuffer.
func ( *PacketFactoryNoOp) (
	 *rtp.Header,  []byte,  uint32,  uint8,
) (*RetainablePacket, error) {
	return &RetainablePacket{
		onRelease:      .releasePacket,
		count:          1,
		header:         ,
		payload:        ,
		sequenceNumber: .SequenceNumber,
	}, nil
}

func ( *PacketFactoryNoOp) ( *rtp.Header,  *[]byte) {
	// no-op
}