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

//go:build !js
// +build !js

package webrtc

import (
	
	
	

	
)

// RTPTransceiver represents a combination of an RTPSender and an RTPReceiver that share a common mid.
type RTPTransceiver struct {
	mid              atomic.Value // string
	sender           atomic.Value // *RTPSender
	receiver         atomic.Value // *RTPReceiver
	direction        atomic.Value // RTPTransceiverDirection
	currentDirection atomic.Value // RTPTransceiverDirection

	codecs []RTPCodecParameters // User provided codecs via SetCodecPreferences

	kind RTPCodecType

	api *API
	mu  sync.RWMutex
}

func newRTPTransceiver(
	 *RTPReceiver,
	 *RTPSender,
	 RTPTransceiverDirection,
	 RTPCodecType,
	 *API,
) *RTPTransceiver {
	 := &RTPTransceiver{kind: , api: }
	.setReceiver()
	.setSender()
	.setDirection()
	.setCurrentDirection(RTPTransceiverDirectionUnknown)

	return 
}

// SetCodecPreferences sets preferred list of supported codecs
// if codecs is empty or nil we reset to default from MediaEngine.
func ( *RTPTransceiver) ( []RTPCodecParameters) error {
	.mu.Lock()
	defer .mu.Unlock()

	for ,  := range  {
		if ,  := codecParametersFuzzySearch(
			, .api.mediaEngine.getCodecsByKind(.kind),
		);  == codecMatchNone {
			return fmt.Errorf("%w %s", errRTPTransceiverCodecUnsupported, .MimeType)
		}
	}

	.codecs = 

	return nil
}

// Codecs returns list of supported codecs.
func ( *RTPTransceiver) () []RTPCodecParameters {
	.mu.RLock()
	defer .mu.RUnlock()

	 := .api.mediaEngine.getCodecsByKind(.kind)
	if len(.codecs) == 0 {
		return 
	}

	 := []RTPCodecParameters{}
	for ,  := range .codecs {
		if ,  := codecParametersFuzzySearch(, );  != codecMatchNone {
			if .PayloadType == 0 {
				.PayloadType = .PayloadType
			}
			.RTCPFeedback = rtcpFeedbackIntersection(.RTCPFeedback, .RTCPFeedback)
			 = append(, )
		}
	}

	return 
}

// Sender returns the RTPTransceiver's RTPSender if it has one.
func ( *RTPTransceiver) () *RTPSender {
	if ,  := .sender.Load().(*RTPSender);  {
		return 
	}

	return nil
}

// SetSender sets the RTPSender and Track to current transceiver.
func ( *RTPTransceiver) ( *RTPSender,  TrackLocal) error {
	.setSender()

	return .setSendingTrack()
}

func ( *RTPTransceiver) ( *RTPSender) {
	if  != nil {
		.setRTPTransceiver()
	}

	if  := .Sender();  != nil {
		.setRTPTransceiver(nil)
	}

	.sender.Store()
}

// Receiver returns the RTPTransceiver's RTPReceiver if it has one.
func ( *RTPTransceiver) () *RTPReceiver {
	if ,  := .receiver.Load().(*RTPReceiver);  {
		return 
	}

	return nil
}

// SetMid sets the RTPTransceiver's mid. If it was already set, will return an error.
func ( *RTPTransceiver) ( string) error {
	if  := .Mid();  != "" {
		return fmt.Errorf("%w: %s to %s", errRTPTransceiverCannotChangeMid, , )
	}
	.mid.Store()

	return nil
}

// Mid gets the Transceiver's mid value. When not already set, this value will be set in CreateOffer or CreateAnswer.
func ( *RTPTransceiver) () string {
	if ,  := .mid.Load().(string);  {
		return 
	}

	return ""
}

// Kind returns RTPTransceiver's kind.
func ( *RTPTransceiver) () RTPCodecType {
	return .kind
}

// Direction returns the RTPTransceiver's current direction.
func ( *RTPTransceiver) () RTPTransceiverDirection {
	if ,  := .direction.Load().(RTPTransceiverDirection);  {
		return 
	}

	return RTPTransceiverDirection(0)
}

// Stop irreversibly stops the RTPTransceiver.
func ( *RTPTransceiver) () error {
	if  := .Sender();  != nil {
		if  := .Stop();  != nil {
			return 
		}
	}
	if  := .Receiver();  != nil {
		if  := .Stop();  != nil {
			return 
		}
	}

	.setDirection(RTPTransceiverDirectionInactive)
	.setCurrentDirection(RTPTransceiverDirectionInactive)

	return nil
}

func ( *RTPTransceiver) ( *RTPReceiver) {
	if  != nil {
		.setRTPTransceiver()
	}

	if  := .Receiver();  != nil {
		.setRTPTransceiver(nil)
	}

	.receiver.Store()
}

func ( *RTPTransceiver) ( RTPTransceiverDirection) {
	.direction.Store()
}

func ( *RTPTransceiver) ( RTPTransceiverDirection) {
	.currentDirection.Store()
}

func ( *RTPTransceiver) () RTPTransceiverDirection {
	if ,  := .currentDirection.Load().(RTPTransceiverDirection);  {
		return 
	}

	return RTPTransceiverDirectionUnknown
}

func ( *RTPTransceiver) ( TrackLocal) error { //nolint:cyclop
	if  := .Sender().ReplaceTrack();  != nil {
		return 
	}
	if  == nil {
		.setSender(nil)
	}

	switch {
	case  != nil && .Direction() == RTPTransceiverDirectionRecvonly:
		.setDirection(RTPTransceiverDirectionSendrecv)
	case  != nil && .Direction() == RTPTransceiverDirectionInactive:
		.setDirection(RTPTransceiverDirectionSendonly)
	case  == nil && .Direction() == RTPTransceiverDirectionSendrecv:
		.setDirection(RTPTransceiverDirectionRecvonly)
	case  != nil && .Direction() == RTPTransceiverDirectionSendonly:
		// Handle the case where a sendonly transceiver was added by a negotiation
		// initiated by remote peer. For example a remote peer added a transceiver
		// with direction recvonly.
	case  != nil && .Direction() == RTPTransceiverDirectionSendrecv:
		// Similar to above, but for sendrecv transceiver.
	case  == nil && .Direction() == RTPTransceiverDirectionSendonly:
		.setDirection(RTPTransceiverDirectionInactive)
	default:
		return errRTPTransceiverSetSendingInvalidState
	}

	return nil
}

func findByMid( string,  []*RTPTransceiver) (*RTPTransceiver, []*RTPTransceiver) {
	for ,  := range  {
		if .Mid() ==  {
			return , append([:], [+1:]...)
		}
	}

	return nil, 
}

// Given a direction+type pluck a transceiver from the passed list
// if no entry satisfies the requested type+direction return a inactive Transceiver.
func satisfyTypeAndDirection(
	 RTPCodecType,
	 RTPTransceiverDirection,
	 []*RTPTransceiver,
) (*RTPTransceiver, []*RTPTransceiver) {
	// Get direction order from most preferred to least
	 := func() []RTPTransceiverDirection {
		switch  {
		case RTPTransceiverDirectionSendrecv:
			return []RTPTransceiverDirection{
				RTPTransceiverDirectionRecvonly,
				RTPTransceiverDirectionSendrecv,
				RTPTransceiverDirectionSendonly,
			}
		case RTPTransceiverDirectionSendonly:
			return []RTPTransceiverDirection{RTPTransceiverDirectionRecvonly, RTPTransceiverDirectionSendrecv}
		case RTPTransceiverDirectionRecvonly:
			return []RTPTransceiverDirection{RTPTransceiverDirectionSendonly, RTPTransceiverDirectionSendrecv}
		default:
			return []RTPTransceiverDirection{}
		}
	}

	for ,  := range () {
		for  := range  {
			 := []
			if .Mid() == "" && .kind ==  &&  == .Direction() {
				return , append([:], [+1:]...)
			}
		}
	}

	return nil, 
}

// handleUnknownRTPPacket consumes a single RTP Packet and returns information that is helpful
// for demuxing and handling an unknown SSRC (usually for Simulcast).
func handleUnknownRTPPacket(
	 []byte,
	,
	,
	 uint8,
	, ,  *string,
) ( PayloadType,  bool,  error) {
	 := &rtp.Packet{}
	if  = .Unmarshal();  != nil {
		return 0, false, 
	}

	if .Padding && len(.Payload) == 0 {
		 = true
	}

	if !.Header.Extension {
		return , , nil
	}

	 = PayloadType(.PayloadType)
	if  := .GetExtension();  != nil {
		* = string()
	}

	if  := .GetExtension();  != nil {
		* = string()
	}

	if  := .GetExtension();  != nil {
		* = string()
	}

	return , , nil
}