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

package rtcp

import (
	
	
)

// SDESType is the item type used in the RTCP SDES control packet.
type SDESType uint8

// RTP SDES item types registered with IANA. See: https://www.iana.org/assignments/rtp-parameters/rtp-parameters.xhtml#rtp-parameters-5
const (
	SDESEnd      SDESType = iota // end of SDES list                RFC 3550, 6.5
	SDESCNAME                    // canonical name                  RFC 3550, 6.5.1
	SDESName                     // user name                       RFC 3550, 6.5.2
	SDESEmail                    // user's electronic mail address  RFC 3550, 6.5.3
	SDESPhone                    // user's phone number             RFC 3550, 6.5.4
	SDESLocation                 // geographic user location        RFC 3550, 6.5.5
	SDESTool                     // name of application or tool     RFC 3550, 6.5.6
	SDESNote                     // notice about the source         RFC 3550, 6.5.7
	SDESPrivate                  // private extensions              RFC 3550, 6.5.8  (not implemented)
)

func ( SDESType) () string {
	switch  {
	case SDESEnd:
		return "END"
	case SDESCNAME:
		return "CNAME"
	case SDESName:
		return "NAME"
	case SDESEmail:
		return "EMAIL"
	case SDESPhone:
		return "PHONE"
	case SDESLocation:
		return "LOC"
	case SDESTool:
		return "TOOL"
	case SDESNote:
		return "NOTE"
	case SDESPrivate:
		return "PRIV"
	default:
		return string()
	}
}

const (
	sdesSourceLen        = 4
	sdesTypeLen          = 1
	sdesTypeOffset       = 0
	sdesOctetCountLen    = 1
	sdesOctetCountOffset = 1
	sdesMaxOctetCount    = (1 << 8) - 1
	sdesTextOffset       = 2
)

// A SourceDescription (SDES) packet describes the sources in an RTP stream.
type SourceDescription struct {
	Chunks []SourceDescriptionChunk
}

// NewCNAMESourceDescription creates a new SourceDescription with a single CNAME item.
func ( uint32,  string) *SourceDescription {
	return &SourceDescription{
		Chunks: []SourceDescriptionChunk{{
			Source: ,
			Items: []SourceDescriptionItem{{
				Type: SDESCNAME,
				Text: ,
			}},
		}},
	}
}

// Marshal encodes the SourceDescription in binary
func ( SourceDescription) () ([]byte, error) {
	/*
	 *         0                   1                   2                   3
	 *         0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
	 *        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 * header |V=2|P|    SC   |  PT=SDES=202  |             length            |
	 *        +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
	 * chunk  |                          SSRC/CSRC_1                          |
	 *   1    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 *        |                           SDES items                          |
	 *        |                              ...                              |
	 *        +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
	 * chunk  |                          SSRC/CSRC_2                          |
	 *   2    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 *        |                           SDES items                          |
	 *        |                              ...                              |
	 *        +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
	 */

	 := make([]byte, .MarshalSize())
	 := [headerLength:]

	 := 0
	for ,  := range .Chunks {
		,  := .Marshal()
		if  != nil {
			return nil, 
		}
		copy([:], )
		 += len()
	}

	if len(.Chunks) > countMax {
		return nil, errTooManyChunks
	}

	,  := .Header().Marshal()
	if  != nil {
		return nil, 
	}
	copy(, )

	return , nil
}

// Unmarshal decodes the SourceDescription from binary
func ( *SourceDescription) ( []byte) error {
	/*
	 *         0                   1                   2                   3
	 *         0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
	 *        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 * header |V=2|P|    SC   |  PT=SDES=202  |             length            |
	 *        +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
	 * chunk  |                          SSRC/CSRC_1                          |
	 *   1    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 *        |                           SDES items                          |
	 *        |                              ...                              |
	 *        +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
	 * chunk  |                          SSRC/CSRC_2                          |
	 *   2    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 *        |                           SDES items                          |
	 *        |                              ...                              |
	 *        +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
	 */

	var  Header
	if  := .Unmarshal();  != nil {
		return 
	}

	if .Type != TypeSourceDescription {
		return errWrongType
	}

	for  := headerLength;  < len(); {
		var  SourceDescriptionChunk
		if  := .Unmarshal([:]);  != nil {
			return 
		}
		.Chunks = append(.Chunks, )

		 += .len()
	}

	if len(.Chunks) != int(.Count) {
		return errInvalidHeader
	}

	return nil
}

// MarshalSize returns the size of the packet once marshaled
func ( *SourceDescription) () int {
	 := 0
	for ,  := range .Chunks {
		 += .len()
	}
	return headerLength + 
}

// Header returns the Header associated with this packet.
func ( *SourceDescription) () Header {
	return Header{
		Count:  uint8(len(.Chunks)),
		Type:   TypeSourceDescription,
		Length: uint16((.MarshalSize() / 4) - 1),
	}
}

// A SourceDescriptionChunk contains items describing a single RTP source
type SourceDescriptionChunk struct {
	// The source (ssrc) or contributing source (csrc) identifier this packet describes
	Source uint32
	Items  []SourceDescriptionItem
}

// Marshal encodes the SourceDescriptionChunk in binary
func ( SourceDescriptionChunk) () ([]byte, error) {
	/*
	 *  +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
	 *  |                          SSRC/CSRC_1                          |
	 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 *  |                           SDES items                          |
	 *  |                              ...                              |
	 *  +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
	 */

	 := make([]byte, sdesSourceLen)
	binary.BigEndian.PutUint32(, .Source)

	for ,  := range .Items {
		,  := .Marshal()
		if  != nil {
			return nil, 
		}
		 = append(, ...)
	}

	// The list of items in each chunk MUST be terminated by one or more null octets
	 = append(, uint8(SDESEnd))

	// additional null octets MUST be included if needed to pad until the next 32-bit boundary
	 = append(, make([]byte, getPadding(len()))...)

	return , nil
}

// Unmarshal decodes the SourceDescriptionChunk from binary
func ( *SourceDescriptionChunk) ( []byte) error {
	/*
	 *  +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
	 *  |                          SSRC/CSRC_1                          |
	 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 *  |                           SDES items                          |
	 *  |                              ...                              |
	 *  +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
	 */

	if len() < (sdesSourceLen + sdesTypeLen) {
		return errPacketTooShort
	}

	.Source = binary.BigEndian.Uint32()

	for  := 4;  < len(); {
		if  := SDESType([]);  == SDESEnd {
			return nil
		}

		var  SourceDescriptionItem
		if  := .Unmarshal([:]);  != nil {
			return 
		}
		.Items = append(.Items, )
		 += .Len()
	}

	return errPacketTooShort
}

func ( SourceDescriptionChunk) () int {
	 := sdesSourceLen
	for ,  := range .Items {
		 += .Len()
	}
	 += sdesTypeLen // for terminating null octet

	// align to 32-bit boundary
	 += getPadding()

	return 
}

// A SourceDescriptionItem is a part of a SourceDescription that describes a stream.
type SourceDescriptionItem struct {
	// The type identifier for this item. eg, SDESCNAME for canonical name description.
	//
	// Type zero or SDESEnd is interpreted as the end of an item list and cannot be used.
	Type SDESType
	// Text is a unicode text blob associated with the item. Its meaning varies based on the item's Type.
	Text string
}

// Len returns the length of the SourceDescriptionItem when encoded as binary.
func ( SourceDescriptionItem) () int {
	/*
	 *   0                   1                   2                   3
	 *   0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
	 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 *  |    CNAME=1    |     length    | user and domain name        ...
	 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 */
	return sdesTypeLen + sdesOctetCountLen + len([]byte(.Text))
}

// Marshal encodes the SourceDescriptionItem in binary
func ( SourceDescriptionItem) () ([]byte, error) {
	/*
	 *   0                   1                   2                   3
	 *   0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
	 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 *  |    CNAME=1    |     length    | user and domain name        ...
	 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 */

	if .Type == SDESEnd {
		return nil, errSDESMissingType
	}

	 := make([]byte, sdesTypeLen+sdesOctetCountLen)

	[sdesTypeOffset] = uint8(.Type)

	 := []byte(.Text)
	 := len()
	if  > sdesMaxOctetCount {
		return nil, errSDESTextTooLong
	}
	[sdesOctetCountOffset] = uint8()

	 = append(, ...)

	return , nil
}

// Unmarshal decodes the SourceDescriptionItem from binary
func ( *SourceDescriptionItem) ( []byte) error {
	/*
	 *   0                   1                   2                   3
	 *   0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
	 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 *  |    CNAME=1    |     length    | user and domain name        ...
	 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 */

	if len() < (sdesTypeLen + sdesOctetCountLen) {
		return errPacketTooShort
	}

	.Type = SDESType([sdesTypeOffset])

	 := int([sdesOctetCountOffset])
	if sdesTextOffset+ > len() {
		return errPacketTooShort
	}

	 := [sdesTextOffset : sdesTextOffset+]
	.Text = string()

	return nil
}

// DestinationSSRC returns an array of SSRC values that this packet refers to.
func ( *SourceDescription) () []uint32 {
	 := make([]uint32, len(.Chunks))
	for ,  := range .Chunks {
		[] = .Source
	}
	return 
}

func ( *SourceDescription) () string {
	 := "Source Description:\n"
	for ,  := range .Chunks {
		 += fmt.Sprintf("\t%x: %s\n", .Source, .Items)
	}
	return 
}