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

package codecs

import (
	

	
)

// AV1Depacketizer is a AV1 RTP Packet depacketizer.
// Reads AV1 packets from a RTP stream and outputs AV1 low overhead bitstream.
type AV1Depacketizer struct {
	// holds the fragmented OBU from the previous packet.
	buffer []byte

	// Z, Y, N are flags from the AV1 Aggregation Header.
	Z, Y, N bool

	videoDepacketizer
}

// Unmarshal parses an AV1 RTP payload into its constituent OBUs stream with obu_size_field,
// It assumes that the payload is in order (e.g. the caller is responsible for reordering RTP packets).
// If the last OBU in the payload is fragmented, it will be stored in the buffer until the
// it is completed.
//
//nolint:gocognit,cyclop
func ( *AV1Depacketizer) ( []byte) ( []byte,  error) {
	 = make([]byte, 0)

	if len() <= 1 {
		return nil, errShortPacket
	}

	// |Z|Y| W |N|-|-|-|
	 := (av1ZMask & [0]) != 0     // Z
	 := (av1YMask & [0]) != 0     // Y
	 := (av1WMask & [0]) >> 4 // W
	 := (av1NMask & [0]) != 0     // N
	.Z = 
	.Y = 
	.N = 
	if  {
		.buffer = nil
	}

	// Make sure we clear the buffer if Z is not 0.
	if ! && len(.buffer) > 0 {
		.buffer = nil
	}

	 := 0
	for  := 1;  < len(); ++ {
		 :=  == 0
		 :=  != 0 &&  == int()-1

		// https://aomediacodec.github.io/av1-rtp-spec/#44-av1-aggregation-header
		// W: two bit field that describes the number of OBU elements in the packet.
		// This field MUST be set equal to 0 or equal to the number of OBU elements contained in the packet.
		// If set to 0, each OBU element MUST be preceded by a length field. If not set to 0
		// (i.e., W = 1, 2 or 3) the last OBU element MUST NOT be preceded by a length field.
		var ,  int
		if  == 0 || ! {
			, ,  := obu.ReadLeb128([:])
			 = int() //nolint:gosec // G115 false positive
			 = int()                 //nolint:gosec // G115 false positive
			if  != nil {
				return nil, 
			}

			 += 
			if  == 0 && + == len() {
				 = true
			}
		} else {
			// https://aomediacodec.github.io/av1-rtp-spec/#44-av1-aggregation-header
			// Length of the last OBU element =
			// length of the RTP payload
			// - length of aggregation header
			// - length of previous OBU elements including length fields
			 = len() - 
		}

		if + > len() {
			return nil, fmt.Errorf(
				"%w: OBU size %d + %d offset exceeds payload length %d",
				errShortPacket, , , len(),
			)
		}

		var  []byte
		if  &&  {
			// We lost the first fragment of the OBU
			// We drop the buffer and continue
			if len(.buffer) == 0 {
				if  {
					break
				}
				 += 

				continue
			}

			 = make([]byte, len(.buffer)+)

			copy(, .buffer)
			copy([len(.buffer):], [:+])
			.buffer = nil
		} else {
			 = [ : +]
		}
		 += 

		if  &&  {
			.buffer = 

			break
		}

		if len() == 0 {
			continue
		}

		,  := obu.ParseOBUHeader()
		if  != nil {
			return nil, 
		}

		// The temporal delimiter OBU, if present, SHOULD be removed when transmitting,
		// and MUST be ignored by receivers. Tile list OBUs are not supported.
		// They SHOULD be removed when transmitted, and MUST be ignored by receivers.
		// https://aomediacodec.github.io/av1-rtp-spec/#5-packetization-rules
		if .Type == obu.OBUTemporalDelimiter || .Type == obu.OBUTileList {
			continue
		}

		// obu_has_size_field should be set to 0 for AV1 RTP packets.
		// But we still check it to be sure, if we get obu size we just use it, instead of calculating it.
		if .HasSizeField {
			, ,  := obu.ReadLeb128([.Size():])
			if  != nil {
				return nil, 
			}

			// We validate the obu_size_field if it is present.
			 := .Size() + int() + int() //nolint:gosec
			if  !=  {
				return nil, fmt.Errorf(
					"%w: OBU size %d does not match calculated size %d",
					errShortPacket, , ,
				)
			}

			 = append(, ...)
		} else {
			.HasSizeField = true
			 = append(, .Marshal()...)
			 := len() - .Size()
			 = append(, obu.WriteToLeb128(uint())...) // nolint: gosec // G104
			 = append(, [.Size():]...)
		}

		if  {
			break
		}
	}

	if  != 0 &&  != int(-1) {
		return nil, fmt.Errorf(
			"%w: OBU count %d does not match number of OBUs %d",
			errShortPacket, , ,
		)
	}

	return , nil
}

// IsPartitionHead returns true if Z in the AV1 Aggregation Header
// is set to 0.
func ( *AV1Depacketizer) ( []byte) bool {
	if len() == 0 {
		return false
	}

	return ([0] & av1ZMask) == 0
}