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

package obu

import (
	
)

// Type represents the type of an AV1 OBU.
type Type uint8

// OBU types as defined in the AV1 specification.
// 5.3.1: https://aomediacodec.github.io/av1-spec/av1-spec.pdf#page=39
const (
	// OBUSequenceHeader av1 sequence_header_obu.
	OBUSequenceHeader = Type(1)
	// OBUTemporalDelimiter av1 temporal_delimiter_obu.
	OBUTemporalDelimiter = Type(2)
	// OBUFrameHeader av1 frame_header_obu.
	OBUFrameHeader = Type(3)
	// OBUTileGroup av1 tile_group_obu.
	OBUTileGroup = Type(4)
	// OBUMetadata av1 metadata_obu.
	OBUMetadata = Type(5)
	// OBUFrame av1 frame_obu.
	OBUFrame = Type(6)
	// OBURedundantFrameHeader av1 redundant_frame_header_obu.
	OBURedundantFrameHeader = Type(7)
	// OBUTileList av1 tile_list_obu.
	OBUTileList = Type(8)
	// OBUPadding av1 padding_obu.
	OBUPadding = Type(15)
)

//nolint:cyclop
func ( Type) () string {
	switch  {
	case OBUSequenceHeader:
		return "OBU_SEQUENCE_HEADER"
	case OBUTemporalDelimiter:
		return "OBU_TEMPORAL_DELIMITER"
	case OBUFrameHeader:
		return "OBU_FRAME_HEADER"
	case OBUTileGroup:
		return "OBU_TILE_GROUP"
	case OBUMetadata:
		return "OBU_METADATA"
	case OBUFrame:
		return "OBU_FRAME"
	case OBURedundantFrameHeader:
		return "OBU_REDUNDANT_FRAME_HEADER"
	case OBUTileList:
		return "OBU_TILE_LIST"
	case OBUPadding:
		return "OBU_PADDING"
	default:
		return "OBU_RESERVED"
	}
}

// Header represents the header of an OBU obu_header().
// 5.3.2: https://aomediacodec.github.io/av1-spec/av1-spec.pdf#page=40
type Header struct {
	Type            Type
	ExtensionHeader *ExtensionHeader
	HasSizeField    bool
	Reserved1Bit    bool
}

// ParseOBUHeader parses an OBU header from the given data.
// 5.3.2: https://aomediacodec.github.io/av1-spec/av1-spec.pdf#page=40
/*
	obu_header() { Type
		obu_forbidden_bit     f(1)
		obu_type              f(4)
		obu_extension_flag    f(1)
		obu_has_size_field    f(1)
		obu_reserved_1bit     f(1)
		if ( obu_extension_flag == 1 )
			obu_extension_header()
		}
	}
*/
func ( []byte) (*Header, error) {
	if len() < 1 {
		return nil, fmt.Errorf("%w: data is too short", ErrShortHeader)
	}

	 := [0] & 0x80
	if  != 0 {
		return nil, fmt.Errorf("%w: forbidden bit is set", ErrInvalidOBUHeader)
	}

	 := Type(([0] & 0x78) >> 3)
	 := ([0] & 0x04) != 0
	 := ([0] & 0x02) != 0
	 := ([0] & 0x01) != 0

	 := &Header{
		Type:         ,
		HasSizeField: ,
		Reserved1Bit: ,
	}

	if  {
		if len() < 2 {
			return nil, fmt.Errorf("%w: Unexpected end of data, expected extension header", ErrShortHeader)
		}

		 := ParseOBUExtensionHeader([1])
		.ExtensionHeader = &
	}

	return , nil
}

// Marshal serializes the OBU header to a byte slice.
// If the OBU has an extension header, the extension header is serialized as well.
// 5.3.2: https://aomediacodec.github.io/av1-spec/av1-spec.pdf#page=40
/*
	obu_header() { Type
		obu_forbidden_bit     f(1)
		obu_type              f(4)
		obu_extension_flag    f(1)
		obu_has_size_field    f(1)
		obu_reserved_1bit     f(1)
		if ( obu_extension_flag == 1 )
			obu_extension_header()
		}
	}
*/
func ( *Header) () []byte {
	 := make([]byte, .Size())

	[0] = (byte(.Type) & 0x0f) << 3

	if .ExtensionHeader != nil {
		[0] |= 0x04
		[1] = .ExtensionHeader.Marshal()
	}

	if .HasSizeField {
		[0] |= 0x02
	}

	if .Reserved1Bit {
		[0] |= 0x01
	}

	return 
}

// Size returns the size of the OBU header in bytes.
func ( *Header) () int {
	 := 1
	if .ExtensionHeader != nil {
		++
	}

	return 
}

// ExtensionHeader represents an OBU extension header obu_extension_header().
// 5.3.3 https://aomediacodec.github.io/av1-spec/av1-spec.pdf#page=40
type ExtensionHeader struct {
	TemporalID    uint8
	SpatialID     uint8
	Reserved3Bits uint8
}

// ParseOBUExtensionHeader parses an OBU extension header from the given data.
// 5.3.3 https://aomediacodec.github.io/av1-spec/av1-spec.pdf#page=40
/*
	obu_extension_header() { Type
		temporal_id                      f(3)
		spatial_id                       f(2)
		extension_header_reserved_3bits  f(3)
	}
*/
func ( byte) ExtensionHeader {
	return ExtensionHeader{
		TemporalID:     >> 5,
		SpatialID:     ( >> 3) & 0x03,
		Reserved3Bits:  & 0x07,
	}
}

// Marshal serializes the OBU extension header to a byte slice.
// 5.3.3 https://aomediacodec.github.io/av1-spec/av1-spec.pdf#page=40
/*
	obu_extension_header() { Type
		temporal_id                      f(3)
		spatial_id                       f(2)
		extension_header_reserved_3bits  f(3)
	}
*/
func ( *ExtensionHeader) () byte {
	return (.TemporalID << 5) | ((.SpatialID & 0x3) << 3) | (.Reserved3Bits & 0x07)
}

// OBU represents an AV1 OBU.
// 5.1 https://aomediacodec.github.io/av1-spec/av1-spec.pdf#page=39
type OBU struct {
	Header  Header
	Payload []byte
}

// Marshal serializes the OBU to low-overhead bitstream format.
// 5.2 https://aomediacodec.github.io/av1-spec/av1-spec.pdf#page=40
func ( *OBU) () []byte {
	 := .Header.Marshal()

	if .Header.HasSizeField {
		 = append(, WriteToLeb128(uint(len(.Payload)))...)
	}

	return append(, .Payload...)
}