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

package codecs

// VP8Payloader payloads VP8 packets.
type VP8Payloader struct {
	EnablePictureID bool
	pictureID       uint16
}

const (
	vp8HeaderSize = 1
)

// Payload fragments a VP8 packet across one or more byte arrays.
func ( *VP8Payloader) ( uint16,  []byte) [][]byte { //nolint:cyclop
	/*
	 * https://tools.ietf.org/html/rfc7741#section-4.2
	 *
	 *       0 1 2 3 4 5 6 7
	 *      +-+-+-+-+-+-+-+-+
	 *      |X|R|N|S|R| PID | (REQUIRED)
	 *      +-+-+-+-+-+-+-+-+
	 * X:   |I|L|T|K| RSV   | (OPTIONAL)
	 *      +-+-+-+-+-+-+-+-+
	 * I:   |M| PictureID   | (OPTIONAL)
	 *      +-+-+-+-+-+-+-+-+
	 * L:   |   TL0PICIDX   | (OPTIONAL)
	 *      +-+-+-+-+-+-+-+-+
	 * T/K: |TID|Y| KEYIDX  | (OPTIONAL)
	 *      +-+-+-+-+-+-+-+-+
	 *  S: Start of VP8 partition.  SHOULD be set to 1 when the first payload
	 *     octet of the RTP packet is the beginning of a new VP8 partition,
	 *     and MUST NOT be 1 otherwise.  The S bit MUST be set to 1 for the
	 *     first packet of each encoded frame.
	 */

	 := vp8HeaderSize
	if .EnablePictureID {
		switch {
		case .pictureID == 0:
		case .pictureID < 128:
			 = vp8HeaderSize + 2
		default:
			 = vp8HeaderSize + 3
		}
	}

	 := int() - 

	 := 
	 := len()

	 := 0
	var  [][]byte

	// Make sure the fragment/payload size is correct
	if minInt(, ) <= 0 {
		return 
	}
	 := true
	for  > 0 {
		 := minInt(, )
		 := make([]byte, +)

		if  {
			[0] = 0x10
			 = false
		}
		if .EnablePictureID {
			switch  {
			case vp8HeaderSize:
			case vp8HeaderSize + 2:
				[0] |= 0x80
				[1] |= 0x80
				[2] |= uint8(.pictureID & 0x7F) // nolint: gosec // G115 false positive
			case vp8HeaderSize + 3:
				[0] |= 0x80
				[1] |= 0x80
				[2] |= 0x80 | uint8((.pictureID>>8)&0x7F) // nolint: gosec // G115 false positive
				[3] |= uint8(.pictureID & 0xFF)           // nolint: gosec // G115 false positive
			}
		}

		copy([:], [:+])
		 = append(, )

		 -= 
		 += 
	}

	.pictureID++
	.pictureID &= 0x7FFF

	return 
}

// VP8Packet represents the VP8 header that is stored in the payload of an RTP Packet.
type VP8Packet struct {
	// Required Header
	X   uint8 /* extended control bits present */
	N   uint8 /* when set to 1 this frame can be discarded */
	S   uint8 /* start of VP8 partition */
	PID uint8 /* partition index */

	// Extended control bits
	I uint8 /* 1 if PictureID is present */
	L uint8 /* 1 if TL0PICIDX is present */
	T uint8 /* 1 if TID is present */
	K uint8 /* 1 if KEYIDX is present */

	// Optional extension
	PictureID uint16 /* 8 or 16 bits, picture ID */
	TL0PICIDX uint8  /* 8 bits temporal level zero index */
	TID       uint8  /* 2 bits temporal layer index */
	Y         uint8  /* 1 bit layer sync bit */
	KEYIDX    uint8  /* 5 bits temporal key frame index */

	Payload []byte

	videoDepacketizer
}

// Unmarshal parses the passed byte slice and stores the result in the VP8Packet this method is called upon.
func ( *VP8Packet) ( []byte) ([]byte, error) { //nolint:gocognit,cyclop
	if  == nil {
		return nil, errNilPacket
	}

	 := len()

	 := 0

	if  >=  {
		return nil, errShortPacket
	}
	.X = ([] & 0x80) >> 7
	.N = ([] & 0x20) >> 5
	.S = ([] & 0x10) >> 4
	.PID = [] & 0x07

	++

	if .X == 1 {
		if  >=  {
			return nil, errShortPacket
		}
		.I = ([] & 0x80) >> 7
		.L = ([] & 0x40) >> 6
		.T = ([] & 0x20) >> 5
		.K = ([] & 0x10) >> 4
		++
	} else {
		.I = 0
		.L = 0
		.T = 0
		.K = 0
	}

	// nolint: nestif
	if .I == 1 { // PID present?
		if  >=  {
			return nil, errShortPacket
		}
		if []&0x80 > 0 { // M == 1, PID is 16bit
			if +1 >=  {
				return nil, errShortPacket
			}
			.PictureID = (uint16([]&0x7F) << 8) | uint16([+1])
			 += 2
		} else {
			.PictureID = uint16([])
			++
		}
	} else {
		.PictureID = 0
	}

	if .L == 1 {
		if  >=  {
			return nil, errShortPacket
		}
		.TL0PICIDX = []
		++
	} else {
		.TL0PICIDX = 0
	}

	if .T == 1 || .K == 1 { // nolint: nestif
		if  >=  {
			return nil, errShortPacket
		}
		if .T == 1 {
			.TID = [] >> 6
			.Y = ([] >> 5) & 0x1
		} else {
			.TID = 0
			.Y = 0
		}
		if .K == 1 {
			.KEYIDX = [] & 0x1F
		} else {
			.KEYIDX = 0
		}
		++
	} else {
		.TID = 0
		.Y = 0
		.KEYIDX = 0
	}

	.Payload = [:]

	return .Payload, nil
}

// VP8PartitionHeadChecker checks VP8 partition head
//
// Deprecated: replaced by VP8Packet.IsPartitionHead().
type VP8PartitionHeadChecker struct{}

// IsPartitionHead checks whether if this is a head of the VP8 partition.
//
// Deprecated: replaced by VP8Packet.IsPartitionHead().
func (*VP8PartitionHeadChecker) ( []byte) bool {
	return (&VP8Packet{}).IsPartitionHead()
}

// IsPartitionHead checks whether if this is a head of the VP8 partition.
func (*VP8Packet) ( []byte) bool {
	if len() < 1 {
		return false
	}

	return ([0] & 0x10) != 0
}