package quic

import (
	
	

	
	
	
	
)

type newConnID struct {
	SequenceNumber      uint64
	ConnectionID        protocol.ConnectionID
	StatelessResetToken protocol.StatelessResetToken
}

type connIDManager struct {
	queue []newConnID

	highestProbingID uint64
	pathProbing      map[pathID]newConnID // initialized lazily

	handshakeComplete         bool
	activeSequenceNumber      uint64
	highestRetired            uint64
	activeConnectionID        protocol.ConnectionID
	activeStatelessResetToken *protocol.StatelessResetToken

	// We change the connection ID after sending on average
	// protocol.PacketsPerConnectionID packets. The actual value is randomized
	// hide the packet loss rate from on-path observers.
	rand                   utils.Rand
	packetsSinceLastChange uint32
	packetsPerConnectionID uint32

	addStatelessResetToken    func(protocol.StatelessResetToken)
	removeStatelessResetToken func(protocol.StatelessResetToken)
	queueControlFrame         func(wire.Frame)

	closed bool
}

func newConnIDManager(
	 protocol.ConnectionID,
	 func(protocol.StatelessResetToken),
	 func(protocol.StatelessResetToken),
	 func(wire.Frame),
) *connIDManager {
	return &connIDManager{
		activeConnectionID:        ,
		addStatelessResetToken:    ,
		removeStatelessResetToken: ,
		queueControlFrame:         ,
		queue:                     make([]newConnID, 0, protocol.MaxActiveConnectionIDs),
	}
}

func ( *connIDManager) ( protocol.ConnectionID,  protocol.StatelessResetToken) error {
	return .addConnectionID(1, , )
}

func ( *connIDManager) ( *wire.NewConnectionIDFrame) error {
	if  := .add();  != nil {
		return 
	}
	if len(.queue) >= protocol.MaxActiveConnectionIDs {
		return &qerr.TransportError{ErrorCode: qerr.ConnectionIDLimitError}
	}
	return nil
}

func ( *connIDManager) ( *wire.NewConnectionIDFrame) error {
	if .activeConnectionID.Len() == 0 {
		return &qerr.TransportError{
			ErrorCode:    qerr.ProtocolViolation,
			ErrorMessage: "received NEW_CONNECTION_ID frame but zero-length connection IDs are in use",
		}
	}
	// If the NEW_CONNECTION_ID frame is reordered, such that its sequence number is smaller than the currently active
	// connection ID or if it was already retired, send the RETIRE_CONNECTION_ID frame immediately.
	if .SequenceNumber < max(.activeSequenceNumber, .highestProbingID) || .SequenceNumber < .highestRetired {
		.queueControlFrame(&wire.RetireConnectionIDFrame{
			SequenceNumber: .SequenceNumber,
		})
		return nil
	}

	if .RetirePriorTo != 0 && .pathProbing != nil {
		for ,  := range .pathProbing {
			if .SequenceNumber < .RetirePriorTo {
				.queueControlFrame(&wire.RetireConnectionIDFrame{
					SequenceNumber: .SequenceNumber,
				})
				.removeStatelessResetToken(.StatelessResetToken)
				delete(.pathProbing, )
			}
		}
	}
	// Retire elements in the queue.
	// Doesn't retire the active connection ID.
	if .RetirePriorTo > .highestRetired {
		var  []newConnID
		for ,  := range .queue {
			if .SequenceNumber >= .RetirePriorTo {
				 = append(, )
			} else {
				.queueControlFrame(&wire.RetireConnectionIDFrame{SequenceNumber: .SequenceNumber})
			}
		}
		.queue = 
		.highestRetired = .RetirePriorTo
	}

	if .SequenceNumber == .activeSequenceNumber {
		return nil
	}

	if  := .addConnectionID(.SequenceNumber, .ConnectionID, .StatelessResetToken);  != nil {
		return 
	}

	// Retire the active connection ID, if necessary.
	if .activeSequenceNumber < .RetirePriorTo {
		// The queue is guaranteed to have at least one element at this point.
		.updateConnectionID()
	}
	return nil
}

func ( *connIDManager) ( uint64,  protocol.ConnectionID,  protocol.StatelessResetToken) error {
	// fast path: add to the end of the queue
	if len(.queue) == 0 || .queue[len(.queue)-1].SequenceNumber <  {
		.queue = append(.queue, newConnID{
			SequenceNumber:      ,
			ConnectionID:        ,
			StatelessResetToken: ,
		})
		return nil
	}

	// slow path: insert in the middle
	for ,  := range .queue {
		if .SequenceNumber ==  {
			if .ConnectionID !=  {
				return fmt.Errorf("received conflicting connection IDs for sequence number %d", )
			}
			if .StatelessResetToken !=  {
				return fmt.Errorf("received conflicting stateless reset tokens for sequence number %d", )
			}
			return nil
		}

		// insert at the correct position to maintain sorted order
		if .SequenceNumber >  {
			.queue = slices.Insert(.queue, , newConnID{
				SequenceNumber:      ,
				ConnectionID:        ,
				StatelessResetToken: ,
			})
			return nil
		}
	}
	return nil // unreachable
}

func ( *connIDManager) () {
	.assertNotClosed()
	.queueControlFrame(&wire.RetireConnectionIDFrame{
		SequenceNumber: .activeSequenceNumber,
	})
	.highestRetired = max(.highestRetired, .activeSequenceNumber)
	if .activeStatelessResetToken != nil {
		.removeStatelessResetToken(*.activeStatelessResetToken)
	}

	 := .queue[0]
	.queue = .queue[1:]
	.activeSequenceNumber = .SequenceNumber
	.activeConnectionID = .ConnectionID
	.activeStatelessResetToken = &.StatelessResetToken
	.packetsSinceLastChange = 0
	.packetsPerConnectionID = protocol.PacketsPerConnectionID/2 + uint32(.rand.Int31n(protocol.PacketsPerConnectionID))
	.addStatelessResetToken(*.activeStatelessResetToken)
}

func ( *connIDManager) () {
	.closed = true
	if .activeStatelessResetToken != nil {
		.removeStatelessResetToken(*.activeStatelessResetToken)
	}
	if .pathProbing != nil {
		for ,  := range .pathProbing {
			.removeStatelessResetToken(.StatelessResetToken)
		}
	}
}

// is called when the server performs a Retry
// and when the server changes the connection ID in the first Initial sent
func ( *connIDManager) ( protocol.ConnectionID) {
	if .activeSequenceNumber != 0 {
		panic("expected first connection ID to have sequence number 0")
	}
	.activeConnectionID = 
}

// is called when the server provides a stateless reset token in the transport parameters
func ( *connIDManager) ( protocol.StatelessResetToken) {
	.assertNotClosed()
	if .activeSequenceNumber != 0 {
		panic("expected first connection ID to have sequence number 0")
	}
	.activeStatelessResetToken = &
	.addStatelessResetToken()
}

func ( *connIDManager) () {
	.packetsSinceLastChange++
}

func ( *connIDManager) () bool {
	if !.handshakeComplete {
		return false
	}
	// initiate the first change as early as possible (after handshake completion)
	if len(.queue) > 0 && .activeSequenceNumber == 0 {
		return true
	}
	// For later changes, only change if
	// 1. The queue of connection IDs is filled more than 50%.
	// 2. We sent at least PacketsPerConnectionID packets
	return 2*len(.queue) >= protocol.MaxActiveConnectionIDs &&
		.packetsSinceLastChange >= .packetsPerConnectionID
}

func ( *connIDManager) () protocol.ConnectionID {
	.assertNotClosed()
	if .shouldUpdateConnID() {
		.updateConnectionID()
	}
	return .activeConnectionID
}

func ( *connIDManager) () {
	.handshakeComplete = true
}

// GetConnIDForPath retrieves a connection ID for a new path (i.e. not the active one).
// Once a connection ID is allocated for a path, it cannot be used for a different path.
// When called with the same pathID, it will return the same connection ID,
// unless the peer requested that this connection ID be retired.
func ( *connIDManager) ( pathID) (protocol.ConnectionID, bool) {
	.assertNotClosed()
	// if we're using zero-length connection IDs, we don't need to change the connection ID
	if .activeConnectionID.Len() == 0 {
		return protocol.ConnectionID{}, true
	}

	if .pathProbing == nil {
		.pathProbing = make(map[pathID]newConnID)
	}
	,  := .pathProbing[]
	if  {
		return .ConnectionID, true
	}
	if len(.queue) == 0 {
		return protocol.ConnectionID{}, false
	}
	 := .queue[0]
	.queue = .queue[1:]
	.pathProbing[] = 
	.highestProbingID = .SequenceNumber
	.addStatelessResetToken(.StatelessResetToken)
	return .ConnectionID, true
}

func ( *connIDManager) ( pathID) {
	.assertNotClosed()
	// if we're using zero-length connection IDs, we don't need to change the connection ID
	if .activeConnectionID.Len() == 0 {
		return
	}

	,  := .pathProbing[]
	if ! {
		return
	}
	.queueControlFrame(&wire.RetireConnectionIDFrame{
		SequenceNumber: .SequenceNumber,
	})
	.removeStatelessResetToken(.StatelessResetToken)
	delete(.pathProbing, )
}

func ( *connIDManager) ( protocol.StatelessResetToken) bool {
	if .activeStatelessResetToken != nil {
		if *.activeStatelessResetToken ==  {
			return true
		}
	}
	if .pathProbing != nil {
		for ,  := range .pathProbing {
			if .StatelessResetToken ==  {
				return true
			}
		}
	}
	return false
}

// Using the connIDManager after it has been closed can have disastrous effects:
// If the connection ID is rotated, a new entry would be inserted into the packet handler map,
// leading to a memory leak of the connection struct.
// See https://github.com/quic-go/quic-go/pull/4852 for more details.
func ( *connIDManager) () {
	if .closed {
		panic("connection ID manager is closed")
	}
}