/* Copyright (c) 2017 - 2022 LiteSpeed Technologies Inc.  See LICENSE. */
#ifndef LSQUIC_BW_SAMPLER_H
#define LSQUIC_BW_SAMPLER_H 1

/* Translated from Chromium. */
// Copyright 2016 The Chromium Authors. All rights reserved.

struct lsquic_packet_out;

/* This struct provides a type for bits per second units.  It's made into
 * a struct so that it is a little harder to make a mistake.  The Chromium
 * equivalent of this is QuicBandwidth.  Use macros to operate.
 */
struct bandwidth
{
    uint64_t            value;  /* Bits per second */
};

#define BW_INFINITE() ((struct bandwidth) { .value = UINT64_MAX, })
#define BW_ZERO() ((struct bandwidth) { .value = 0, })
#define BW_FROM_BYTES_AND_DELTA(bytes_, usecs_) \
    ((struct bandwidth) { .value = (bytes_) * 8 * 1000000 / (usecs_), })
#define BW_IS_ZERO(bw_) ((bw_)->value == 0)
#define BW_TO_BYTES_PER_SEC(bw_) ((bw_)->value / 8)
#define BW_VALUE(bw_) (+(bw_)->value)
#define BW_TIMES(bw_, factor_) \
                ((struct bandwidth) { .value = BW_VALUE(bw_) * (factor_), })
#define BW(initial_value_) ((struct bandwidth) { .value = (initial_value_) })

struct bw_sampler
{
    struct lsquic_conn *bws_conn;
    uint64_t            bws_total_sent,
                        bws_total_acked,
                        bws_total_lost;
    /* Value of bws_total_sent at the time last ACKed packet was sent.  Only
     * valid if bws_last_acked_sent_time is valid.
     */
    uint64_t            bws_last_acked_total_sent;
    /* Time when last acked packet was sent.  Zero if no valid timestamp is
     * available.
     */
    lsquic_time_t       bws_last_acked_sent_time;
    lsquic_time_t       bws_last_acked_packet_time;
    lsquic_packno_t     bws_last_sent_packno;
    lsquic_packno_t     bws_end_of_app_limited_phase;
    struct malo        *bws_malo;   /* For struct osp_state objects */
    enum quic_ft_bit    bws_retx_frames;
    enum {
        BWS_CONN_ABORTED    = 1 << 0,
        BWS_WARNED          = 1 << 1,
        BWS_APP_LIMITED     = 1 << 2,
    }                   bws_flags;
};

struct bw_sample
{
    TAILQ_ENTRY(bw_sample)      next;
    struct bandwidth            bandwidth;
    lsquic_time_t               rtt;
    int                         is_app_limited;
};

int
lsquic_bw_sampler_init (struct bw_sampler *, struct lsquic_conn *,
                                                            enum quic_ft_bit);

void
lsquic_bw_sampler_packet_sent (struct bw_sampler *, struct lsquic_packet_out *,
                                                            uint64_t in_flight);

/* Free returned sample via lsquic_malo_put() after you're done */
struct bw_sample *
lsquic_bw_sampler_packet_acked (struct bw_sampler *, struct lsquic_packet_out *,
                            lsquic_time_t ack_time);

void
lsquic_bw_sampler_packet_lost (struct bw_sampler *, struct lsquic_packet_out *);

void
lsquic_bw_sampler_app_limited (struct bw_sampler *);

void
lsquic_bw_sampler_cleanup (struct bw_sampler *);

unsigned
lsquic_bw_sampler_entry_count (const struct bw_sampler *);

#define lsquic_bw_sampler_total_acked(sampler_) (+(sampler_)->bws_total_acked)

/* The following types are exposed in the header file because of unit tests.
 * Do not use outside of lsquic_bw_sampler.c.
 */
struct bwps_send_state
{
    uint64_t    total_bytes_sent,
                total_bytes_acked,
                total_bytes_lost;
    int         is_app_limited;
};

struct bwp_state    /* BWP State stands for Bandwidth Packet State */
{
    struct bwps_send_state      bwps_send_state;
    uint64_t                    bwps_sent_at_last_ack;
    lsquic_time_t               bwps_last_ack_sent_time;
    lsquic_time_t               bwps_last_ack_ack_time;
    unsigned short              bwps_packet_size;
};

#endif
