package quic

import (
	
	
	

	
	
)

// A sendConn allows sending using a simple Write() on a non-connected packet conn.
type sendConn interface {
	Write(b []byte, gsoSize uint16, ecn protocol.ECN) error
	WriteTo([]byte, net.Addr) error
	WriteToInfo([]byte, net.Addr, packetInfo) error
	Close() error
	LocalAddr() net.Addr
	RemoteAddr() net.Addr
	ChangeRemoteAddr(addr net.Addr, info packetInfo)

	capabilities() connCapabilities
}

type remoteAddrInfo struct {
	addr net.Addr
	oob  []byte
}

type sconn struct {
	rawConn

	localAddr net.Addr

	remoteAddrInfo atomic.Pointer[remoteAddrInfo]

	logger utils.Logger

	// writeMu serializes writes. A sconn is written from several goroutines:
	// the connection run loop (inline sends, probes, connection-close packets)
	// and the dedicated sendQueue.Run goroutine. They share the gotGSOError and
	// wroteFirstPacket fields and the oob scratch buffer, so writes must not
	// overlap. The mutex is uncontended on the common path and is never held
	// across a blocking operation.
	writeMu     sync.Mutex
	performance sendConnPerformanceCounters

	// If GSO enabled, and we receive a GSO error for this remote address, GSO is disabled.
	gotGSOError bool
	// Used to catch the error sometimes returned by the first sendmsg call on Linux,
	// see https://github.com/golang/go/issues/63322.
	wroteFirstPacket bool
}

var _ sendConn = &sconn{}

func newSendConn( rawConn,  net.Addr,  packetInfo,  utils.Logger) *sconn {
	 := .LocalAddr()
	if .addr.IsValid() {
		if ,  := .(*net.UDPAddr);  {
			 := *
			.IP = .addr.AsSlice()
			 = &
		}
	}

	 := &sconn{
		rawConn:   ,
		localAddr: ,
		logger:    ,
	}
	.remoteAddrInfo.Store(&remoteAddrInfo{
		addr: ,
		oob:  packetInfoOOB(),
	})
	return 
}

func ( *sconn) ( []byte,  uint16,  protocol.ECN) error {
	.writeMu.Lock()
	defer .writeMu.Unlock()
	 := .remoteAddrInfo.Load()
	 := .writePacket(, .addr, .oob, , )
	if  != nil && isGSOError() {
		// disable GSO for future calls
		.gotGSOError = true
		if .logger.Debug() {
			.logger.Debugf("GSO failed when sending to %s", .addr)
		}
		// send out the packets one by one
		for len() > 0 {
			 := len()
			if  > int() {
				 = int()
			}
			if  := .writePacket([:], .addr, .oob, 0, );  != nil {
				return 
			}
			 = [:]
		}
		return nil
	}
	return 
}

func ( *sconn) ( []byte,  net.Addr,  []byte,  uint16,  protocol.ECN) error {
	,  := .WritePacket(, , , , )
	if  != nil && !.wroteFirstPacket && isPermissionError() {
		_,  = .WritePacket(, , , , )
	}
	.wroteFirstPacket = true
	if  == nil {
		.performance.recordWrite(len(), )
	}
	return 
}

func ( *sconn) ( []byte,  net.Addr) error {
	.writeMu.Lock()
	defer .writeMu.Unlock()
	,  := .WritePacket(, , nil, 0, protocol.ECNUnsupported)
	return 
}

func ( *sconn) ( []byte,  net.Addr,  packetInfo) error {
	.writeMu.Lock()
	defer .writeMu.Unlock()
	,  := .WritePacket(, , .OOB(), 0, protocol.ECNUnsupported)
	return 
}

func ( *sconn) () connCapabilities {
	 := .rawConn.capabilities()
	if .GSO {
		.GSO = !.gotGSOError
	}
	return 
}

func ( *sconn) ( net.Addr,  packetInfo) {
	.remoteAddrInfo.Store(&remoteAddrInfo{
		addr: ,
		oob:  packetInfoOOB(),
	})
}

func packetInfoOOB( packetInfo) []byte {
	 := .OOB()
	// Reserve space for UDP_SEGMENT and ECN control messages.
	 := len()
	return append(, make([]byte, 64)...)[:]
}

func ( *sconn) () net.Addr { return .remoteAddrInfo.Load().addr }
func ( *sconn) () net.Addr  { return .localAddr }