package websocket

import (
	
	
	
	
	

	
	ma 
	manet 

	ws 
)

// GracefulCloseTimeout is the time to wait trying to gracefully close a
// connection before simply cutting it.
var GracefulCloseTimeout = 100 * time.Millisecond

// Conn implements net.Conn interface for gorilla/websocket.
type Conn struct {
	*ws.Conn
	Scope              network.ConnManagementScope
	secure             bool
	DefaultMessageType int
	reader             io.Reader
	closeOnceVal       func() error
	laddr              ma.Multiaddr
	raddr              ma.Multiaddr

	readLock, writeLock sync.Mutex
}

var _ net.Conn = (*Conn)(nil)
var _ manet.Conn = (*Conn)(nil)

// newConn creates a Conn given a regular gorilla/websocket Conn.
func newConn( *ws.Conn,  bool,  network.ConnManagementScope) *Conn {
	 := NewAddrWithScheme(.LocalAddr().String(), )
	,  := manet.FromNetAddr()
	if  != nil {
		log.Errorf("BUG: invalid localaddr on websocket conn", .LocalAddr())
		return nil
	}

	 := NewAddrWithScheme(.RemoteAddr().String(), )
	,  := manet.FromNetAddr()
	if  != nil {
		log.Errorf("BUG: invalid remoteaddr on websocket conn", .RemoteAddr())
		return nil
	}

	 := &Conn{
		Conn:               ,
		Scope:              ,
		secure:             ,
		DefaultMessageType: ws.BinaryMessage,
		laddr:              ,
		raddr:              ,
	}
	.closeOnceVal = sync.OnceValue(.closeOnceFn)
	return 
}

// LocalMultiaddr implements manet.Conn.
func ( *Conn) () ma.Multiaddr {
	return .laddr
}

// RemoteMultiaddr implements manet.Conn.
func ( *Conn) () ma.Multiaddr {
	return .raddr
}

func ( *Conn) ( []byte) (int, error) {
	.readLock.Lock()
	defer .readLock.Unlock()

	if .reader == nil {
		if  := .prepNextReader();  != nil {
			return 0, 
		}
	}

	for {
		,  := .reader.Read()
		switch  {
		case io.EOF:
			.reader = nil

			if  > 0 {
				return , nil
			}

			if  := .prepNextReader();  != nil {
				return 0, 
			}

			// explicitly looping
		default:
			return , 
		}
	}
}

func ( *Conn) () error {
	, ,  := .Conn.NextReader()
	if  != nil {
		if ,  := .(*ws.CloseError);  {
			if .Code == 1000 || .Code == 1005 {
				return io.EOF
			}
		}
		return 
	}

	if  == ws.CloseMessage {
		return io.EOF
	}

	.reader = 
	return nil
}

func ( *Conn) ( []byte) ( int,  error) {
	.writeLock.Lock()
	defer .writeLock.Unlock()

	if  := .Conn.WriteMessage(.DefaultMessageType, );  != nil {
		return 0, 
	}

	return len(), nil
}

// Close closes the connection.
// subsequent and concurrent calls will return the same error value.
// This method is thread-safe.
func ( *Conn) () error {
	return .closeOnceVal()
}

func ( *Conn) () error {
	 := .Conn.WriteControl(
		ws.CloseMessage,
		ws.FormatCloseMessage(ws.CloseNormalClosure, "closed"),
		time.Now().Add(GracefulCloseTimeout),
	)
	 := .Conn.Close()
	return errors.Join(, )
}

func ( *Conn) () net.Addr {
	return NewAddrWithScheme(.Conn.LocalAddr().String(), .secure)
}

func ( *Conn) () net.Addr {
	return NewAddrWithScheme(.Conn.RemoteAddr().String(), .secure)
}

func ( *Conn) ( time.Time) error {
	if  := .SetReadDeadline();  != nil {
		return 
	}

	return .SetWriteDeadline()
}

func ( *Conn) ( time.Time) error {
	// Don't lock when setting the read deadline. That would prevent us from
	// interrupting an in-progress read.
	return .Conn.SetReadDeadline()
}

func ( *Conn) ( time.Time) error {
	// Unlike the read deadline, we need to lock when setting the write
	// deadline.

	.writeLock.Lock()
	defer .writeLock.Unlock()

	return .Conn.SetWriteDeadline()
}