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

package rtp

import (
	
	
	
)

const (
	headerExtensionIDReserved = 0xF
)

// HeaderExtension represents an RTP extension header.
type HeaderExtension interface {
	Set(id uint8, payload []byte) error
	GetIDs() []uint8
	Get(id uint8) []byte
	Del(id uint8) error

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

// OneByteHeaderExtension is an RFC8285 one-byte header extension.
type OneByteHeaderExtension struct {
	payload []byte
}

// Set sets the extension payload for the specified ID.
func ( *OneByteHeaderExtension) ( uint8,  []byte) error {
	if  < 1 ||  > 14 {
		return fmt.Errorf("%w actual(%d)", errRFC8285OneByteHeaderIDRange, )
	}
	if len() > 16 {
		return fmt.Errorf("%w actual(%d)", errRFC8285OneByteHeaderSize, len())
	}

	for  := 4;  < len(.payload); {
		if .payload[] == 0x00 { // padding
			++

			continue
		}

		 := .payload[] >> 4
		 := int(.payload[]&^0xF0 + 1)
		++

		if  ==  {
			.payload = append(.payload[:+1], append(, .payload[+1+:]...)...)

			return nil
		}
		 += 
	}
	.payload = append(.payload, (<<4 | uint8(len()-1))) // nolint: gosec // G115
	.payload = append(.payload, ...)
	binary.BigEndian.PutUint16(.payload[2:4], binary.BigEndian.Uint16(.payload[2:4])+1)

	return nil
}

// GetIDs returns the available IDs.
func ( *OneByteHeaderExtension) () []uint8 {
	 := make([]uint8, 0, binary.BigEndian.Uint16(.payload[2:4]))
	for  := 4;  < len(.payload); {
		if .payload[] == 0x00 { // padding
			++

			continue
		}

		 := .payload[] >> 4
		 := int(.payload[]&^0xF0 + 1)
		++

		if  == headerExtensionIDReserved {
			break
		}

		 = append(, )
		 += 
	}

	return 
}

// Get returns the payload of the extension with the given ID.
func ( *OneByteHeaderExtension) ( uint8) []byte {
	for  := 4;  < len(.payload); {
		if .payload[] == 0x00 { // padding
			++

			continue
		}

		 := .payload[] >> 4
		 := int(.payload[]&^0xF0 + 1)
		++

		if  ==  {
			return .payload[ : +]
		}
		 += 
	}

	return nil
}

// Del deletes the extension with the specified ID.
func ( *OneByteHeaderExtension) ( uint8) error {
	for  := 4;  < len(.payload); {
		if .payload[] == 0x00 { // padding
			++

			continue
		}

		 := .payload[] >> 4
		 := int(.payload[]&^0xF0 + 1)

		if  ==  {
			.payload = append(.payload[:], .payload[+1+:]...)

			return nil
		}
		 +=  + 1
	}

	return errHeaderExtensionNotFound
}

// Unmarshal parses the extension payload.
func ( *OneByteHeaderExtension) ( []byte) (int, error) {
	 := binary.BigEndian.Uint16([0:2])
	if  != ExtensionProfileOneByte {
		return 0, fmt.Errorf("%w actual(%x)", errHeaderExtensionNotFound, [0:2])
	}
	.payload = 

	return len(), nil
}

// Marshal returns the extension payload.
func ( OneByteHeaderExtension) () ([]byte, error) {
	return .payload, nil
}

// MarshalTo writes the extension payload to the given buffer.
func ( OneByteHeaderExtension) ( []byte) (int, error) {
	 := .MarshalSize()
	if  > len() {
		return 0, io.ErrShortBuffer
	}

	return copy(, .payload), nil
}

// MarshalSize returns the size of the extension payload.
func ( OneByteHeaderExtension) () int {
	return len(.payload)
}

// TwoByteHeaderExtension is an RFC8285 two-byte header extension.
type TwoByteHeaderExtension struct {
	payload []byte
}

// Set sets the extension payload for the specified ID.
func ( *TwoByteHeaderExtension) ( uint8,  []byte) error {
	if  < 1 {
		return fmt.Errorf("%w actual(%d)", errRFC8285TwoByteHeaderIDRange, )
	}
	if len() > 255 {
		return fmt.Errorf("%w actual(%d)", errRFC8285TwoByteHeaderSize, len())
	}

	for  := 4;  < len(.payload); {
		if .payload[] == 0x00 { // padding
			++

			continue
		}

		 := .payload[]
		++

		 := int(.payload[])
		++

		if  ==  {
			.payload = append(.payload[:+2], append(, .payload[+2+:]...)...)

			return nil
		}
		 += 
	}
	.payload = append(.payload, , uint8(len())) // nolint: gosec // G115
	.payload = append(.payload, ...)
	binary.BigEndian.PutUint16(.payload[2:4], binary.BigEndian.Uint16(.payload[2:4])+1)

	return nil
}

// GetIDs returns the available IDs.
func ( *TwoByteHeaderExtension) () []uint8 {
	 := make([]uint8, 0, binary.BigEndian.Uint16(.payload[2:4]))
	for  := 4;  < len(.payload); {
		if .payload[] == 0x00 { // padding
			++

			continue
		}

		 := .payload[]
		++

		 := int(.payload[])
		++

		 = append(, )
		 += 
	}

	return 
}

// Get returns the payload of the extension with the given ID.
func ( *TwoByteHeaderExtension) ( uint8) []byte {
	for  := 4;  < len(.payload); {
		if .payload[] == 0x00 { // padding
			++

			continue
		}

		 := .payload[]
		++

		 := int(.payload[])
		++

		if  ==  {
			return .payload[ : +]
		}
		 += 
	}

	return nil
}

// Del deletes the extension with the specified ID.
func ( *TwoByteHeaderExtension) ( uint8) error {
	for  := 4;  < len(.payload); {
		if .payload[] == 0x00 { // padding
			++

			continue
		}

		 := .payload[]

		 := int(.payload[+1])

		if  ==  {
			.payload = append(.payload[:], .payload[+2+:]...)

			return nil
		}
		 +=  + 2
	}

	return errHeaderExtensionNotFound
}

// Unmarshal parses the extension payload.
func ( *TwoByteHeaderExtension) ( []byte) (int, error) {
	 := binary.BigEndian.Uint16([0:2])
	if  != ExtensionProfileTwoByte {
		return 0, fmt.Errorf("%w actual(%x)", errHeaderExtensionNotFound, [0:2])
	}
	.payload = 

	return len(), nil
}

// Marshal returns the extension payload.
func ( TwoByteHeaderExtension) () ([]byte, error) {
	return .payload, nil
}

// MarshalTo marshals the extension to the given buffer.
func ( TwoByteHeaderExtension) ( []byte) (int, error) {
	 := .MarshalSize()
	if  > len() {
		return 0, io.ErrShortBuffer
	}

	return copy(, .payload), nil
}

// MarshalSize returns the size of the extension payload.
func ( TwoByteHeaderExtension) () int {
	return len(.payload)
}

// RawExtension represents an RFC3550 header extension.
type RawExtension struct {
	payload []byte
}

// Set sets the extension payload for the specified ID.
func ( *RawExtension) ( uint8,  []byte) error {
	if  != 0 {
		return fmt.Errorf("%w actual(%d)", errRFC3550HeaderIDRange, )
	}
	.payload = 

	return nil
}

// GetIDs returns the available IDs.
func ( *RawExtension) () []uint8 {
	return []uint8{0}
}

// Get returns the payload of the extension with the given ID.
func ( *RawExtension) ( uint8) []byte {
	if  == 0 {
		return .payload
	}

	return nil
}

// Del deletes the extension with the specified ID.
func ( *RawExtension) ( uint8) error {
	if  == 0 {
		.payload = nil

		return nil
	}

	return fmt.Errorf("%w actual(%d)", errRFC3550HeaderIDRange, )
}

// Unmarshal parses the extension from the given buffer.
func ( *RawExtension) ( []byte) (int, error) {
	 := binary.BigEndian.Uint16([0:2])
	if  == ExtensionProfileOneByte ||  == ExtensionProfileTwoByte {
		return 0, fmt.Errorf("%w actual(%x)", errHeaderExtensionNotFound, [0:2])
	}
	.payload = 

	return len(), nil
}

// Marshal returns the raw extension payload.
func ( RawExtension) () ([]byte, error) {
	return .payload, nil
}

// MarshalTo marshals the extension to the given buffer.
func ( RawExtension) ( []byte) (int, error) {
	 := .MarshalSize()
	if  > len() {
		return 0, io.ErrShortBuffer
	}

	return copy(, .payload), nil
}

// MarshalSize returns the size of the extension when marshaled.
func ( RawExtension) () int {
	return len(.payload)
}