/*
 * 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 /* HAVE_CONFIG_H */

#include <ngtcp2/ngtcp2.h>

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

/* NGTCP2_ACKTR_MAX_ENT is the maximum number of ngtcp2_acktr_entry
   which ngtcp2_acktr stores. */
#define NGTCP2_ACKTR_MAX_ENT 1024

typedef struct ngtcp2_log ngtcp2_log;

/*
 * ngtcp2_acktr_entry is a range of packets which need to be acked.
 */
typedef struct ngtcp2_acktr_entry {
  /* 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_acktr_entry;

/*
 * ngtcp2_acktr_entry_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_new(ngtcp2_acktr_entry **ent, int64_t pkt_num,
                           ngtcp2_tstamp tstamp, const ngtcp2_mem *mem);

/*
 * ngtcp2_acktr_entry_del deallocates memory allocated for |ent|.  It
 * deallocates memory pointed by |ent|.
 */
void ngtcp2_acktr_entry_del(ngtcp2_acktr_entry *ent, const ngtcp2_mem *mem);

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 0x00
/* NGTCP2_ACKTR_FLAG_IMMEDIATE_ACK indicates that immediate
   acknowledgement is required. */
#define NGTCP2_ACKTR_FLAG_IMMEDIATE_ACK 0x01
/* NGTCP2_ACKTR_FLAG_ACTIVE_ACK indicates that there are pending
   protected packet to be acknowledged. */
#define NGTCP2_ACKTR_FLAG_ACTIVE_ACK 0x02
/* NGTCP2_ACKTR_FLAG_CANCEL_TIMER is set when ACK delay timer is
   expired and canceled. */
#define NGTCP2_ACKTR_FLAG_CANCEL_TIMER 0x0100

/*
 * ngtcp2_acktr tracks received packets which we have to send ack.
 */
typedef struct ngtcp2_acktr {
  ngtcp2_ringbuf acks;
  /* ents includes ngtcp2_acktr_entry sorted by decreasing order of
     packet number. */
  ngtcp2_ksl ents;
  ngtcp2_log *log;
  const ngtcp2_mem *mem;
  /* flags is bitwise OR of zero, or more of NGTCP2_ACKTR_FLAG_*. */
  uint16_t flags;
  /* 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 packets received without sending ACK. */
  size_t rx_npkt;
} ngtcp2_acktr;

/*
 * ngtcp2_acktr_init initializes |acktr|.
 *
 * This function returns 0 if it succeeds, or one of the following
 * negative error codes:
 *
 * NGTCP2_ERR_NOMEM
 *     Out of memory.
 */
int 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 pointer 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(ngtcp2_acktr *acktr);

/*
 * ngtcp2_acktr_empty returns nonzero if it has no packet to
 * acknowledge.
 */
int ngtcp2_acktr_empty(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(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);

#endif /* NGTCP2_ACKTR_H */
