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
	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

	// 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()
			 = &
		}
	}

	 := .OOB()
	// increase oob slice capacity, so we can add the UDP_SEGMENT and ECN control messages without allocating
	 := len()
	 = append(, make([]byte, 64)...)[:]
	 := &sconn{
		rawConn:   ,
		localAddr: ,
		logger:    ,
	}
	.remoteAddrInfo.Store(&remoteAddrInfo{
		addr: ,
		oob:  ,
	})
	return 
}

func ( *sconn) ( []byte,  uint16,  protocol.ECN) error {
	 := .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
	return 
}

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

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

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

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