package quic

import (
	
	
	
	

	
	
	
)

type outgoingStream interface {
	updateSendWindow(protocol.ByteCount)
	enableResetStreamAt()
	closeForShutdown(error)
}

type outgoingStreamsMap[ outgoingStream] struct {
	mutex sync.RWMutex

	streamType protocol.StreamType
	streams    map[protocol.StreamID]

	openQueue []chan struct{}

	nextStream  protocol.StreamID // stream ID of the stream returned by OpenStream(Sync)
	maxStream   protocol.StreamID // the maximum stream ID we're allowed to open
	blockedSent bool              // was a STREAMS_BLOCKED sent for the current maxStream

	newStream            func(protocol.StreamID) 
	queueStreamIDBlocked func(*wire.StreamsBlockedFrame)

	closeErr error
}

func newOutgoingStreamsMap[ outgoingStream](
	 protocol.StreamType,
	 func(protocol.StreamID) ,
	 func(wire.Frame),
	 protocol.Perspective,
) *outgoingStreamsMap[] {
	var  protocol.StreamID
	switch {
	case  == protocol.StreamTypeBidi &&  == protocol.PerspectiveServer:
		 = protocol.FirstOutgoingBidiStreamServer
	case  == protocol.StreamTypeBidi &&  == protocol.PerspectiveClient:
		 = protocol.FirstOutgoingBidiStreamClient
	case  == protocol.StreamTypeUni &&  == protocol.PerspectiveServer:
		 = protocol.FirstOutgoingUniStreamServer
	case  == protocol.StreamTypeUni &&  == protocol.PerspectiveClient:
		 = protocol.FirstOutgoingUniStreamClient
	}
	return &outgoingStreamsMap[]{
		streamType:           ,
		streams:              make(map[protocol.StreamID]),
		maxStream:            protocol.InvalidStreamNum,
		nextStream:           ,
		newStream:            ,
		queueStreamIDBlocked: func( *wire.StreamsBlockedFrame) { () },
	}
}

func ( *outgoingStreamsMap[]) () (, error) {
	.mutex.Lock()
	defer .mutex.Unlock()

	if .closeErr != nil {
		return *new(), .closeErr
	}

	// if there are OpenStreamSync calls waiting, return an error here
	if len(.openQueue) > 0 || .nextStream > .maxStream {
		.maybeSendBlockedFrame()
		return *new(), &StreamLimitReachedError{}
	}
	return .openStream(), nil
}

func ( *outgoingStreamsMap[]) ( context.Context) (, error) {
	.mutex.Lock()
	defer .mutex.Unlock()

	if .closeErr != nil {
		return *new(), .closeErr
	}
	if  := .Err();  != nil {
		return *new(), 
	}
	if len(.openQueue) == 0 && .nextStream <= .maxStream {
		return .openStream(), nil
	}

	 := make(chan struct{}, 1)
	.openQueue = append(.openQueue, )
	.maybeSendBlockedFrame()

	for {
		.mutex.Unlock()
		select {
		case <-.Done():
			.mutex.Lock()
			.openQueue = slices.DeleteFunc(.openQueue, func( chan struct{}) bool {
				return  == 
			})
			// If we just received a MAX_STREAMS frame, this might have been the next stream
			// that could be opened. Make sure we unblock the next OpenStreamSync call.
			.maybeUnblockOpenSync()
			return *new(), .Err()
		case <-:
		}

		.mutex.Lock()
		if .closeErr != nil {
			return *new(), .closeErr
		}
		if .nextStream > .maxStream {
			// no stream available. Continue waiting
			continue
		}
		 := .openStream()
		.openQueue = .openQueue[1:]
		.maybeUnblockOpenSync()
		return , nil
	}
}

func ( *outgoingStreamsMap[]) ()  {
	 := .newStream(.nextStream)
	.streams[.nextStream] = 
	.nextStream += 4
	return 
}

// maybeSendBlockedFrame queues a STREAMS_BLOCKED frame for the current stream offset,
// if we haven't sent one for this offset yet
func ( *outgoingStreamsMap[]) () {
	if .blockedSent {
		return
	}

	var  protocol.StreamNum
	if .maxStream != protocol.InvalidStreamID {
		 = .maxStream.StreamNum()
	}
	.queueStreamIDBlocked(&wire.StreamsBlockedFrame{
		Type:        .streamType,
		StreamLimit: ,
	})
	.blockedSent = true
}

func ( *outgoingStreamsMap[]) ( protocol.StreamID) (, error) {
	.mutex.RLock()
	if  >= .nextStream {
		.mutex.RUnlock()
		return *new(), &qerr.TransportError{
			ErrorCode:    qerr.StreamStateError,
			ErrorMessage: fmt.Sprintf("peer attempted to open stream %d", ),
		}
	}
	 := .streams[]
	.mutex.RUnlock()
	return , nil
}

func ( *outgoingStreamsMap[]) ( protocol.StreamID) error {
	.mutex.Lock()
	defer .mutex.Unlock()

	if ,  := .streams[]; ! {
		return &qerr.TransportError{
			ErrorCode:    qerr.StreamStateError,
			ErrorMessage: fmt.Sprintf("tried to delete unknown outgoing stream %d", ),
		}
	}
	delete(.streams, )
	return nil
}

func ( *outgoingStreamsMap[]) ( protocol.StreamID) {
	.mutex.Lock()
	defer .mutex.Unlock()

	if  <= .maxStream {
		return
	}
	.maxStream = 
	.blockedSent = false
	if .maxStream < .nextStream-4+4*protocol.StreamID(len(.openQueue)) {
		.maybeSendBlockedFrame()
	}
	.maybeUnblockOpenSync()
}

// UpdateSendWindow is called when the peer's transport parameters are received.
// Only in the case of a 0-RTT handshake will we have open streams at this point.
// We might need to update the send window, in case the server increased it.
func ( *outgoingStreamsMap[]) ( protocol.ByteCount) {
	.mutex.Lock()
	for ,  := range .streams {
		.updateSendWindow()
	}
	.mutex.Unlock()
}

func ( *outgoingStreamsMap[]) () {
	.mutex.Lock()
	for ,  := range .streams {
		.enableResetStreamAt()
	}
	.mutex.Unlock()
}

// unblockOpenSync unblocks the next OpenStreamSync go-routine to open a new stream
func ( *outgoingStreamsMap[]) () {
	if len(.openQueue) == 0 {
		return
	}
	if .nextStream > .maxStream {
		return
	}
	// unblockOpenSync is called both from OpenStreamSync and from SetMaxStream.
	// It's sufficient to only unblock OpenStreamSync once.
	select {
	case .openQueue[0] <- struct{}{}:
	default:
	}
}

func ( *outgoingStreamsMap[]) ( error) {
	.mutex.Lock()
	defer .mutex.Unlock()

	.closeErr = 
	for ,  := range .streams {
		.closeForShutdown()
	}
	for ,  := range .openQueue {
		if  != nil {
			close()
		}
	}
	.openQueue = nil
}