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

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

package webrtc

import (
	
	
	
	

	
	
	
	
)

const sctpMaxChannels = uint16(65535)

// SCTPTransport provides details about the SCTP transport.
type SCTPTransport struct {
	lock sync.RWMutex

	dtlsTransport *DTLSTransport

	// State represents the current state of the SCTP transport.
	state SCTPTransportState

	// SCTPTransportState doesn't have an enum to distinguish between New/Connecting
	// so we need a dedicated field
	isStarted bool

	// MaxChannels represents the maximum amount of DataChannel's that can
	// be used simultaneously.
	maxChannels *uint16

	// OnStateChange  func()

	onErrorHandler func(error)
	onCloseHandler func(error)

	sctpAssociation            *sctp.Association
	onDataChannelHandler       func(*DataChannel)
	onDataChannelOpenedHandler func(*DataChannel)

	// DataChannels
	dataChannels          []*DataChannel
	dataChannelIDsUsed    map[uint16]struct{}
	dataChannelsOpened    uint32
	dataChannelsRequested uint32
	dataChannelsAccepted  uint32

	api *API
	log logging.LeveledLogger
}

// NewSCTPTransport creates a new SCTPTransport.
// This constructor is part of the ORTC API. It is not
// meant to be used together with the basic WebRTC API.
func ( *API) ( *DTLSTransport) *SCTPTransport {
	 := &SCTPTransport{
		dtlsTransport:      ,
		state:              SCTPTransportStateConnecting,
		api:                ,
		log:                .settingEngine.LoggerFactory.NewLogger("ortc"),
		dataChannelIDsUsed: make(map[uint16]struct{}),
	}

	.updateMaxChannels()

	return 
}

// Transport returns the DTLSTransport instance the SCTPTransport is sending over.
func ( *SCTPTransport) () *DTLSTransport {
	.lock.RLock()
	defer .lock.RUnlock()

	return .dtlsTransport
}

// GetCapabilities returns the SCTPCapabilities of the SCTPTransport.
func ( *SCTPTransport) () SCTPCapabilities {
	var  uint32
	if  := .association();  != nil {
		 = .MaxMessageSize()
	}

	return SCTPCapabilities{
		MaxMessageSize: ,
	}
}

// Start the SCTPTransport. Since both local and remote parties must mutually
// create an SCTPTransport, SCTP SO (Simultaneous Open) is used to establish
// a connection over SCTP.
func ( *SCTPTransport) ( SCTPCapabilities) error {
	if .isStarted {
		return nil
	}
	.isStarted = true

	 := .MaxMessageSize
	if  == 0 {
		 = sctpMaxMessageSizeUnsetValue
	}

	 := .Transport()
	if  == nil || .conn == nil {
		return errSCTPTransportDTLS
	}
	,  := sctp.Client(sctp.Config{
		NetConn:              .conn,
		MaxReceiveBufferSize: .api.settingEngine.sctp.maxReceiveBufferSize,
		EnableZeroChecksum:   .api.settingEngine.sctp.enableZeroChecksum,
		LoggerFactory:        .api.settingEngine.LoggerFactory,
		RTOMax:               float64(.api.settingEngine.sctp.rtoMax) / float64(time.Millisecond),
		BlockWrite:           .api.settingEngine.detach.DataChannels && .api.settingEngine.dataChannelBlockWrite,
		MaxMessageSize:       ,
		MTU:                  outboundMTU,
		MinCwnd:              .api.settingEngine.sctp.minCwnd,
		FastRtxWnd:           .api.settingEngine.sctp.fastRtxWnd,
		CwndCAStep:           .api.settingEngine.sctp.cwndCAStep,
	})
	if  != nil {
		return 
	}

	.lock.Lock()
	.sctpAssociation = 
	.state = SCTPTransportStateConnected
	 := append([]*DataChannel{}, .dataChannels...)
	.lock.Unlock()

	var  uint32
	for ,  := range  {
		if .ReadyState() == DataChannelStateConnecting {
			 := .open()
			if  != nil {
				.log.Warnf("failed to open data channel: %s", )

				continue
			}
			++
		}
	}

	.lock.Lock()
	.dataChannelsOpened += 
	.lock.Unlock()

	go .acceptDataChannels(, )

	return nil
}

// Stop stops the SCTPTransport.
func ( *SCTPTransport) () error {
	.lock.Lock()
	defer .lock.Unlock()
	if .sctpAssociation == nil {
		return nil
	}

	.sctpAssociation.Abort("")

	.sctpAssociation = nil
	.state = SCTPTransportStateClosed

	return nil
}

//nolint:cyclop
func ( *SCTPTransport) (
	 *sctp.Association,
	 []*DataChannel,
) {
	 := make([]*datachannel.DataChannel, 0, len())
	for ,  := range  {
		.mu.Lock()
		 := .dataChannel == nil
		.mu.Unlock()
		if  {
			continue
		}
		 = append(, .dataChannel)
	}
:
	for {
		,  := datachannel.Accept(, &datachannel.Config{
			LoggerFactory: .api.settingEngine.LoggerFactory,
		}, ...)
		if  != nil {
			if !errors.Is(, io.EOF) {
				.log.Errorf("Failed to accept data channel: %v", )
				.onError()
				.onClose()
			} else {
				.onClose(nil)
			}

			return
		}
		for ,  := range  {
			if .StreamIdentifier() == .StreamIdentifier() {
				continue 
			}
		}

		var (
			    *uint16
			 *uint16
		)
		 := uint16(.Config.ReliabilityParameter) //nolint:gosec //G115
		 := true

		switch .Config.ChannelType {
		case datachannel.ChannelTypeReliable:
			 = true
		case datachannel.ChannelTypeReliableUnordered:
			 = false
		case datachannel.ChannelTypePartialReliableRexmit:
			 = true
			 = &
		case datachannel.ChannelTypePartialReliableRexmitUnordered:
			 = false
			 = &
		case datachannel.ChannelTypePartialReliableTimed:
			 = true
			 = &
		case datachannel.ChannelTypePartialReliableTimedUnordered:
			 = false
			 = &
		default:
		}

		 := .StreamIdentifier()
		,  := .api.newDataChannel(&DataChannelParameters{
			ID:                &,
			Label:             .Config.Label,
			Protocol:          .Config.Protocol,
			Negotiated:        .Config.Negotiated,
			Ordered:           ,
			MaxPacketLifeTime: ,
			MaxRetransmits:    ,
		}, , .api.settingEngine.LoggerFactory.NewLogger("ortc"))
		if  != nil {
			// This data channel is invalid. Close it and log an error.
			if  := .Close();  != nil {
				.log.Errorf("Failed to close invalid data channel: %v", )
			}
			.log.Errorf("Failed to accept data channel: %v", )
			.onError()
			// We've received a datachannel with invalid configuration. We can still receive other datachannels.
			continue 
		}

		<-.onDataChannel()
		.handleOpen(, true, .Config.Negotiated)

		.lock.Lock()
		.dataChannelsOpened++
		 := .onDataChannelOpenedHandler
		.lock.Unlock()

		if  != nil {
			()
		}
	}
}

// OnError sets an event handler which is invoked when the SCTP Association errors.
func ( *SCTPTransport) ( func( error)) {
	.lock.Lock()
	defer .lock.Unlock()
	.onErrorHandler = 
}

func ( *SCTPTransport) ( error) {
	.lock.RLock()
	 := .onErrorHandler
	.lock.RUnlock()

	if  != nil {
		go ()
	}
}

// OnClose sets an event handler which is invoked when the SCTP Association closes.
func ( *SCTPTransport) ( func( error)) {
	.lock.Lock()
	defer .lock.Unlock()
	.onCloseHandler = 
}

func ( *SCTPTransport) ( error) {
	.lock.RLock()
	 := .onCloseHandler
	.lock.RUnlock()

	if  != nil {
		go ()
	}
}

// OnDataChannel sets an event handler which is invoked when a data
// channel message arrives from a remote peer.
func ( *SCTPTransport) ( func(*DataChannel)) {
	.lock.Lock()
	defer .lock.Unlock()
	.onDataChannelHandler = 
}

// OnDataChannelOpened sets an event handler which is invoked when a data
// channel is opened.
func ( *SCTPTransport) ( func(*DataChannel)) {
	.lock.Lock()
	defer .lock.Unlock()
	.onDataChannelOpenedHandler = 
}

func ( *SCTPTransport) ( *DataChannel) ( chan struct{}) {
	.lock.Lock()
	.dataChannels = append(.dataChannels, )
	.dataChannelsAccepted++
	if .ID() != nil {
		.dataChannelIDsUsed[*.ID()] = struct{}{}
	} else {
		// This cannot happen, the constructor for this datachannel in the caller
		// takes a pointer to the id.
		.log.Errorf("accepted data channel with no ID")
	}
	 := .onDataChannelHandler
	.lock.Unlock()

	 = make(chan struct{})
	if  == nil ||  == nil {
		close()

		return
	}

	// Run this synchronously to allow setup done in onDataChannelFn()
	// to complete before datachannel event handlers might be called.
	go func() {
		()
		close()
	}()

	return
}

func ( *SCTPTransport) () {
	 := sctpMaxChannels
	.maxChannels = &
}

// MaxChannels is the maximum number of RTCDataChannels that can be open simultaneously.
func ( *SCTPTransport) () uint16 {
	.lock.Lock()
	defer .lock.Unlock()

	if .maxChannels == nil {
		return sctpMaxChannels
	}

	return *.maxChannels
}

// State returns the current state of the SCTPTransport.
func ( *SCTPTransport) () SCTPTransportState {
	.lock.RLock()
	defer .lock.RUnlock()

	return .state
}

func ( *SCTPTransport) ( *statsReportCollector) {
	.Collecting()

	 := SCTPTransportStats{
		Timestamp: statsTimestampFrom(time.Now()),
		Type:      StatsTypeSCTPTransport,
		ID:        "sctpTransport",
	}

	 := .association()
	if  != nil {
		.BytesSent = .BytesSent()
		.BytesReceived = .BytesReceived()
		.SmoothedRoundTripTime = .SRTT() * 0.001 // convert milliseconds to seconds
		.CongestionWindow = .CWND()
		.ReceiverWindow = .RWND()
		.MTU = .MTU()
	}

	.Collect(.ID, )
}

func ( *SCTPTransport) ( DTLSRole,  **uint16) error {
	var  uint16
	if  != DTLSRoleClient {
		++
	}

	 := .MaxChannels()

	.lock.Lock()
	defer .lock.Unlock()

	for ;  < -1;  += 2 {
		if ,  := .dataChannelIDsUsed[];  {
			continue
		}
		* = &
		.dataChannelIDsUsed[] = struct{}{}

		return nil
	}

	return &rtcerr.OperationError{Err: ErrMaxDataChannelID}
}

func ( *SCTPTransport) () *sctp.Association {
	if  == nil {
		return nil
	}
	.lock.RLock()
	 := .sctpAssociation
	.lock.RUnlock()

	return 
}

// BufferedAmount returns total amount (in bytes) of currently buffered user data.
func ( *SCTPTransport) () int {
	.lock.Lock()
	defer .lock.Unlock()
	if .sctpAssociation == nil {
		return 0
	}

	return .sctpAssociation.BufferedAmount()
}