package quic

import (
	
	
	

	
	
	
	
	
)

// StreamLimitReachedError is returned from Conn.OpenStream and Conn.OpenUniStream
// when it is not possible to open a new stream because the number of opens streams reached
// the peer's stream limit.
type StreamLimitReachedError struct{}

func ( StreamLimitReachedError) () string { return "too many open streams" }

type streamsMap struct {
	ctx         context.Context // not used for cancellations, but carries the values associated with the connection
	perspective protocol.Perspective

	maxIncomingBidiStreams uint64
	maxIncomingUniStreams  uint64

	sender            streamSender
	queueControlFrame func(wire.Frame)
	newFlowController func(protocol.StreamID) flowcontrol.StreamFlowController

	mutex                 sync.Mutex
	outgoingBidiStreams   *outgoingStreamsMap[*Stream]
	outgoingUniStreams    *outgoingStreamsMap[*SendStream]
	incomingBidiStreams   *incomingStreamsMap[*Stream]
	incomingUniStreams    *incomingStreamsMap[*ReceiveStream]
	reset                 bool
	supportsResetStreamAt bool
}

func newStreamsMap(
	 context.Context,
	 streamSender,
	 func(wire.Frame),
	 func(protocol.StreamID) flowcontrol.StreamFlowController,
	 uint64,
	 uint64,
	 protocol.Perspective,
) *streamsMap {
	 := &streamsMap{
		ctx:                    ,
		perspective:            ,
		queueControlFrame:      ,
		newFlowController:      ,
		maxIncomingBidiStreams: ,
		maxIncomingUniStreams:  ,
		sender:                 ,
	}
	.initMaps()
	return 
}

func ( *streamsMap) () {
	.outgoingBidiStreams = newOutgoingStreamsMap(
		protocol.StreamTypeBidi,
		func( protocol.StreamID) *Stream {
			return newStream(.ctx, , .sender, .newFlowController(), .supportsResetStreamAt)
		},
		.queueControlFrame,
		.perspective,
	)
	.incomingBidiStreams = newIncomingStreamsMap(
		protocol.StreamTypeBidi,
		func( protocol.StreamID) *Stream {
			return newStream(.ctx, , .sender, .newFlowController(), .supportsResetStreamAt)
		},
		.maxIncomingBidiStreams,
		.queueControlFrame,
		.perspective,
	)
	.outgoingUniStreams = newOutgoingStreamsMap(
		protocol.StreamTypeUni,
		func( protocol.StreamID) *SendStream {
			return newSendStream(.ctx, , .sender, .newFlowController(), .supportsResetStreamAt)
		},
		.queueControlFrame,
		.perspective,
	)
	.incomingUniStreams = newIncomingStreamsMap(
		protocol.StreamTypeUni,
		func( protocol.StreamID) *ReceiveStream {
			return newReceiveStream(, .sender, .newFlowController())
		},
		.maxIncomingUniStreams,
		.queueControlFrame,
		.perspective,
	)
}

func ( *streamsMap) () (*Stream, error) {
	.mutex.Lock()
	 := .reset
	 := .outgoingBidiStreams
	.mutex.Unlock()
	if  {
		return nil, Err0RTTRejected
	}
	return .OpenStream()
}

func ( *streamsMap) ( context.Context) (*Stream, error) {
	.mutex.Lock()
	 := .reset
	 := .outgoingBidiStreams
	.mutex.Unlock()
	if  {
		return nil, Err0RTTRejected
	}
	return .OpenStreamSync()
}

func ( *streamsMap) () (*SendStream, error) {
	.mutex.Lock()
	 := .reset
	 := .outgoingUniStreams
	.mutex.Unlock()
	if  {
		return nil, Err0RTTRejected
	}
	return .OpenStream()
}

func ( *streamsMap) ( context.Context) (*SendStream, error) {
	.mutex.Lock()
	 := .reset
	 := .outgoingUniStreams
	.mutex.Unlock()
	if  {
		return nil, Err0RTTRejected
	}
	return .OpenStreamSync()
}

func ( *streamsMap) ( context.Context) (*Stream, error) {
	.mutex.Lock()
	 := .reset
	 := .incomingBidiStreams
	.mutex.Unlock()
	if  {
		return nil, Err0RTTRejected
	}
	return .AcceptStream()
}

func ( *streamsMap) ( context.Context) (*ReceiveStream, error) {
	.mutex.Lock()
	 := .reset
	 := .incomingUniStreams
	.mutex.Unlock()
	if  {
		return nil, Err0RTTRejected
	}
	return .AcceptStream()
}

func ( *streamsMap) ( protocol.StreamID) error {
	switch .Type() {
	case protocol.StreamTypeUni:
		if .InitiatedBy() == .perspective {
			return .outgoingUniStreams.DeleteStream()
		}
		return .incomingUniStreams.DeleteStream()
	case protocol.StreamTypeBidi:
		if .InitiatedBy() == .perspective {
			return .outgoingBidiStreams.DeleteStream()
		}
		return .incomingBidiStreams.DeleteStream()
	}
	panic("")
}

func ( *streamsMap) ( *wire.MaxStreamsFrame) {
	switch .Type {
	case protocol.StreamTypeUni:
		.outgoingUniStreams.SetMaxStream(.MaxStreamNum.StreamID(protocol.StreamTypeUni, .perspective))
	case protocol.StreamTypeBidi:
		.outgoingBidiStreams.SetMaxStream(.MaxStreamNum.StreamID(protocol.StreamTypeBidi, .perspective))
	}
}

type sendStreamFrameHandler interface {
	updateSendWindow(protocol.ByteCount)
	handleStopSendingFrame(*wire.StopSendingFrame)
}

func ( *streamsMap) ( protocol.StreamID) (sendStreamFrameHandler, error) {
	switch .Type() {
	case protocol.StreamTypeUni:
		if .InitiatedBy() != .perspective {
			// an outgoing unidirectional stream is a send stream, not a receive stream
			return nil, &qerr.TransportError{
				ErrorCode:    qerr.StreamStateError,
				ErrorMessage: fmt.Sprintf("invalid frame for send stream %d", ),
			}
		}
		,  := .outgoingUniStreams.GetStream()
		if  == nil ||  != nil {
			return nil, 
		}
		return , nil
	case protocol.StreamTypeBidi:
		if .InitiatedBy() == .perspective {
			,  := .outgoingBidiStreams.GetStream()
			if  == nil ||  != nil {
				return nil, 
			}
			return , nil
		}
		,  := .incomingBidiStreams.GetOrOpenStream()
		if  == nil ||  != nil {
			return nil, 
		}
		return , nil
	}
	panic("unreachable")
}

func ( *streamsMap) ( *wire.MaxStreamDataFrame) error {
	,  := .getSendStream(.StreamID)
	if  != nil {
		return 
	}
	if  == nil { // stream already deleted
		return nil
	}
	.updateSendWindow(.MaximumStreamData)
	return nil
}

func ( *streamsMap) ( *wire.StopSendingFrame) error {
	,  := .getSendStream(.StreamID)
	if  != nil {
		return 
	}
	if  == nil { // stream already deleted
		return nil
	}
	.handleStopSendingFrame()
	return nil
}

type receiveStreamFrameHandler interface {
	handleResetStreamFrame(*wire.ResetStreamFrame, monotime.Time) error
	handleStreamFrame(*wire.StreamFrame, monotime.Time) error
}

func ( *streamsMap) ( protocol.StreamID) (receiveStreamFrameHandler, error) {
	switch .Type() {
	case protocol.StreamTypeUni:
		// an outgoing unidirectional stream is a send stream, not a receive stream
		if .InitiatedBy() == .perspective {
			return nil, &qerr.TransportError{
				ErrorCode:    qerr.StreamStateError,
				ErrorMessage: fmt.Sprintf("invalid frame for receive stream %d", ),
			}
		}
		,  := .incomingUniStreams.GetOrOpenStream()
		if  != nil ||  == nil {
			return nil, 
		}
		return , nil
	case protocol.StreamTypeBidi:
		var  *Stream
		var  error
		if .InitiatedBy() == .perspective {
			,  = .outgoingBidiStreams.GetStream()
		} else {
			,  = .incomingBidiStreams.GetOrOpenStream()
		}
		if  == nil ||  != nil {
			return nil, 
		}
		return , nil
	}
	panic("unreachable")
}

func ( *streamsMap) ( *wire.StreamDataBlockedFrame) error {
	if ,  := .getReceiveStream(.StreamID);  != nil {
		return 
	}
	// We don't need to do anything in response to a STREAM_DATA_BLOCKED frame,
	// but we need to make sure that the stream ID is valid.
	return nil // we don't need to do anything in response to a STREAM_DATA_BLOCKED frame
}

func ( *streamsMap) ( *wire.ResetStreamFrame,  monotime.Time) error {
	,  := .getReceiveStream(.StreamID)
	if  != nil {
		return 
	}
	if  == nil { // stream already deleted
		return nil
	}
	return .handleResetStreamFrame(, )
}

func ( *streamsMap) ( *wire.StreamFrame,  monotime.Time) error {
	,  := .getReceiveStream(.StreamID)
	if  != nil {
		return 
	}
	if  == nil { // stream already deleted
		return nil
	}
	return .handleStreamFrame(, )
}

func ( *streamsMap) ( *wire.TransportParameters) {
	.supportsResetStreamAt = .EnableResetStreamAt
	.outgoingBidiStreams.EnableResetStreamAt()
	.outgoingUniStreams.EnableResetStreamAt()
	.outgoingBidiStreams.UpdateSendWindow(.InitialMaxStreamDataBidiRemote)
	.outgoingBidiStreams.SetMaxStream(.MaxBidiStreamNum.StreamID(protocol.StreamTypeBidi, .perspective))
	.outgoingUniStreams.UpdateSendWindow(.InitialMaxStreamDataUni)
	.outgoingUniStreams.SetMaxStream(.MaxUniStreamNum.StreamID(protocol.StreamTypeUni, .perspective))
}

func ( *streamsMap) ( error) {
	.outgoingBidiStreams.CloseWithError()
	.outgoingUniStreams.CloseWithError()
	.incomingBidiStreams.CloseWithError()
	.incomingUniStreams.CloseWithError()
}

// ResetFor0RTT resets is used when 0-RTT is rejected. In that case, the streams maps are
// 1. closed with an Err0RTTRejected, making calls to Open{Uni}Stream{Sync} / Accept{Uni}Stream return that error.
// 2. reset to their initial state, such that we can immediately process new incoming stream data.
// Afterwards, calls to Open{Uni}Stream{Sync} / Accept{Uni}Stream will continue to return the error,
// until UseResetMaps() has been called.
func ( *streamsMap) () {
	.mutex.Lock()
	defer .mutex.Unlock()
	.reset = true
	.CloseWithError(Err0RTTRejected)
	.initMaps()
}

func ( *streamsMap) () {
	.mutex.Lock()
	.reset = false
	.mutex.Unlock()
}