/*
 * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The OpenAirInterface Software Alliance licenses this file to You under
 * the OAI Public License, Version 1.1  (the "License"); you may not use this file
 * except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.openairinterface.org/?page_id=698
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *-------------------------------------------------------------------------------
 * For more information about the OpenAirInterface (OAI) Software Alliance:
 *      contact@openairinterface.org
 */

#ifndef _RLC_ENTITY_AM_H_
#define _RLC_ENTITY_AM_H_

#include <stdint.h>

#include "rlc_entity.h"
#include "rlc_pdu.h"
#include "rlc_sdu.h"

/*
 * Here comes some documentation to understand the reassembly
 * logic in the code and the fields in the structure rlc_am_reassemble_t.
 *
 * Inside RLC, we deal with SDUs, PDUs and PDU segments.
 * SDUs are packets coming from upper layer.
 * A PDU is made of a header and a payload.
 * In the payload there are SDUs.
 * First SDU and last SDU in a PDU may be incomplete.
 * PDU segments exist in case of retransmissions when the MAC
 * layer asks for less data than previously, in which case
 * only part of the previous PDU is sent.
 *
 * This is PDU data (just bytes):
 * ---------------------------------------------------------
 * |  PDU data                                             |
 * ---------------------------------------------------------
 * It contains SDUs, like:
 * ---------------------------------------------------------
 * | SDU 1 | SDU 2     |  [...]                   | SDU n  |
 * ---------------------------------------------------------
 * SDU 1 may be only the end of an SDU from which previous bytes were
 * transmitted in previous PDUs.
 * SDU n may be only the start of an SDU, that is more bytes from
 * this SDU may be sent in successive PDUs.
 *
 * At front of the PDU data, we have a header:
 * ---------------  ---------------------------------------------------------
 * | PDU header  |  | SDU 1 | SDU 2     |  [...]                   | SDU n  |
 * ---------------  ---------------------------------------------------------
 * PDU header describes PDU data (most notably lengths).
 *
 * A PDU segment is a part of a PDU. For example, from this PDU data:
 * ---------------------------------------------------------
 * | SDU 1 | SDU 2     |  [...]                   | SDU n  |
 * ---------------------------------------------------------
 * We can extract the following PDU segment (data part only):
 *                ----------------------
 *                | PDU segment data   |
 *                ----------------------
 * This PDU segment would contain the end of SDU 2 above and some SDUs up to,
 * let's say SDU x (x is 5 below).
 *
 * In front of a transmitted PDU segment, we have a header,
 * containing the important variable 'so' (segment offset) that gives
 * the index of the first byte of the segment in the original PDU.
 * -------------- ----------------------
 * | seg. header| | PDU segment data   |
 * -------------- ----------------------
 *
 * Let's now explain the data structure rlc_am_reassemble_t.
 *
 * In the structure rlc_am_reassemble_t, the fields fi, e, sn and so
 * are coming from the PDU segment header and the semantics is the
 * one of the RLC specs.
 *
 * The currently processed PDU segment is stored in 'start'.
 * We have 'start->s->data_offset' and 'start->s->size'.
 * start->s->data_offset is the index of the start of the data in the
 * PDU segment. That is if the header is of length 3 bytes
 * then start->s->data_offset is 3.
 * start->s->size is the total length of the PDU segment,
 * including header.
 * The size of actual data bytes in the PDU segment is thus
 * start->s->size - start->s->data_offset.
 *
 * The field sdu_len is the length of the current SDU being
 * processed.
 *
 * The field sdu_offset is the starting point of the
 * current SDU being processed (starting from beginning
 * of PDU segment, including header).
 *
 * The field data_pos is the current read pointer. 0 points to
 * the beginning of the PDU segment (including header).
 *
 * The field pdu_byte points to the current byte in the original
 * PDU (not the PDU segment). It starts at 0 when we start
 * processing a new PDU (when a new 'sn' is seen) and always
 * increases after each byte processed. This is tha variable
 * that is used to know if the next PDU segment will be used
 * or not and if yes, starting from which data byte (see
 * function rlc_am_reassemble_next_segment).
 *
 * 'so' is important and points to the byte in the original PDU
 * that is the first byte of the PDU segment.
 *
 * For example, let's take this PDU segment data from above:
 *                ----------------------
 *                | PDU segment data   |
 *                ----------------------
 * Let's say it is decomposed as:
 *                ----------------------
 *                |222|33|4444|55555555|
 *                ----------------------
 * It contains SDUs 2, 3, 4, and 5.
 * SDU 2 is 3 bytes, SDU 3 is 2 bytes, SDU 4 is 4 bytes, SDU 5 is 8 bytes.
 *
 * Let's suppose that the original PDU starts with:
 * ----------------
 * |1111111|222222|
 * ----------------
 *
 * (In this example, in the PDU segment, SDU 2 is not full,
 * we only have its end.)
 *
 * Then 'so' is 13 (SDU 1 is 7 bytes, head of SDU 2 is 6 bytes).
 *
 * Let's continue with our PDU segment data.
 * Let's say we are current processing SDU 4.
 * Let's say the read pointer (variable 'data_pos') is there:
 *                ----------------------
 *                |222|33|4444|55555555|
 *                ----------------------
 *                         ^
 *                      read pointer (data_pos)
 *
 * Then:
 *     - sdu_len is 4
 *     - sdu_offset is 5 + [PDU segment header length]
 *       (it points to the beginning of SDU 4, starting
 *        from the head of the PDU segment, that is
 *        3 bytes for SDU 2, 2 bytes for SDU 3, and the
 *        PDU segment header length)
 *     - start->s->data_offset is [PDU segment header length]
 *     - pdu_byte is 20
 *       (13 bytes from beginning of original PDU,
 *        3 bytes for SDU 2, 2 bytes for SDU 3, then 2 bytes for SDU 4)
 *     - data_pos = read pointer = 7 + [PDU segment header length]
 *
 * To finish this description, in the code, a PDU is simply
 * seen as a PDU segment with 'so' = 0 (and is_last == 1 (lsf in the specs),
 * but this variable is not used by the reassembly logic).
 *
 * And for [PDU segment header length] we use start->s->data_offset.
 *
 * To recap, here is an illustration of the various variables
 * and what starting point they use. In the figures, the start
 * of the variable name is aligned to the byte it refers to.
 * + is used to show the starting point.
 *
 * Let's put the PDU segment back into the original PDU.
 * And let's show the values for when the read pointer
 * is on the second byte of SDU 4 (as above).
 *
 * +++++++++++++++ so
 * +++++++++++++++++++++++ pdu_byte
 * ---------------------------------------------------------
 * | SDU 1| SDU 2..222|33|4444|55555555| [...]   | SDU n   |
 * ---------------------------------------------------------
 *
 * And now the PDU segment with header.
 *
 *
 *                        ++++ sdu_len
 * ++++++++++++++++++++++ sdu_offset
 * +++++++++++++++++++++++ data_pos
 * +++++++++++++++ start->s->data_offset
 * +++++++++++++++++++++++++++++++++++++ start->s->size
 * -------------- ----------------------
 * | seg. header| |222|33|4444|55555555|
 * -------------- ----------------------
 *
 * We see three case for the starting point:
 *     - start of original PDU (without any header)
 *     - start of header of current PDU segment
 *     - start of current SDU (for sdu_len)
 */

typedef struct {
  rlc_rx_pdu_segment_t *start;      /* start of list */
  rlc_rx_pdu_segment_t *end;        /* end of list (last element) */
  int                  pos;         /* byte to get from current buffer */
  char                 sdu[SDU_MAX]; /* sdu is reassembled here */
  int                  sdu_pos;      /* next byte to put in sdu */

  /* decoder of current PDU */
  rlc_pdu_decoder_t    dec;
  int fi;
  int e;
  int sn;
  int so;
  int sdu_len;
  int sdu_offset;
  int data_pos;
  int pdu_byte;
} rlc_am_reassemble_t;

typedef struct {
  rlc_entity_t common;

  /* configuration */
  int t_reordering;
  int t_status_prohibit;
  int t_poll_retransmit;
  int poll_pdu;              /* -1 means infinity */
  int poll_byte;             /* -1 means infinity */
  int max_retx_threshold;

  /* runtime rx */
  int vr_r;
  int vr_x;
  int vr_ms;
  int vr_h;

  int status_triggered;

  /* runtime tx */
  int vt_a;
  int vt_s;
  int poll_sn;
  int pdu_without_poll;
  int byte_without_poll;
  int force_poll;

  /* set to the latest know time by the user of the module. Unit: ms */
  uint64_t t_current;

  /* timers (stores the TTI of activation, 0 means not active) */
  uint64_t t_reordering_start;
  uint64_t t_status_prohibit_start;
  uint64_t t_poll_retransmit_start;

  /* rx management */
  rlc_rx_pdu_segment_t *rx_list;
  int                  rx_size;
  int                  rx_maxsize;

  /* reassembly management */
  rlc_am_reassemble_t    reassemble;

  /* tx management */
  rlc_sdu_t *tx_list;
  rlc_sdu_t *tx_end;
  int       tx_size;
  int       tx_maxsize;

  rlc_tx_pdu_segment_t *wait_list;
  rlc_tx_pdu_segment_t *retransmit_list;

  rlc_tx_pdu_segment_t *ack_list;
} rlc_entity_am_t;

void rlc_entity_am_recv_sdu(rlc_entity_t *entity, char *buffer, int size,
                            int sdu_id);
void rlc_entity_am_recv_pdu(rlc_entity_t *entity, char *buffer, int size);
rlc_entity_buffer_status_t rlc_entity_am_buffer_status(
    rlc_entity_t *entity, int maxsize);
int rlc_entity_am_generate_pdu(rlc_entity_t *entity, char *buffer, int size);
void rlc_entity_am_set_time(rlc_entity_t *entity, uint64_t now);
void rlc_entity_am_discard_sdu(rlc_entity_t *entity, int sdu_id);
void rlc_entity_am_reestablishment(rlc_entity_t *entity);
void rlc_entity_am_delete(rlc_entity_t *entity);

#endif /* _RLC_ENTITY_AM_H_ */