// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

//go:build go1.21

package quic

import (
	"time"
)

// connIDLen is the length in bytes of connection IDs chosen by this package.
// Since 1-RTT packets don't include a connection ID length field,
// we use a consistent length for all our IDs.
// https://www.rfc-editor.org/rfc/rfc9000.html#section-5.1-6
const connIDLen = 8

// Local values of various transport parameters.
// https://www.rfc-editor.org/rfc/rfc9000.html#section-18.2
const (
	// The max_udp_payload_size transport parameter is the size of our
	// network receive buffer.
	//
	// Set this to the largest UDP packet that can be sent over
	// Ethernet without using jumbo frames: 1500 byte Ethernet frame,
	// minus 20 byte IPv4 header and 8 byte UDP header.
	//
	// The maximum possible UDP payload is 65527 bytes. Supporting this
	// without wasting memory in unused receive buffers will require some
	// care. For now, just limit ourselves to the most common case.
	maxUDPPayloadSize = 1472

	ackDelayExponent = 3                     // ack_delay_exponent
	maxAckDelay      = 25 * time.Millisecond // max_ack_delay
)

// A side distinguishes between the client and server sides of a connection.
type side int8

const (
	clientSide = side(iota)
	serverSide
)

func (s side) String() string {
	switch s {
	case clientSide:
		return "client"
	case serverSide:
		return "server"
	default:
		return "BUG"
	}
}

// A numberSpace is the context in which a packet number applies.
// https://www.rfc-editor.org/rfc/rfc9000.html#section-12.3-7
type numberSpace byte

const (
	initialSpace = numberSpace(iota)
	handshakeSpace
	appDataSpace
)

func (n numberSpace) String() string {
	switch n {
	case initialSpace:
		return "Initial"
	case handshakeSpace:
		return "Handshake"
	case appDataSpace:
		return "AppData"
	default:
		return "BUG"
	}
}

// A streamType is the type of a stream: bidirectional or unidirectional.
type streamType uint8

const (
	bidiStream = streamType(iota)
	uniStream
)

func (s streamType) String() string {
	switch s {
	case bidiStream:
		return "bidi"
	case uniStream:
		return "uni"
	default:
		return "BUG"
	}
}

// A streamID is a QUIC stream ID.
// https://www.rfc-editor.org/rfc/rfc9000.html#section-2.1
type streamID uint64

// The two least significant bits of a stream ID indicate the initiator
// and directionality of the stream. The upper bits are the stream number.
// Each of the four possible combinations of initiator and direction
// each has a distinct number space.
const (
	clientInitiatedStreamBit = 0x0
	serverInitiatedStreamBit = 0x1
	initiatorStreamBitMask   = 0x1

	bidiStreamBit    = 0x0
	uniStreamBit     = 0x2
	dirStreamBitMask = 0x2
)

func newStreamID(initiator side, typ streamType, num int64) streamID {
	id := streamID(num << 2)
	if typ == uniStream {
		id |= uniStreamBit
	}
	if initiator == serverSide {
		id |= serverInitiatedStreamBit
	}
	return id
}

func (s streamID) initiator() side {
	if s&initiatorStreamBitMask == serverInitiatedStreamBit {
		return serverSide
	}
	return clientSide
}

func (s streamID) num() int64 {
	return int64(s) >> 2
}

func (s streamID) streamType() streamType {
	if s&dirStreamBitMask == uniStreamBit {
		return uniStream
	}
	return bidiStream
}
