package webtransport

import (
	
	
	

	
	
	
)

// session is the map value in the conns map
type session struct {
	created chan struct{} // is closed once the session map has been initialized
	counter int           // how many streams are waiting for this session to be established
	conn    *Session
}

type sessionManager struct {
	refCount  sync.WaitGroup
	ctx       context.Context
	ctxCancel context.CancelFunc

	timeout time.Duration

	mx    sync.Mutex
	conns map[quic.ConnectionTracingID]map[sessionID]*session
}

func newSessionManager( time.Duration) *sessionManager {
	 := &sessionManager{
		timeout: ,
		conns:   make(map[quic.ConnectionTracingID]map[sessionID]*session),
	}
	.ctx, .ctxCancel = context.WithCancel(context.Background())
	return 
}

// AddStream adds a new bidirectional stream to a WebTransport session.
// If the WebTransport session has not yet been established,
// it starts a new go routine and waits for establishment of the session.
// If that takes longer than timeout, the stream is reset.
func ( *sessionManager) ( quic.ConnectionTracingID,  quic.Stream,  sessionID) {
	,  := .getOrCreateSession(, )
	if  {
		.conn.addIncomingStream()
		return
	}

	.refCount.Add(1)
	go func() {
		defer .refCount.Done()
		.handleStream(, )

		.mx.Lock()
		defer .mx.Unlock()

		.counter--
		// Once no more streams are waiting for this session to be established,
		// and this session is still outstanding, delete it from the map.
		if .counter == 0 && .conn == nil {
			.maybeDelete(, )
		}
	}()
}

func ( *sessionManager) ( quic.ConnectionTracingID,  sessionID) {
	,  := .conns[]
	if ! { // should never happen
		return
	}
	delete(, )
	if len() == 0 {
		delete(.conns, )
	}
}

// AddUniStream adds a new unidirectional stream to a WebTransport session.
// If the WebTransport session has not yet been established,
// it starts a new go routine and waits for establishment of the session.
// If that takes longer than timeout, the stream is reset.
func ( *sessionManager) ( quic.ConnectionTracingID,  quic.ReceiveStream) {
	,  := quicvarint.Read(quicvarint.NewReader())
	if  != nil {
		.CancelRead(1337)
	}
	 := sessionID()

	,  := .getOrCreateSession(, )
	if  {
		.conn.addIncomingUniStream()
		return
	}

	.refCount.Add(1)
	go func() {
		defer .refCount.Done()
		.handleUniStream(, )

		.mx.Lock()
		defer .mx.Unlock()

		.counter--
		// Once no more streams are waiting for this session to be established,
		// and this session is still outstanding, delete it from the map.
		if .counter == 0 && .conn == nil {
			.maybeDelete(, )
		}
	}()
}

func ( *sessionManager) ( quic.ConnectionTracingID,  sessionID) ( *session,  bool) {
	.mx.Lock()
	defer .mx.Unlock()

	,  := .conns[]
	if ! {
		 = make(map[sessionID]*session)
		.conns[] = 
	}

	,  = []
	if  && .conn != nil {
		return , true
	}
	if ! {
		 = &session{created: make(chan struct{})}
		[] = 
	}
	.counter++
	return , false
}

func ( *sessionManager) ( quic.Stream,  *session) {
	 := time.NewTimer(.timeout)
	defer .Stop()

	// When multiple streams are waiting for the same session to be established,
	// the timeout is calculated for every stream separately.
	select {
	case <-.created:
		.conn.addIncomingStream()
	case <-.C:
		.CancelRead(WebTransportBufferedStreamRejectedErrorCode)
		.CancelWrite(WebTransportBufferedStreamRejectedErrorCode)
	case <-.ctx.Done():
	}
}

func ( *sessionManager) ( quic.ReceiveStream,  *session) {
	 := time.NewTimer(.timeout)
	defer .Stop()

	// When multiple streams are waiting for the same session to be established,
	// the timeout is calculated for every stream separately.
	select {
	case <-.created:
		.conn.addIncomingUniStream()
	case <-.C:
		.CancelRead(WebTransportBufferedStreamRejectedErrorCode)
	case <-.ctx.Done():
	}
}

// AddSession adds a new WebTransport session.
func ( *sessionManager) ( http3.Connection,  sessionID,  http3.Stream) *Session {
	 := newSession(, , )
	 := .Context().Value(quic.ConnectionTracingKey).(quic.ConnectionTracingID)

	.mx.Lock()
	defer .mx.Unlock()

	,  := .conns[]
	if ! {
		 = make(map[sessionID]*session)
		.conns[] = 
	}
	if ,  := [];  {
		// We might already have an entry of this session.
		// This can happen when we receive a stream for this WebTransport session before we complete the HTTP request
		// that establishes the session.
		.conn = 
		close(.created)
		return 
	}
	 := make(chan struct{})
	close()
	[] = &session{created: , conn: }
	return 
}

func ( *sessionManager) () {
	.ctxCancel()
	.refCount.Wait()
}