package tls

import (
	
	
	
	

	
)

const (
	// RFC IDs
	max_idle_timeout                    uint64 = 0x1
	max_udp_payload_size                uint64 = 0x3
	initial_max_data                    uint64 = 0x4
	initial_max_stream_data_bidi_local  uint64 = 0x5
	initial_max_stream_data_bidi_remote uint64 = 0x6
	initial_max_stream_data_uni         uint64 = 0x7
	initial_max_streams_bidi            uint64 = 0x8
	initial_max_streams_uni             uint64 = 0x9
	max_ack_delay                       uint64 = 0xb
	disable_active_migration            uint64 = 0xc
	active_connection_id_limit          uint64 = 0xe
	initial_source_connection_id        uint64 = 0xf
	version_information                 uint64 = 0x11 // RFC 9368
	padding                             uint64 = 0x15
	max_datagram_frame_size             uint64 = 0x20 // RFC 9221
	grease_quic_bit                     uint64 = 0x2ab2

	// Legacy IDs from draft
	version_information_legacy uint64 = 0xff73db // draft-ietf-quic-version-negotiation-13 and early
)

type TransportParameters []TransportParameter

func ( TransportParameters) () []byte {
	var  []byte
	for ,  := range  {
		 = quicvarint.Append(, .ID())
		 = quicvarint.Append(, uint64(len(.Value())))
		 = append(, .Value()...)
	}
	return 
}

// TransportParameter represents a QUIC transport parameter.
//
// Caller will write the following to the wire:
//
//	var b []byte
//	b = quicvarint.Append(b, ID())
//	b = quicvarint.Append(b, len(Value()))
//	b = append(b, Value())
//
// Therefore Value() should return the exact bytes to be written to the wire AFTER the length field,
// i.e., the bytes MAY be a Variable Length Integer per RFC depending on the type of the transport
// parameter, but MUST NOT including the length field unless the parameter is defined so.
type TransportParameter interface {
	ID() uint64
	Value() []byte
}

type GREASETransportParameter struct {
	IdOverride    uint64 // if set to a valid GREASE ID, use this instead of randomly generated one.
	Length        uint16 // if len(ValueOverride) == 0, will generate random data of this size.
	ValueOverride []byte // if len(ValueOverride) > 0, use this instead of random bytes.
}

const (
	GREASE_MAX_MULTIPLIER = (0x3FFFFFFFFFFFFFFF - 27) / 31
)

// IsGREASEID returns true if id is a valid GREASE ID for
// transport parameters.
func (GREASETransportParameter) ( uint64) bool {
	return  >= 27 && (-27)%31 == 0
}

// GetGREASEID returns a random valid GREASE ID for transport parameters.
func (GREASETransportParameter) () uint64 {
	 := big.NewInt(GREASE_MAX_MULTIPLIER)

	,  := rand.Int(rand.Reader, )
	if  != nil {
		return 27
	}

	return 27 + .Uint64()*31
}

func ( *GREASETransportParameter) () uint64 {
	if !.IsGREASEID(.IdOverride) {
		.IdOverride = .GetGREASEID()
	}
	return .IdOverride
}

func ( *GREASETransportParameter) () []byte {
	if len(.ValueOverride) == 0 {
		.ValueOverride = make([]byte, .Length)
		rand.Read(.ValueOverride)
	}
	return .ValueOverride
}

type MaxIdleTimeout uint64 // in milliseconds

func (MaxIdleTimeout) () uint64 {
	return max_idle_timeout
}

func ( MaxIdleTimeout) () []byte {
	return quicvarint.Append([]byte{}, uint64())
}

type MaxUDPPayloadSize uint64

func (MaxUDPPayloadSize) () uint64 {
	return max_udp_payload_size
}

func ( MaxUDPPayloadSize) () []byte {
	return quicvarint.Append([]byte{}, uint64())
}

type InitialMaxData uint64

func (InitialMaxData) () uint64 {
	return initial_max_data
}

func ( InitialMaxData) () []byte {
	return quicvarint.Append([]byte{}, uint64())
}

type InitialMaxStreamDataBidiLocal uint64

func (InitialMaxStreamDataBidiLocal) () uint64 {
	return initial_max_stream_data_bidi_local
}

func ( InitialMaxStreamDataBidiLocal) () []byte {
	return quicvarint.Append([]byte{}, uint64())
}

type InitialMaxStreamDataBidiRemote uint64

func (InitialMaxStreamDataBidiRemote) () uint64 {
	return initial_max_stream_data_bidi_remote
}

func ( InitialMaxStreamDataBidiRemote) () []byte {
	return quicvarint.Append([]byte{}, uint64())
}

type InitialMaxStreamDataUni uint64

func (InitialMaxStreamDataUni) () uint64 {
	return initial_max_stream_data_uni
}

func ( InitialMaxStreamDataUni) () []byte {
	return quicvarint.Append([]byte{}, uint64())
}

type InitialMaxStreamsBidi uint64

func (InitialMaxStreamsBidi) () uint64 {
	return initial_max_streams_bidi
}

func ( InitialMaxStreamsBidi) () []byte {
	return quicvarint.Append([]byte{}, uint64())
}

type InitialMaxStreamsUni uint64

func (InitialMaxStreamsUni) () uint64 {
	return initial_max_streams_uni
}

func ( InitialMaxStreamsUni) () []byte {
	return quicvarint.Append([]byte{}, uint64())
}

type MaxAckDelay uint64

func (MaxAckDelay) () uint64 {
	return max_ack_delay
}

func ( MaxAckDelay) () []byte {
	return quicvarint.Append([]byte{}, uint64())
}

type DisableActiveMigration struct{}

func (*DisableActiveMigration) () uint64 {
	return disable_active_migration
}

// Its Value MUST ALWAYS be empty.
func (*DisableActiveMigration) () []byte {
	return []byte{}
}

type ActiveConnectionIDLimit uint64

func (ActiveConnectionIDLimit) () uint64 {
	return active_connection_id_limit
}

func ( ActiveConnectionIDLimit) () []byte {
	return quicvarint.Append([]byte{}, uint64())
}

type InitialSourceConnectionID []byte // if empty, will be set to the Connection ID used for the Initial packet.

func (InitialSourceConnectionID) () uint64 {
	return initial_source_connection_id
}

func ( InitialSourceConnectionID) () []byte {
	return []byte()
}

type VersionInformation struct {
	ChoosenVersion    uint32
	AvailableVersions []uint32 // Also known as "Other Versions" in early drafts.

	LegacyID bool // If true, use the legacy-assigned ID (0xff73db) instead of the RFC-assigned one (0x11).
}

const (
	VERSION_NEGOTIATION uint32 = 0x00000000 // rfc9000
	VERSION_1           uint32 = 0x00000001 // rfc9000
	VERSION_2           uint32 = 0x6b3343cf // rfc9369

	VERSION_GREASE uint32 = 0x0a0a0a0a // -> 0x?a?a?a?a
)

func ( *VersionInformation) () uint64 {
	if .LegacyID {
		return version_information_legacy
	}
	return version_information
}

func ( *VersionInformation) () []byte {
	var  []byte
	 = binary.BigEndian.AppendUint32(, .ChoosenVersion)
	for ,  := range .AvailableVersions {
		if  != VERSION_GREASE {
			 = binary.BigEndian.AppendUint32(, )
		} else {
			 = binary.BigEndian.AppendUint32(, .GetGREASEVersion())
		}
	}
	return 
}

func (*VersionInformation) () uint32 {
	// get a random uint32
	 := big.NewInt(math.MaxUint32)
	,  := rand.Int(rand.Reader, )
	if  != nil {
		return VERSION_GREASE
	}

	return uint32(.Uint64()&math.MaxUint32) | 0x0a0a0a0a // all GREASE versions are in 0x?a?a?a?a
}

type PaddingTransportParameter []byte

func (PaddingTransportParameter) () uint64 {
	return padding
}

func ( PaddingTransportParameter) () []byte {
	return 
}

type MaxDatagramFrameSize uint64

func (MaxDatagramFrameSize) () uint64 {
	return max_datagram_frame_size
}

func ( MaxDatagramFrameSize) () []byte {
	return quicvarint.Append([]byte{}, uint64())
}

type GREASEQUICBit struct{}

func (*GREASEQUICBit) () uint64 {
	return grease_quic_bit
}

// Its Value MUST ALWAYS be empty.
func (*GREASEQUICBit) () []byte {
	return []byte{}
}

type FakeQUICTransportParameter struct {
	Id  uint64
	Val []byte
}

func ( *FakeQUICTransportParameter) () uint64 {
	if .Id == 0 {
		panic("it is not allowed to use a FakeQUICTransportParameter without setting the ID")
	}
	return .Id
}

func ( *FakeQUICTransportParameter) () []byte {
	return .Val
}