// 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"
)

type rttState struct {
	minRTT          time.Duration
	latestRTT       time.Duration
	smoothedRTT     time.Duration
	rttvar          time.Duration // RTT variation
	firstSampleTime time.Time     // time of first RTT sample
}

func (r *rttState) init() {
	r.minRTT = -1 // -1 indicates the first sample has not been taken yet

	// "[...] the initial RTT SHOULD be set to 333 milliseconds."
	// https://www.rfc-editor.org/rfc/rfc9002.html#section-6.2.2-1
	const initialRTT = 333 * time.Millisecond

	// https://www.rfc-editor.org/rfc/rfc9002.html#section-5.3-12
	r.smoothedRTT = initialRTT
	r.rttvar = initialRTT / 2
}

func (r *rttState) establishPersistentCongestion() {
	// "Endpoints SHOULD set the min_rtt to the newest RTT sample
	// after persistent congestion is established."
	// https://www.rfc-editor.org/rfc/rfc9002#section-5.2-5
	r.minRTT = r.latestRTT
}

// updateRTTSample is called when we generate a new RTT sample.
// https://www.rfc-editor.org/rfc/rfc9002.html#section-5
func (r *rttState) updateSample(now time.Time, handshakeConfirmed bool, spaceID numberSpace, latestRTT, ackDelay, maxAckDelay time.Duration) {
	r.latestRTT = latestRTT

	if r.minRTT < 0 {
		// First RTT sample.
		// "min_rtt MUST be set to the latest_rtt on the first RTT sample."
		// https://www.rfc-editor.org/rfc/rfc9002.html#section-5.2-2
		r.minRTT = latestRTT
		// https://www.rfc-editor.org/rfc/rfc9002.html#section-5.3-14
		r.smoothedRTT = latestRTT
		r.rttvar = latestRTT / 2
		r.firstSampleTime = now
		return
	}

	// "min_rtt MUST be set to the lesser of min_rtt and latest_rtt [...]
	// on all other samples."
	// https://www.rfc-editor.org/rfc/rfc9002.html#section-5.2-2
	r.minRTT = min(r.minRTT, latestRTT)

	// https://www.rfc-editor.org/rfc/rfc9002.html#section-5.3-16
	if handshakeConfirmed {
		ackDelay = min(ackDelay, maxAckDelay)
	}
	adjustedRTT := latestRTT - ackDelay
	if adjustedRTT < r.minRTT {
		adjustedRTT = latestRTT
	}
	rttvarSample := abs(r.smoothedRTT - adjustedRTT)
	r.rttvar = (3*r.rttvar + rttvarSample) / 4
	r.smoothedRTT = ((7 * r.smoothedRTT) + adjustedRTT) / 8
}
