package quic

import (
	
	
	

	
	
	
	
)

type connRunnerCallbacks struct {
	AddConnectionID    func(protocol.ConnectionID)
	RemoveConnectionID func(protocol.ConnectionID)
	ReplaceWithClosed  func([]protocol.ConnectionID, []byte, time.Duration)
}

// The memory address of the Transport is used as the key.
type connRunners map[connRunner]connRunnerCallbacks

func ( connRunners) ( protocol.ConnectionID) {
	for ,  := range  {
		.AddConnectionID()
	}
}

func ( connRunners) ( protocol.ConnectionID) {
	for ,  := range  {
		.RemoveConnectionID()
	}
}

func ( connRunners) ( []protocol.ConnectionID,  []byte,  time.Duration) {
	for ,  := range  {
		.ReplaceWithClosed(, , )
	}
}

type connIDToRetire struct {
	t      monotime.Time
	connID protocol.ConnectionID
}

type connIDGenerator struct {
	generator   ConnectionIDGenerator
	highestSeq  uint64
	connRunners connRunners

	activeSrcConnIDs        map[uint64]protocol.ConnectionID
	connIDsToRetire         []connIDToRetire       // sorted by t
	initialClientDestConnID *protocol.ConnectionID // nil for the client

	statelessResetter *statelessResetter

	queueControlFrame func(wire.Frame)
}

func newConnIDGenerator(
	 connRunner,
	 protocol.ConnectionID,
	 *protocol.ConnectionID, // nil for the client
	 *statelessResetter,
	 connRunnerCallbacks,
	 func(wire.Frame),
	 ConnectionIDGenerator,
) *connIDGenerator {
	 := &connIDGenerator{
		generator:         ,
		activeSrcConnIDs:  make(map[uint64]protocol.ConnectionID),
		statelessResetter: ,
		connRunners:       map[connRunner]connRunnerCallbacks{: },
		queueControlFrame: ,
	}
	.activeSrcConnIDs[0] = 
	.initialClientDestConnID = 
	return 
}

func ( *connIDGenerator) ( uint64) error {
	if .generator.ConnectionIDLen() == 0 {
		return nil
	}
	// The active_connection_id_limit transport parameter is the number of
	// connection IDs the peer will store. This limit includes the connection ID
	// used during the handshake, and the one sent in the preferred_address
	// transport parameter.
	// We currently don't send the preferred_address transport parameter,
	// so we can issue (limit - 1) connection IDs.
	for  := uint64(len(.activeSrcConnIDs));  < min(, protocol.MaxIssuedConnectionIDs); ++ {
		if  := .issueNewConnID();  != nil {
			return 
		}
	}
	return nil
}

func ( *connIDGenerator) ( uint64,  protocol.ConnectionID,  monotime.Time) error {
	if  > .highestSeq {
		return &qerr.TransportError{
			ErrorCode:    qerr.ProtocolViolation,
			ErrorMessage: fmt.Sprintf("retired connection ID %d (highest issued: %d)", , .highestSeq),
		}
	}
	,  := .activeSrcConnIDs[]
	// We might already have deleted this connection ID, if this is a duplicate frame.
	if ! {
		return nil
	}
	if  ==  {
		return &qerr.TransportError{
			ErrorCode:    qerr.ProtocolViolation,
			ErrorMessage: fmt.Sprintf("retired connection ID %d (%s), which was used as the Destination Connection ID on this packet", , ),
		}
	}
	.queueConnIDForRetiring(, )

	delete(.activeSrcConnIDs, )
	// Don't issue a replacement for the initial connection ID.
	if  == 0 {
		return nil
	}
	return .issueNewConnID()
}

func ( *connIDGenerator) ( protocol.ConnectionID,  monotime.Time) {
	 := slices.IndexFunc(.connIDsToRetire, func( connIDToRetire) bool {
		return .t.After()
	})
	if  == -1 {
		 = len(.connIDsToRetire)
	}
	.connIDsToRetire = slices.Insert(.connIDsToRetire, , connIDToRetire{t: , connID: })
}

func ( *connIDGenerator) () error {
	,  := .generator.GenerateConnectionID()
	if  != nil {
		return 
	}
	.activeSrcConnIDs[.highestSeq+1] = 
	.connRunners.AddConnectionID()
	.queueControlFrame(&wire.NewConnectionIDFrame{
		SequenceNumber:      .highestSeq + 1,
		ConnectionID:        ,
		StatelessResetToken: .statelessResetter.GetStatelessResetToken(),
	})
	.highestSeq++
	return nil
}

func ( *connIDGenerator) ( monotime.Time) {
	if .initialClientDestConnID != nil {
		.queueConnIDForRetiring(*.initialClientDestConnID, )
		.initialClientDestConnID = nil
	}
}

func ( *connIDGenerator) ( monotime.Time) {
	if len(.connIDsToRetire) == 0 {
		return
	}
	for ,  := range .connIDsToRetire {
		if .t.After() {
			break
		}
		.connRunners.RemoveConnectionID(.connID)
		.connIDsToRetire = .connIDsToRetire[1:]
	}
}

func ( *connIDGenerator) () {
	if .initialClientDestConnID != nil {
		.connRunners.RemoveConnectionID(*.initialClientDestConnID)
	}
	for ,  := range .activeSrcConnIDs {
		.connRunners.RemoveConnectionID()
	}
	for ,  := range .connIDsToRetire {
		.connRunners.RemoveConnectionID(.connID)
	}
}

func ( *connIDGenerator) ( []byte,  time.Duration) {
	 := make([]protocol.ConnectionID, 0, len(.activeSrcConnIDs)+len(.connIDsToRetire)+1)
	if .initialClientDestConnID != nil {
		 = append(, *.initialClientDestConnID)
	}
	for ,  := range .activeSrcConnIDs {
		 = append(, )
	}
	for ,  := range .connIDsToRetire {
		 = append(, .connID)
	}
	.connRunners.ReplaceWithClosed(, , )
}

func ( *connIDGenerator) ( connRunner,  connRunnerCallbacks) {
	// The transport might have already been added earlier.
	// This happens if the application migrates back to and old path.
	if ,  := .connRunners[];  {
		return
	}
	.connRunners[] = 
	if .initialClientDestConnID != nil {
		.AddConnectionID(*.initialClientDestConnID)
	}
	for ,  := range .activeSrcConnIDs {
		.AddConnectionID()
	}
}