package flowcontrol

import (
	

	
	
	
	
)

type streamFlowController struct {
	baseFlowController

	streamID protocol.StreamID

	connection connectionFlowControllerI

	receivedFinalOffset bool
}

var _ StreamFlowController = &streamFlowController{}

// NewStreamFlowController gets a new flow controller for a stream
func (
	 protocol.StreamID,
	 ConnectionFlowController,
	 protocol.ByteCount,
	 protocol.ByteCount,
	 protocol.ByteCount,
	 *utils.RTTStats,
	 utils.Logger,
) StreamFlowController {
	return &streamFlowController{
		streamID:   ,
		connection: .(connectionFlowControllerI),
		baseFlowController: baseFlowController{
			rttStats:             ,
			receiveWindow:        ,
			receiveWindowSize:    ,
			maxReceiveWindowSize: ,
			sendWindow:           ,
			logger:               ,
		},
	}
}

// UpdateHighestReceived updates the highestReceived value, if the offset is higher.
func ( *streamFlowController) ( protocol.ByteCount,  bool,  monotime.Time) error {
	// If the final offset for this stream is already known, check for consistency.
	if .receivedFinalOffset {
		// If we receive another final offset, check that it's the same.
		if  &&  != .highestReceived {
			return &qerr.TransportError{
				ErrorCode:    qerr.FinalSizeError,
				ErrorMessage: fmt.Sprintf("received inconsistent final offset for stream %d (old: %d, new: %d bytes)", .streamID, .highestReceived, ),
			}
		}
		// Check that the offset is below the final offset.
		if  > .highestReceived {
			return &qerr.TransportError{
				ErrorCode:    qerr.FinalSizeError,
				ErrorMessage: fmt.Sprintf("received offset %d for stream %d, but final offset was already received at %d", , .streamID, .highestReceived),
			}
		}
	}

	if  {
		.receivedFinalOffset = true
	}
	if  == .highestReceived {
		return nil
	}
	// A higher offset was received before. This can happen due to reordering.
	if  < .highestReceived {
		if  {
			return &qerr.TransportError{
				ErrorCode:    qerr.FinalSizeError,
				ErrorMessage: fmt.Sprintf("received final offset %d for stream %d, but already received offset %d before", , .streamID, .highestReceived),
			}
		}
		return nil
	}

	// If this is the first frame received for this stream, start flow-control auto-tuning.
	if .highestReceived == 0 {
		.startNewAutoTuningEpoch()
	}
	 :=  - .highestReceived
	.highestReceived = 

	if .checkFlowControlViolation() {
		return &qerr.TransportError{
			ErrorCode:    qerr.FlowControlError,
			ErrorMessage: fmt.Sprintf("received %d bytes on stream %d, allowed %d bytes", , .streamID, .receiveWindow),
		}
	}
	return .connection.IncrementHighestReceived(, )
}

func ( *streamFlowController) ( protocol.ByteCount) (,  bool) {
	.mutex.Lock()
	.addBytesRead()
	 = .shouldQueueWindowUpdate()
	.mutex.Unlock()
	 = .connection.AddBytesRead()
	return
}

func ( *streamFlowController) () {
	.mutex.Lock()
	 := .highestReceived - .bytesRead
	.bytesRead = .highestReceived
	.mutex.Unlock()
	if  > 0 {
		.connection.AddBytesRead()
	}
}

func ( *streamFlowController) ( protocol.ByteCount) {
	.baseFlowController.AddBytesSent()
	.connection.AddBytesSent()
}

func ( *streamFlowController) () protocol.ByteCount {
	return min(.baseFlowController.SendWindowSize(), .connection.SendWindowSize())
}

func ( *streamFlowController) () bool {
	,  := .baseFlowController.IsNewlyBlocked()
	return 
}

func ( *streamFlowController) () bool {
	return !.receivedFinalOffset && .hasWindowUpdate()
}

func ( *streamFlowController) ( monotime.Time) protocol.ByteCount {
	// If we already received the final offset for this stream, the peer won't need any additional flow control credit.
	if .receivedFinalOffset {
		return 0
	}

	.mutex.Lock()
	defer .mutex.Unlock()

	 := .receiveWindowSize
	 := .getWindowUpdate()
	if .receiveWindowSize >  { // auto-tuning enlarged the window size
		.logger.Debugf("Increasing receive flow control window for stream %d to %d", .streamID, .receiveWindowSize)
		.connection.EnsureMinimumWindowSize(protocol.ByteCount(float64(.receiveWindowSize)*protocol.ConnectionFlowControlMultiplier), )
	}
	return 
}