/*
 * ngtcp2
 *
 * Copyright (c) 2017 ngtcp2 contributors
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
#ifndef NGTCP2_ACKTR_H
#define NGTCP2_ACKTR_H

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif /* defined(HAVE_CONFIG_H) */

#include <ngtcp2/ngtcp2.h>

#include "ngtcp2_mem.h"
#include "ngtcp2_ringbuf.h"
#include "ngtcp2_ksl.h"
#include "ngtcp2_pkt.h"
#include "ngtcp2_objalloc.h"

/* NGTCP2_ACKTR_MAX_ENT is the maximum number of ngtcp2_acktr_entry
   which ngtcp2_acktr stores. */
#define NGTCP2_ACKTR_MAX_ENT (NGTCP2_MAX_ACK_RANGES + 1)

typedef struct ngtcp2_log ngtcp2_log;

/*
 * ngtcp2_acktr_entry is a range of packets which need to be acked.
 */
typedef struct ngtcp2_acktr_entry {
  union {
    struct {
      /* pkt_num is the largest packet number to acknowledge in this
         range. */
      int64_t pkt_num;
      /* len is the consecutive packets started from pkt_num which
         includes pkt_num itself counting in decreasing order.  So pkt_num
         = 987 and len = 2, this entry includes packet 987 and 986. */
      size_t len;
      /* tstamp is the timestamp when a packet denoted by pkt_num is
         received. */
      ngtcp2_tstamp tstamp;
    };

    ngtcp2_opl_entry oplent;
  };
} ngtcp2_acktr_entry;

ngtcp2_objalloc_decl(acktr_entry, ngtcp2_acktr_entry, oplent)

/*
 * ngtcp2_acktr_entry_objalloc_new allocates memory for ent, and
 * initializes it with the given parameters.  The pointer to the
 * allocated object is stored to |*ent|.
 *
 * This function returns 0 if it succeeds, or one of the following
 * negative error codes:
 *
 * NGTCP2_ERR_NOMEM
 *     Out of memory.
 */
int ngtcp2_acktr_entry_objalloc_new(ngtcp2_acktr_entry **ent, int64_t pkt_num,
                                    ngtcp2_tstamp tstamp,
                                    ngtcp2_objalloc *objalloc);

/*
 * ngtcp2_acktr_entry_objalloc_del deallocates memory allocated for
 * |ent|.
 */
void ngtcp2_acktr_entry_objalloc_del(ngtcp2_acktr_entry *ent,
                                     ngtcp2_objalloc *objalloc);

typedef struct ngtcp2_acktr_ack_entry {
  /* largest_ack is the largest packet number in outgoing ACK frame */
  int64_t largest_ack;
  /* pkt_num is the packet number that ACK frame is included. */
  int64_t pkt_num;
} ngtcp2_acktr_ack_entry;

/* NGTCP2_ACKTR_FLAG_NONE indicates that no flag set. */
#define NGTCP2_ACKTR_FLAG_NONE 0x00U
/* NGTCP2_ACKTR_FLAG_IMMEDIATE_ACK indicates that immediate
   acknowledgement is required. */
#define NGTCP2_ACKTR_FLAG_IMMEDIATE_ACK 0x01U
/* NGTCP2_ACKTR_FLAG_ACTIVE_ACK indicates that there are pending
   protected packet to be acknowledged. */
#define NGTCP2_ACKTR_FLAG_ACTIVE_ACK 0x02U
/* NGTCP2_ACKTR_FLAG_CANCEL_TIMER is set when ACK delay timer is
   expired and canceled. */
#define NGTCP2_ACKTR_FLAG_CANCEL_TIMER 0x0100U

ngtcp2_static_ringbuf_def(acks, 32, sizeof(ngtcp2_acktr_ack_entry))

/*
 * ngtcp2_acktr tracks received packets which we have to send ack.
 */
typedef struct ngtcp2_acktr {
  ngtcp2_objalloc objalloc;
  ngtcp2_static_ringbuf_acks acks;
  /* ents includes ngtcp2_acktr_entry sorted by decreasing order of
     packet number. */
  ngtcp2_ksl ents;
  ngtcp2_log *log;
  /* first_unacked_ts is timestamp when ngtcp2_acktr_entry is added
     first time after the last outgoing ACK frame. */
  ngtcp2_tstamp first_unacked_ts;
  /* rx_npkt is the number of ACK eliciting packets received without
     sending ACK. */
  size_t rx_npkt;
  /* max_pkt_num is the largest packet number received so far. */
  int64_t max_pkt_num;
  /* max_pkt_ts is the timestamp when max_pkt_num packet is
     received. */
  ngtcp2_tstamp max_pkt_ts;

  struct {
    /* ect0, ect1, and ce are the number of QUIC packets received
       with those markings. */
    size_t ect0;
    size_t ect1;
    size_t ce;
    struct {
      /* ect0, ect1, ce are the ECN counts received in the latest
         ACK frame. */
      uint64_t ect0;
      uint64_t ect1;
      uint64_t ce;
    } ack;
  } ecn;

  /* flags is bitwise OR of zero, or more of NGTCP2_ACKTR_FLAG_*. */
  uint16_t flags;
} ngtcp2_acktr;

/*
 * ngtcp2_acktr_init initializes |acktr|.
 */
void ngtcp2_acktr_init(ngtcp2_acktr *acktr, ngtcp2_log *log,
                       const ngtcp2_mem *mem);

/*
 * ngtcp2_acktr_free frees resources allocated for |acktr|.  It frees
 * any ngtcp2_acktr_entry added to |acktr|.
 */
void ngtcp2_acktr_free(ngtcp2_acktr *acktr);

/*
 * ngtcp2_acktr_add adds packet number |pkt_num| to |acktr|.
 * |active_ack| is nonzero if |pkt_num| is retransmittable packet.
 *
 * This function assumes that |acktr| does not contain |pkt_num|.
 *
 * This function returns 0 if it succeeds, or one of the following
 * negative error codes:
 *
 * NGTCP2_ERR_NOMEM
 *     OUt of memory.
 */
int ngtcp2_acktr_add(ngtcp2_acktr *acktr, int64_t pkt_num, int active_ack,
                     ngtcp2_tstamp ts);

/*
 * ngtcp2_acktr_forget removes all entries which have the packet
 * number that is equal to or less than ent->pkt_num.  This function
 * assumes that |acktr| includes |ent|.
 */
void ngtcp2_acktr_forget(ngtcp2_acktr *acktr, ngtcp2_acktr_entry *ent);

/*
 * ngtcp2_acktr_get returns the iterator to pointer to the entry which
 * has the largest packet number to be acked.  If there is no entry,
 * returned value satisfies ngtcp2_ksl_it_end(&it) != 0.
 */
ngtcp2_ksl_it ngtcp2_acktr_get(const ngtcp2_acktr *acktr);

/*
 * ngtcp2_acktr_empty returns nonzero if it has no packet to
 * acknowledge.
 */
int ngtcp2_acktr_empty(const ngtcp2_acktr *acktr);

/*
 * ngtcp2_acktr_add_ack records outgoing ACK frame whose largest
 * acknowledged packet number is |largest_ack|.  |pkt_num| is the
 * packet number of a packet in which ACK frame is included.  This
 * function returns a pointer to the object it adds.
 */
ngtcp2_acktr_ack_entry *
ngtcp2_acktr_add_ack(ngtcp2_acktr *acktr, int64_t pkt_num, int64_t largest_ack);

/*
 * ngtcp2_acktr_recv_ack processes the incoming ACK frame |fr|.
 * |pkt_num| is a packet number which includes |fr|.  If we receive
 * ACK which acknowledges the ACKs added by ngtcp2_acktr_add_ack,
 * ngtcp2_acktr_entry which the outgoing ACK acknowledges is removed.
 */
void ngtcp2_acktr_recv_ack(ngtcp2_acktr *acktr, const ngtcp2_ack *fr);

/*
 * ngtcp2_acktr_commit_ack tells |acktr| that ACK frame is generated.
 */
void ngtcp2_acktr_commit_ack(ngtcp2_acktr *acktr);

/*
 * ngtcp2_acktr_require_active_ack returns nonzero if ACK frame should
 * be generated actively.
 */
int ngtcp2_acktr_require_active_ack(const ngtcp2_acktr *acktr,
                                    ngtcp2_duration max_ack_delay,
                                    ngtcp2_tstamp ts);

/*
 * ngtcp2_acktr_immediate_ack tells |acktr| that immediate
 * acknowledgement is required.
 */
void ngtcp2_acktr_immediate_ack(ngtcp2_acktr *acktr);

/*
 * ngtcp2_acktr_create_ack_frame creates ACK frame in the object
 * pointed by |ack|, and returns 0 if it successfully creates ACK
 * frame in |ack|.  If there are no packets to acknowledge, this
 * function returns -1.  |ack|->ranges must be able to contain at
 * least NGTCP2_MAX_ACK_RANGES elements.
 *
 * Call ngtcp2_acktr_commit_ack after a created ACK frame is
 * successfully serialized into a packet.
 */
int ngtcp2_acktr_create_ack_frame(ngtcp2_acktr *acktr, ngtcp2_ack *ack,
                                  uint8_t type, ngtcp2_tstamp ts,
                                  ngtcp2_duration ack_delay,
                                  uint64_t ack_delay_exponent);

/*
 * ngtcp2_acktr_increase_ecn_counts increases ECN counts from |pi|.
 */
void ngtcp2_acktr_increase_ecn_counts(ngtcp2_acktr *acktr,
                                      const ngtcp2_pkt_info *pi);

#endif /* !defined(NGTCP2_ACKTR_H) */
