package quic

import (
	
	
	

	
	
	
)

// ConvertFrame converts a wire.Frame into a logging.Frame.
// This makes it possible for external packages to access the frames.
// Furthermore, it removes the data slices from CRYPTO and STREAM frames.
func toQlogFrame( wire.Frame) qlog.Frame {
	switch f := .(type) {
	case *wire.AckFrame:
		// We use a pool for ACK frames.
		// Implementations of the tracer interface may hold on to frames, so we need to make a copy here.
		return qlog.Frame{Frame: toQlogAckFrame()}
	case *wire.CryptoFrame:
		return qlog.Frame{
			Frame: &qlog.CryptoFrame{
				Offset: int64(.Offset),
				Length: int64(len(.Data)),
			},
		}
	case *wire.StreamFrame:
		return qlog.Frame{
			Frame: &qlog.StreamFrame{
				StreamID: .StreamID,
				Offset:   int64(.Offset),
				Length:   int64(.DataLen()),
				Fin:      .Fin,
			},
		}
	case *wire.DatagramFrame:
		return qlog.Frame{
			Frame: &qlog.DatagramFrame{
				Length: int64(len(.Data)),
			},
		}
	default:
		return qlog.Frame{Frame: }
	}
}

func toQlogAckFrame( *wire.AckFrame) *qlog.AckFrame {
	 := &qlog.AckFrame{
		AckRanges: slices.Clone(.AckRanges),
		DelayTime: .DelayTime,
		ECNCE:     .ECNCE,
		ECT0:      .ECT0,
		ECT1:      .ECT1,
	}
	return 
}

func ( *Conn) ( *longHeaderPacket,  protocol.ECN,  qlog.DatagramID) {
	// quic-go logging
	if .logger.Debug() {
		.header.Log(.logger)
		if .ack != nil {
			wire.LogFrame(.logger, .ack, true)
		}
		for ,  := range .frames {
			wire.LogFrame(.logger, .Frame, true)
		}
		for ,  := range .streamFrames {
			wire.LogFrame(.logger, .Frame, true)
		}
	}

	// tracing
	if .qlogger != nil {
		 := len(.frames) + len(.streamFrames)
		if .ack != nil {
			++
		}
		 := make([]qlog.Frame, 0, )
		if .ack != nil {
			 = append(, toQlogFrame(.ack))
		}
		for ,  := range .frames {
			 = append(, toQlogFrame(.Frame))
		}
		for ,  := range .streamFrames {
			 = append(, toQlogFrame(.Frame))
		}
		.qlogger.RecordEvent(qlog.PacketSent{
			Header: qlog.PacketHeader{
				PacketType:       toQlogPacketType(.header.Type),
				KeyPhaseBit:      .header.KeyPhase,
				PacketNumber:     .header.PacketNumber,
				Version:          .header.Version,
				SrcConnectionID:  .header.SrcConnectionID,
				DestConnectionID: .header.DestConnectionID,
			},
			Raw: qlog.RawInfo{
				Length:        int(.length),
				PayloadLength: int(.header.Length),
			},
			DatagramID: ,
			Frames:     ,
			ECN:        toQlogECN(),
		})
	}
}

func ( *Conn) ( shortHeaderPacket,  protocol.ECN,  protocol.ByteCount) {
	.logShortHeaderPacketWithDatagramID(, , , false, 0)
}

func ( *Conn) ( shortHeaderPacket,  protocol.ECN,  protocol.ByteCount,  bool,  qlog.DatagramID) {
	if .logger.Debug() && ! {
		.logger.Debugf("-> Sending packet %d (%d bytes) for connection %s, 1-RTT (ECN: %s)", .PacketNumber, , .logID, )
	}
	// quic-go logging
	if .logger.Debug() {
		wire.LogShortHeader(.logger, .DestConnID, .PacketNumber, .PacketNumberLen, .KeyPhase)
		if .Ack != nil {
			wire.LogFrame(.logger, .Ack, true)
		}
		for ,  := range .Frames {
			wire.LogFrame(.logger, .Frame, true)
		}
		for ,  := range .StreamFrames {
			wire.LogFrame(.logger, .Frame, true)
		}
	}

	// tracing
	if .qlogger != nil {
		 := len(.Frames) + len(.StreamFrames)
		if .Ack != nil {
			++
		}
		 := make([]qlog.Frame, 0, )
		if .Ack != nil {
			 = append(, toQlogFrame(.Ack))
		}
		for ,  := range .Frames {
			 = append(, toQlogFrame(.Frame))
		}
		for ,  := range .StreamFrames {
			 = append(, toQlogFrame(.Frame))
		}
		.qlogger.RecordEvent(qlog.PacketSent{
			Header: qlog.PacketHeader{
				PacketType:       qlog.PacketType1RTT,
				KeyPhaseBit:      .KeyPhase,
				PacketNumber:     .PacketNumber,
				Version:          .version,
				DestConnectionID: .DestConnID,
			},
			Raw: qlog.RawInfo{
				Length:        int(),
				PayloadLength: int( - wire.ShortHeaderLen(.DestConnID, .PacketNumberLen)),
			},
			DatagramID: ,
			Frames:     ,
			ECN:        toQlogECN(),
		})
	}
}

func ( *Conn) ( *coalescedPacket,  protocol.ECN) {
	var  qlog.DatagramID
	if .qlogger != nil {
		 = qlog.CalculateDatagramID(.buffer.Data)
	}
	if .logger.Debug() {
		// There's a short period between dropping both Initial and Handshake keys and completion of the handshake,
		// during which we might call PackCoalescedPacket but just pack a short header packet.
		if len(.longHdrPackets) == 0 && .shortHdrPacket != nil {
			.logShortHeaderPacketWithDatagramID(
				*.shortHdrPacket,
				,
				.shortHdrPacket.Length,
				false,
				,
			)
			return
		}
		if len(.longHdrPackets) > 1 {
			.logger.Debugf("-> Sending coalesced packet (%d parts, %d bytes) for connection %s", len(.longHdrPackets), .buffer.Len(), .logID)
		} else {
			.logger.Debugf("-> Sending packet %d (%d bytes) for connection %s, %s", .longHdrPackets[0].header.PacketNumber, .buffer.Len(), .logID, .longHdrPackets[0].EncryptionLevel())
		}
	}
	for ,  := range .longHdrPackets {
		.logLongHeaderPacket(, , )
	}
	if  := .shortHdrPacket;  != nil {
		.logShortHeaderPacketWithDatagramID(*, , .Length, true, )
	}
}

func ( *Conn) ( *wire.TransportParameters,  protocol.Perspective,  bool) {
	 := qlog.ParametersSet{
		Restore:                         ,
		OriginalDestinationConnectionID: .OriginalDestinationConnectionID,
		InitialSourceConnectionID:       .InitialSourceConnectionID,
		RetrySourceConnectionID:         .RetrySourceConnectionID,
		StatelessResetToken:             .StatelessResetToken,
		DisableActiveMigration:          .DisableActiveMigration,
		MaxIdleTimeout:                  .MaxIdleTimeout,
		MaxUDPPayloadSize:               .MaxUDPPayloadSize,
		AckDelayExponent:                .AckDelayExponent,
		MaxAckDelay:                     .MaxAckDelay,
		ActiveConnectionIDLimit:         .ActiveConnectionIDLimit,
		InitialMaxData:                  .InitialMaxData,
		InitialMaxStreamDataBidiLocal:   .InitialMaxStreamDataBidiLocal,
		InitialMaxStreamDataBidiRemote:  .InitialMaxStreamDataBidiRemote,
		InitialMaxStreamDataUni:         .InitialMaxStreamDataUni,
		InitialMaxStreamsBidi:           int64(.MaxBidiStreamNum),
		InitialMaxStreamsUni:            int64(.MaxUniStreamNum),
		MaxDatagramFrameSize:            .MaxDatagramFrameSize,
		EnableResetStreamAt:             .EnableResetStreamAt,
	}
	if  == .perspective {
		.Initiator = qlog.InitiatorLocal
	} else {
		.Initiator = qlog.InitiatorRemote
	}
	if .PreferredAddress != nil {
		.PreferredAddress = &qlog.PreferredAddress{
			IPv4:                .PreferredAddress.IPv4,
			IPv6:                .PreferredAddress.IPv6,
			ConnectionID:        .PreferredAddress.ConnectionID,
			StatelessResetToken: .PreferredAddress.StatelessResetToken,
		}
	}
	.qlogger.RecordEvent()
}

func toQlogECN( protocol.ECN) qlog.ECN {
	//nolint:exhaustive // only need to handle the 3 valid values
	switch  {
	case protocol.ECT0:
		return qlog.ECT0
	case protocol.ECT1:
		return qlog.ECT1
	case protocol.ECNCE:
		return qlog.ECNCE
	default:
		return qlog.ECNUnsupported
	}
}

func toQlogPacketType( protocol.PacketType) qlog.PacketType {
	var  qlog.PacketType
	switch  {
	case protocol.PacketTypeInitial:
		 = qlog.PacketTypeInitial
	case protocol.PacketTypeHandshake:
		 = qlog.PacketTypeHandshake
	case protocol.PacketType0RTT:
		 = qlog.PacketType0RTT
	case protocol.PacketTypeRetry:
		 = qlog.PacketTypeRetry
	}
	return 
}

func toPathEndpointInfo( *net.UDPAddr) qlog.PathEndpointInfo {
	if  == nil {
		return qlog.PathEndpointInfo{}
	}

	var  qlog.PathEndpointInfo
	if .IP == nil || .IP.To4() != nil {
		 := netip.AddrPortFrom(netip.AddrFrom4([4]byte(.IP.To4())), uint16(.Port))
		if .IsValid() {
			.IPv4 = 
		}
	} else {
		 := netip.AddrPortFrom(netip.AddrFrom16([16]byte(.IP.To16())), uint16(.Port))
		if .IsValid() {
			.IPv6 = 
		}
	}
	return 
}

// startedConnectionEvent builds a StartedConnection event using consistent logic
// for both endpoints. If the local address is unspecified (e.g., dual-stack
// listener), it selects the family based on the remote address and uses the
// unspecified address of that family with the local port.
func startedConnectionEvent(,  *net.UDPAddr) qlog.StartedConnection {
	var ,  qlog.PathEndpointInfo
	if  != nil {
		 = toPathEndpointInfo()
	}
	if  != nil {
		if .IP == nil || .IP.IsUnspecified() {
			// Choose local family based on the remote address family.
			if  != nil && .IP.To4() != nil {
				 := netip.AddrPortFrom(netip.AddrFrom4([4]byte{}), uint16(.Port))
				if .IsValid() {
					.IPv4 = 
				}
			} else if  != nil && .IP.To16() != nil && .IP.To4() == nil {
				 := netip.AddrPortFrom(netip.AddrFrom16([16]byte{}), uint16(.Port))
				if .IsValid() {
					.IPv6 = 
				}
			}
		} else {
			 = toPathEndpointInfo()
		}
	}
	return qlog.StartedConnection{Local: , Remote: }
}