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

package rtcp

import (
	
	
	
)

// These functions implement an introspective structure
// serializer/deserializer, designed to allow RTCP packet
// Structs to be self-describing. They currently work with
// fields of type uint8, uint16, uint32, and uint64 (and
// types derived from them).
//
// - Unexported fields will take up space in the encoded
//   array, but wil be set to zero when written, and ignore
//   when read.
//
// - Fields that are marked with the tag `encoding:"omit"`
//   will be ignored when reading and writing data.
//
// For example:
//
//   type Example struct {
//     A uint32
//     B bool   `encoding:"omit"`
//     _ uint64
//     C uint16
//   }
//
// "A" will be encoded as four bytes, in network order. "B"
// will not be encoded at all. The anonymous uint64 will
// encode as 8 bytes of value "0", followed by two bytes
// encoding "C" in network order.

type packetBuffer struct {
	bytes []byte
}

const omit = "omit"

// Writes the structure passed to into the buffer that
// PacketBuffer is initialized with. This function will
// modify the PacketBuffer.bytes slice to exclude those
// bytes that have been written into.
func ( *packetBuffer) ( interface{}) error { //nolint:gocognit
	 := reflect.ValueOf()

	// Indirect is safe to call on non-pointers, and
	// will simply return the same value in such cases
	 = reflect.Indirect()

	switch .Kind() {
	case reflect.Uint8:
		if len(.bytes) < 1 {
			return errWrongMarshalSize
		}
		if .CanInterface() {
			.bytes[0] = byte(.Uint())
		}
		.bytes = .bytes[1:]
	case reflect.Uint16:
		if len(.bytes) < 2 {
			return errWrongMarshalSize
		}
		if .CanInterface() {
			binary.BigEndian.PutUint16(.bytes, uint16(.Uint()))
		}
		.bytes = .bytes[2:]
	case reflect.Uint32:
		if len(.bytes) < 4 {
			return errWrongMarshalSize
		}
		if .CanInterface() {
			binary.BigEndian.PutUint32(.bytes, uint32(.Uint()))
		}
		.bytes = .bytes[4:]
	case reflect.Uint64:
		if len(.bytes) < 8 {
			return errWrongMarshalSize
		}
		if .CanInterface() {
			binary.BigEndian.PutUint64(.bytes, .Uint())
		}
		.bytes = .bytes[8:]
	case reflect.Slice:
		for  := 0;  < .Len(); ++ {
			if .Index().CanInterface() {
				if  := .(.Index().Interface());  != nil {
					return 
				}
			} else {
				.bytes = .bytes[.Index().Type().Size():]
			}
		}
	case reflect.Struct:
		for  := 0;  < .NumField(); ++ {
			 := .Type().Field().Tag.Get("encoding")
			if  == omit {
				continue
			}
			if .Field().CanInterface() {
				if  := .(.Field().Interface());  != nil {
					return 
				}
			} else {
				 := int(.Field().Type().Size())
				if len(.bytes) <  {
					return errWrongMarshalSize
				}
				.bytes = .bytes[:]
			}
		}
	default:
		return errBadStructMemberType
	}
	return nil
}

// Reads bytes from the buffer as necessary to populate
// the structure passed as a parameter. This function will
// modify the PacketBuffer.bytes slice to exclude those
// bytes that have already been read.
func ( *packetBuffer) ( interface{}) error { //nolint:gocognit
	 := reflect.ValueOf()
	if .Kind() != reflect.Ptr {
		return errBadReadParameter
	}
	 := reflect.Indirect()

	// If this is an interface, we need to make it concrete before using it
	if .Kind() == reflect.Interface {
		 = reflect.ValueOf(.Interface())
	}
	 = reflect.Indirect()

	switch .Kind() {
	case reflect.Uint8:
		if len(.bytes) < 1 {
			return errWrongMarshalSize
		}
		.SetUint(uint64(.bytes[0]))
		.bytes = .bytes[1:]

	case reflect.Uint16:
		if len(.bytes) < 2 {
			return errWrongMarshalSize
		}
		.SetUint(uint64(binary.BigEndian.Uint16(.bytes)))
		.bytes = .bytes[2:]

	case reflect.Uint32:
		if len(.bytes) < 4 {
			return errWrongMarshalSize
		}
		.SetUint(uint64(binary.BigEndian.Uint32(.bytes)))
		.bytes = .bytes[4:]

	case reflect.Uint64:
		if len(.bytes) < 8 {
			return errWrongMarshalSize
		}
		.SetUint(binary.BigEndian.Uint64(.bytes))
		.bytes = .bytes[8:]

	case reflect.Slice:
		// If we encounter a slice, we consume the rest of the data
		// in the buffer and load it into the slice.
		for len(.bytes) > 0 {
			 := reflect.New(.Type().Elem())
			if  := .(.Interface());  != nil {
				return 
			}
			if .CanSet() {
				.Set(reflect.Append(, reflect.Indirect()))
			}
		}

	case reflect.Struct:
		for  := 0;  < .NumField(); ++ {
			 := .Type().Field().Tag.Get("encoding")
			if  == omit {
				continue
			}
			if .Field().CanInterface() {
				 := .Field()
				 := reflect.NewAt(.Type(), unsafe.Pointer(.UnsafeAddr())) //nolint:gosec // This is the only way to get a typed pointer to a structure's field
				if  := .(.Interface());  != nil {
					return 
				}
			} else {
				 := int(.Field().Type().Size())
				if len(.bytes) <  {
					return errWrongMarshalSize
				}
				.bytes = .bytes[:]
			}
		}

	default:
		return errBadStructMemberType
	}
	return nil
}

// Consumes `size` bytes and returns them as an
// independent PacketBuffer
func ( *packetBuffer) ( int) packetBuffer {
	if  > len(.bytes) {
		 = len(.bytes)
	}
	 := packetBuffer{bytes: .bytes[:]}
	.bytes = .bytes[:]
	return 
}

// Returns the size that a structure will encode into.
// This fuction doesn't check that Write() will succeed,
// and may return unexpectedly large results for those
// structures that Write() will fail on
func wireSize( interface{}) int {
	 := reflect.ValueOf()
	// Indirect is safe to call on non-pointers, and
	// will simply return the same value in such cases
	 = reflect.Indirect()
	 := int(0)

	switch .Kind() {
	case reflect.Slice:
		for  := 0;  < .Len(); ++ {
			if .Index().CanInterface() {
				 += (.Index().Interface())
			} else {
				 += int(.Index().Type().Size())
			}
		}

	case reflect.Struct:
		for  := 0;  < .NumField(); ++ {
			 := .Type().Field().Tag.Get("encoding")
			if  == omit {
				continue
			}
			if .Field().CanInterface() {
				 += (.Field().Interface())
			} else {
				 += int(.Field().Type().Size())
			}
		}

	default:
		 = int(.Type().Size())
	}
	return 
}