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

package ice

import (
	
	
	
	

	
)

// Dial connects to the remote agent, acting as the controlling ice agent.
// Dial blocks until at least one ice candidate pair has successfully connected.
func ( *Agent) ( context.Context, ,  string) (*Conn, error) {
	return .connect(, true, , )
}

// Accept connects to the remote agent, acting as the controlled ice agent.
// Accept blocks until at least one ice candidate pair has successfully connected.
func ( *Agent) ( context.Context, ,  string) (*Conn, error) {
	return .connect(, false, , )
}

// Conn represents the ICE connection.
// At the moment the lifetime of the Conn is equal to the Agent.
type Conn struct {
	bytesReceived uint64
	bytesSent     uint64
	agent         *Agent
}

// BytesSent returns the number of bytes sent.
func ( *Conn) () uint64 {
	return atomic.LoadUint64(&.bytesSent)
}

// BytesReceived returns the number of bytes received.
func ( *Conn) () uint64 {
	return atomic.LoadUint64(&.bytesReceived)
}

func ( *Agent) ( context.Context,  bool, ,  string) (*Conn, error) {
	 := .loop.Err()
	if  != nil {
		return nil, 
	}
	 = .startConnectivityChecks(, , ) //nolint:contextcheck
	if  != nil {
		return nil, 
	}

	// Block until pair selected
	select {
	case <-.loop.Done():
		return nil, .loop.Err()
	case <-.Done():
		return nil, ErrCanceledByCaller
	case <-.onConnected:
	}

	return &Conn{
		agent: ,
	}, nil
}

// Read implements the Conn Read method.
func ( *Conn) ( []byte) (int, error) {
	 := .agent.loop.Err()
	if  != nil {
		return 0, 
	}

	,  := .agent.buf.Read()
	atomic.AddUint64(&.bytesReceived, uint64()) //nolint:gosec // G115

	return , 
}

// Write implements the Conn Write method.
func ( *Conn) ( []byte) (int, error) {
	 := .agent.loop.Err()
	if  != nil {
		return 0, 
	}

	if stun.IsMessage() {
		return 0, errWriteSTUNMessageToIceConn
	}

	 := .agent.getSelectedPair()
	if  == nil {
		if  = .agent.loop.Run(.agent.loop, func( context.Context) {
			 = .agent.getBestValidCandidatePair()
		});  != nil {
			return 0, 
		}

		if  == nil {
			return 0, 
		}
	}

	atomic.AddUint64(&.bytesSent, uint64(len()))

	return .Write()
}

// Close implements the Conn Close method. It is used to close
// the connection. Any calls to Read and Write will be unblocked and return an error.
func ( *Conn) () error {
	return .agent.Close()
}

// LocalAddr returns the local address of the current selected pair or nil if there is none.
func ( *Conn) () net.Addr {
	 := .agent.getSelectedPair()
	if  == nil {
		return nil
	}

	return .Local.addr()
}

// RemoteAddr returns the remote address of the current selected pair or nil if there is none.
func ( *Conn) () net.Addr {
	 := .agent.getSelectedPair()
	if  == nil {
		return nil
	}

	return .Remote.addr()
}

// SetDeadline is a stub.
func ( *Conn) (time.Time) error {
	return nil
}

// SetReadDeadline is a stub.
func ( *Conn) (time.Time) error {
	return nil
}

// SetWriteDeadline is a stub.
func ( *Conn) (time.Time) error {
	return nil
}