package quic

import (
	
	

	
	
	
	
	
	
	
)

const (
	maxPathResponses = 256
	maxControlFrames = 16 << 10
)

// This is the largest possible size of a stream-related control frame
// (which is the RESET_STREAM frame).
const maxStreamControlFrameSize = 25

type streamFrameGetter interface {
	popStreamFrame(protocol.ByteCount, protocol.Version) (ackhandler.StreamFrame, *wire.StreamDataBlockedFrame, bool)
}

type streamControlFrameGetter interface {
	getControlFrame(monotime.Time) (_ ackhandler.Frame, ok, hasMore bool)
}

type framer struct {
	mutex sync.Mutex

	activeStreams            map[protocol.StreamID]streamFrameGetter
	streamQueue              ringbuffer.RingBuffer[protocol.StreamID]
	streamsWithControlFrames map[protocol.StreamID]streamControlFrameGetter

	controlFrameMutex          sync.Mutex
	controlFrames              []wire.Frame
	pathResponses              []*wire.PathResponseFrame
	connFlowController         flowcontrol.ConnectionFlowController
	queuedTooManyControlFrames bool
}

func newFramer( flowcontrol.ConnectionFlowController) *framer {
	return &framer{
		activeStreams:            make(map[protocol.StreamID]streamFrameGetter),
		streamsWithControlFrames: make(map[protocol.StreamID]streamControlFrameGetter),
		connFlowController:       ,
	}
}

func ( *framer) () bool {
	.mutex.Lock()
	 := !.streamQueue.Empty()
	.mutex.Unlock()
	if  {
		return true
	}
	.controlFrameMutex.Lock()
	defer .controlFrameMutex.Unlock()
	return len(.streamsWithControlFrames) > 0 || len(.controlFrames) > 0 || len(.pathResponses) > 0
}

func ( *framer) ( wire.Frame) {
	.controlFrameMutex.Lock()
	defer .controlFrameMutex.Unlock()

	if ,  := .(*wire.PathResponseFrame);  {
		// Only queue up to maxPathResponses PATH_RESPONSE frames.
		// This limit should be high enough to never be hit in practice,
		// unless the peer is doing something malicious.
		if len(.pathResponses) >= maxPathResponses {
			return
		}
		.pathResponses = append(.pathResponses, )
		return
	}
	// This is a hack.
	if len(.controlFrames) >= maxControlFrames {
		.queuedTooManyControlFrames = true
		return
	}
	.controlFrames = append(.controlFrames, )
}

func ( *framer) (
	 []ackhandler.Frame,
	 []ackhandler.StreamFrame,
	 protocol.ByteCount,
	 monotime.Time,
	 protocol.Version,
) ([]ackhandler.Frame, []ackhandler.StreamFrame, protocol.ByteCount) {
	.controlFrameMutex.Lock()
	,  := .appendControlFrames(, , , )
	 -= 

	var  ackhandler.StreamFrame
	var  protocol.ByteCount
	.mutex.Lock()
	// pop STREAM frames, until less than 128 bytes are left in the packet
	 := .streamQueue.Len()
	for  := 0;  < ; ++ {
		if protocol.MinStreamFrameSize >  {
			break
		}
		,  := .getNextStreamFrame(, )
		if .Frame != nil {
			 = append(, )
			 -= .Frame.Length()
			 = 
			 += .Frame.Length()
		}
		// If the stream just became blocked on stream flow control, attempt to pack the
		// STREAM_DATA_BLOCKED into the same packet.
		if  != nil {
			 := .Length()
			// In case it doesn't fit, queue it for the next packet.
			if  <  {
				.controlFrames = append(.controlFrames, )
				break
			}
			 = append(, ackhandler.Frame{Frame: })
			 -= 
			 += 
		}
	}

	// The only way to become blocked on connection-level flow control is by sending STREAM frames.
	if ,  := .connFlowController.IsNewlyBlocked();  {
		 := &wire.DataBlockedFrame{MaximumData: }
		 := .Length()
		// In case it doesn't fit, queue it for the next packet.
		if  >=  {
			 = append(, ackhandler.Frame{Frame: })
			 += 
		} else {
			.controlFrames = append(.controlFrames, )
		}
	}

	.mutex.Unlock()
	.controlFrameMutex.Unlock()

	if .Frame != nil {
		// account for the smaller size of the last STREAM frame
		 -= .Frame.Length()
		.Frame.DataLenPresent = false
		 += .Frame.Length()
	}

	return , ,  + 
}

func ( *framer) (
	 []ackhandler.Frame,
	 protocol.ByteCount,
	 monotime.Time,
	 protocol.Version,
) ([]ackhandler.Frame, protocol.ByteCount) {
	var  protocol.ByteCount
	// add a PATH_RESPONSE first, but only pack a single PATH_RESPONSE per packet
	if len(.pathResponses) > 0 {
		 := .pathResponses[0]
		 := .Length()
		if  <=  {
			 = append(, ackhandler.Frame{Frame: })
			 += 
			.pathResponses = .pathResponses[1:]
		}
	}

	// add stream-related control frames
	for ,  := range .streamsWithControlFrames {
	:
		 :=  - 
		if  <= maxStreamControlFrameSize {
			break
		}
		, ,  := .getControlFrame()
		if ! {
			delete(.streamsWithControlFrames, )
		}
		if ! {
			continue
		}
		 = append(, )
		 += .Frame.Length()
		if  {
			// It is rare that a stream has more than one control frame to queue.
			// We don't want to spawn another loop for just to cover that case.
			goto 
		}
	}

	for len(.controlFrames) > 0 {
		 := .controlFrames[len(.controlFrames)-1]
		 := .Length()
		if + >  {
			break
		}
		 = append(, ackhandler.Frame{Frame: })
		 += 
		.controlFrames = .controlFrames[:len(.controlFrames)-1]
	}

	return , 
}

// QueuedTooManyControlFrames says if the control frame queue exceeded its maximum queue length.
// This is a hack.
// It is easier to implement than propagating an error return value in QueueControlFrame.
// The correct solution would be to queue frames with their respective structs.
// See https://github.com/quic-go/quic-go/issues/4271 for the queueing of stream-related control frames.
func ( *framer) () bool {
	return .queuedTooManyControlFrames
}

func ( *framer) ( protocol.StreamID,  streamFrameGetter) {
	.mutex.Lock()
	if ,  := .activeStreams[]; ! {
		.streamQueue.PushBack()
		.activeStreams[] = 
	}
	.mutex.Unlock()
}

func ( *framer) ( protocol.StreamID,  streamControlFrameGetter) {
	.controlFrameMutex.Lock()
	if ,  := .streamsWithControlFrames[]; ! {
		.streamsWithControlFrames[] = 
	}
	.controlFrameMutex.Unlock()
}

// RemoveActiveStream is called when a stream completes.
func ( *framer) ( protocol.StreamID) {
	.mutex.Lock()
	delete(.activeStreams, )
	// We don't delete the stream from the streamQueue,
	// since we'd have to iterate over the ringbuffer.
	// Instead, we check if the stream is still in activeStreams when appending STREAM frames.
	.mutex.Unlock()
}

func ( *framer) ( protocol.ByteCount,  protocol.Version) (ackhandler.StreamFrame, *wire.StreamDataBlockedFrame) {
	 := .streamQueue.PopFront()
	// This should never return an error. Better check it anyway.
	// The stream will only be in the streamQueue, if it enqueued itself there.
	,  := .activeStreams[]
	// The stream might have been removed after being enqueued.
	if ! {
		return ackhandler.StreamFrame{}, nil
	}
	// For the last STREAM frame, we'll remove the DataLen field later.
	// Therefore, we can pretend to have more bytes available when popping
	// the STREAM frame (which will always have the DataLen set).
	 += protocol.ByteCount(quicvarint.Len(uint64()))
	, ,  := .popStreamFrame(, )
	if  { // put the stream back in the queue (at the end)
		.streamQueue.PushBack()
	} else { // no more data to send. Stream is not active
		delete(.activeStreams, )
	}
	// Note that the frame.Frame can be nil:
	// * if the stream was canceled after it said it had data
	// * the remaining size doesn't allow us to add another STREAM frame
	return , 
}

func ( *framer) () {
	.mutex.Lock()
	defer .mutex.Unlock()
	.controlFrameMutex.Lock()
	defer .controlFrameMutex.Unlock()

	.streamQueue.Clear()
	for  := range .activeStreams {
		delete(.activeStreams, )
	}
	var  int
	for ,  := range .controlFrames {
		switch .(type) {
		case *wire.MaxDataFrame, *wire.MaxStreamDataFrame, *wire.MaxStreamsFrame,
			*wire.DataBlockedFrame, *wire.StreamDataBlockedFrame, *wire.StreamsBlockedFrame:
			continue
		default:
			.controlFrames[] = .controlFrames[]
			++
		}
	}
	.controlFrames = slices.Delete(.controlFrames, , len(.controlFrames))
}