package libp2pquic

import (
	

	
	tpt 
	

	ma 
	
)

const acceptBufferPerVersion = 4

// virtualListener is a listener that exposes a single multiaddr but uses another listener under the hood
type virtualListener struct {
	*listener
	udpAddr       string
	version       quic.Version
	t             *transport
	acceptRunnner *acceptLoopRunner
	acceptChan    chan acceptVal
}

var _ tpt.Listener = &virtualListener{}

func ( *virtualListener) () ma.Multiaddr {
	return .listener.localMultiaddrs[.version]
}

func ( *virtualListener) () error {
	.acceptRunnner.RmAcceptForVersion(.version, tpt.ErrListenerClosed)
	return .t.CloseVirtualListener()
}

func ( *virtualListener) () (tpt.CapableConn, error) {
	return .acceptRunnner.Accept(.listener, .version, .acceptChan)
}

type acceptVal struct {
	conn tpt.CapableConn
	err  error
}

type acceptLoopRunner struct {
	acceptSem chan struct{}

	muxerMu     sync.Mutex
	muxer       map[quic.Version]chan acceptVal
	muxerClosed bool
}

func ( *acceptLoopRunner) ( quic.Version) chan acceptVal {
	.muxerMu.Lock()
	defer .muxerMu.Unlock()

	 := make(chan acceptVal, acceptBufferPerVersion)

	if ,  := .muxer[];  {
		panic("unexpected chan already found in accept muxer")
	}

	.muxer[] = 
	return 
}

func ( *acceptLoopRunner) ( quic.Version,  error) {
	.muxerMu.Lock()
	defer .muxerMu.Unlock()

	if .muxerClosed {
		// Already closed, all versions are removed
		return
	}

	,  := .muxer[]
	if ! {
		panic("expected chan in accept muxer")
	}
	 <- acceptVal{err: }
	delete(.muxer, )
}

func ( *acceptLoopRunner) ( error) {
	.muxerMu.Lock()
	defer .muxerMu.Unlock()
	.muxerClosed = true
	for ,  := range .muxer {
		select {
		case  <- acceptVal{err: }:
		default:
		}
		delete(.muxer, )
		close()
	}
}

// innerAccept is the inner logic of the Accept loop. Assume caller holds the
// acceptSemaphore. May return both a nil conn and nil error if it didn't find a
// conn with the expected version
func ( *acceptLoopRunner) ( *listener,  quic.Version,  chan acceptVal) (tpt.CapableConn, error) {
	select {
	// Check if we have a buffered connection first from an earlier Accept call
	case ,  := <-:
		if ! {
			return nil, tpt.ErrListenerClosed
		}
		return .conn, .err
	default:
	}

	,  := .Accept()

	if  != nil {
		.sendErrAndClose()
		return nil, 
	}

	, ,  := quicreuse.FromQuicMultiaddr(.RemoteMultiaddr())
	if  != nil {
		.sendErrAndClose()
		return nil, 
	}

	if  ==  {
		return , nil
	}

	// This wasn't the version we were expecting, lets queue it up for a
	// future Accept call with a different version
	.muxerMu.Lock()
	,  := .muxer[]
	.muxerMu.Unlock()

	if ! {
		// Nothing to handle this connection version. Close it
		.Close()
		return nil, nil
	}

	// Non blocking
	select {
	case  <- acceptVal{conn: }:
	default:
		.CloseWithError(network.ConnRateLimited)
		// accept queue filled up, drop the connection
		log.Warn("Accept queue filled. Dropping connection.")
	}

	return nil, nil
}

func ( *acceptLoopRunner) ( *listener,  quic.Version,  chan acceptVal) (tpt.CapableConn, error) {
	for {
		var  tpt.CapableConn
		var  error
		select {
		case .acceptSem <- struct{}{}:
			,  = .innerAccept(, , )
			<-.acceptSem

			if  == nil &&  == nil {
				// Didn't find a conn for the expected version and there was no error, lets try again
				continue
			}
		case ,  := <-:
			if ! {
				return nil, tpt.ErrListenerClosed
			}
			 = .conn
			 = .err
		}
		return , 
	}
}