/*******************************************************************************
    OpenAirInterface
    Copyright(c) 1999 - 2014 Eurecom

    OpenAirInterface is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.


    OpenAirInterface is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with OpenAirInterface.The full GNU General Public License is
   included in this distribution in the file called "COPYING". If not,
   see <http://www.gnu.org/licenses/>.

  Contact Information
  OpenAirInterface Admin: openair_admin@eurecom.fr
  OpenAirInterface Tech : openair_tech@eurecom.fr
  OpenAirInterface Dev  : openair4g-devel@lists.eurecom.fr

  Address      : Eurecom, Campus SophiaTech, 450 Route des Chappes, CS 50193 - 06904 Biot Sophia Antipolis cedex, FRANCE

 *******************************************************************************/
#define RLC_AM_MODULE 1
#define RLC_AM_SEGMENT_C 1
//-----------------------------------------------------------------------------
#if USER_MODE
#include <assert.h>
#endif
//-----------------------------------------------------------------------------
#include "platform_types.h"
//-----------------------------------------------------------------------------
#include "msc.h"
#include "list.h"
#include "rlc_am.h"
#include "LAYER2/MAC/extern.h"
#include "UTIL/LOG/log.h"

//-----------------------------------------------------------------------------
void rlc_am_pdu_polling (
  const protocol_ctxt_t* const  ctxt_pP,
  rlc_am_entity_t *const rlc_pP,
  rlc_am_pdu_sn_10_t *const pdu_pP,
  const int16_t payload_sizeP)
{
  // 5.2.2 Polling
  // An AM RLC entity can poll its peer AM RLC entity in order to trigger STATUS reporting at the peer AM RLC entity.
  // 5.2.2.1 Transmission of a AMD PDU or AMD PDU segment
  // Upon assembly of a new AMD PDU, the transmitting side of an AM RLC entity shall:
  //     - increment PDU_WITHOUT_POLL by one;
  //     - increment BYTE_WITHOUT_POLL by every new byte of Data field element that it maps to the Data field of
  //       the RLC data PDU;
  //     - if PDU_WITHOUT_POLL >= pollPDU; or
  //     - if BYTE_WITHOUT_POLL >= pollByte;
  //         -include a poll in the RLC data PDU as described below.
  // Upon assembly of an AMD PDU or AMD PDU segment, the transmitting side of an AM RLC entity shall:
  //     - if both the transmission buffer and the retransmission buffer becomes empty (excluding transmitted RLC data
  //       PDU awaiting for acknowledgements) after the transmission of the RLC data PDU; or
  //     - if no new RLC data PDU can be transmitted after the transmission of the RLC data PDU (e.g. due to window
  //       stalling);
  //         - include a poll in the RLC data PDU as described below.
  // To include a poll in a RLC data PDU, the transmitting side of an AM RLC entity shall:
  //     - set the P field of the RLC data PDU to "1";
  //     - set PDU_WITHOUT_POLL to 0;
  //     - set BYTE_WITHOUT_POLL to 0;
  // After delivering a RLC data PDU including a poll to lower layer and after incrementing of VT(S) if necessary, the
  // transmitting side of an AM RLC entity shall:
  //     - set POLL_SN to VT(S) – 1;
  //     - if t-PollRetransmit is not running:
  //         - start t-PollRetransmit;
  //     - else:
  //         - restart t-PollRetransmit;
  rlc_pP->c_pdu_without_poll     += 1;
  rlc_pP->c_byte_without_poll    += payload_sizeP;

  if (
    (rlc_pP->c_pdu_without_poll >= rlc_pP->poll_pdu) ||
    (rlc_pP->c_byte_without_poll >= rlc_pP->poll_byte) ||
    ((rlc_pP->sdu_buffer_occupancy == 0) && (rlc_pP->retrans_num_bytes_to_retransmit == 0)) ||
    (rlc_pP->vt_s == rlc_pP->vt_ms) ||
    (rlc_pP->force_poll == TRUE)
  ) {
	rlc_pP->force_poll = FALSE;

    if (rlc_pP->c_pdu_without_poll >= rlc_pP->poll_pdu) {
      LOG_T(RLC, PROTOCOL_RLC_AM_CTXT_FMT"[POLL] SET POLL BECAUSE TX NUM PDU THRESHOLD %d  HAS BEEN REACHED\n",
            PROTOCOL_RLC_AM_CTXT_ARGS(ctxt_pP,rlc_pP),
            rlc_pP->poll_pdu);
    } else
    if (rlc_pP->c_byte_without_poll >= rlc_pP->poll_byte) {
      LOG_T(RLC, PROTOCOL_RLC_AM_CTXT_FMT"[POLL] SET POLL BECAUSE TX NUM BYTES THRESHOLD %d  HAS BEEN REACHED\n",
            PROTOCOL_RLC_AM_CTXT_ARGS(ctxt_pP,rlc_pP),
            rlc_pP->poll_byte);
    } else
    if ((rlc_pP->sdu_buffer_occupancy == 0) && (rlc_pP->retrans_num_bytes_to_retransmit == 0)) {
      LOG_T(RLC, PROTOCOL_RLC_AM_CTXT_FMT"[POLL] SET POLL BECAUSE TX BUFFERS ARE EMPTY\n",
            PROTOCOL_RLC_AM_CTXT_ARGS(ctxt_pP,rlc_pP));
    } else
    if (rlc_pP->vt_s == rlc_pP->vt_ms) {
      LOG_T(RLC, PROTOCOL_RLC_AM_CTXT_FMT"[POLL] SET POLL BECAUSE OF WINDOW STALLING\n",
            PROTOCOL_RLC_AM_CTXT_ARGS(ctxt_pP,rlc_pP));
    }

    pdu_pP->b1 = pdu_pP->b1 | 0x20;
    rlc_pP->c_pdu_without_poll     = 0;
    rlc_pP->c_byte_without_poll    = 0;

    rlc_pP->poll_sn = (rlc_pP->vt_s -1) & RLC_AM_SN_MASK;
    //optimisation if (!rlc_pP->t_poll_retransmit.running) {
    rlc_am_start_timer_poll_retransmit(ctxt_pP, rlc_pP);
    //optimisation } else {
    //optimisation     rlc_pP->t_poll_retransmit.frame_time_out = ctxt_pP->frame + rlc_pP->t_poll_retransmit.time_out;
    //optimisation }
  } else {
    pdu_pP->b1 = pdu_pP->b1 & 0xDF;
  }
}
//-----------------------------------------------------------------------------
void rlc_am_segment_10 (
  const protocol_ctxt_t* const  ctxt_pP,
  rlc_am_entity_t *const rlc_pP)
{
  list_t              pdus;
  sdu_size_t          pdu_remaining_size      = 0;
  sdu_size_t          test_pdu_remaining_size = 0;

  sdu_size_t                       nb_bytes_to_transmit = rlc_pP->nb_bytes_requested_by_mac;
  rlc_am_pdu_sn_10_t              *pdu_p        = NULL;
  struct mac_tb_req               *pdu_tb_req_p = NULL;
  mem_block_t                     *pdu_mem_p    = NULL;
  unsigned char                   *data         = NULL;
  unsigned char                   *data_sdu_p   = NULL;
  rlc_am_e_li_t                   *e_li_p       = NULL;
  rlc_am_tx_sdu_management_t      *sdu_mngt_p   = NULL;
  rlc_am_tx_data_pdu_management_t *pdu_mngt_p   = NULL;

  sdu_size_t         li_length_in_bytes         = 0;
  sdu_size_t         test_li_length_in_bytes    = 0;
  sdu_size_t         test_remaining_size_to_substract= 0;
  unsigned int       test_remaining_num_li_to_substract = 0;
  unsigned int       continue_fill_pdu_with_sdu         = 0;
  unsigned int       num_fill_sdu                       = 0;
  unsigned int       test_num_li                        = 0;
  unsigned int       fill_num_li                        = 0;
  unsigned int       sdu_buffer_index                   = 0;
  sdu_size_t         data_pdu_size                      = 0;

  unsigned int       fi_first_byte_pdu_is_first_byte_sdu = 0;
  unsigned int       fi_last_byte_pdu_is_last_byte_sdu   = 0;
  unsigned int       fi                                  = 0;
  signed int         max_li_overhead                     = 0;

  LOG_T(RLC,
        PROTOCOL_RLC_AM_CTXT_FMT"[SEGMENT] rlc_pP->current_sdu_index %d rlc_pP->next_sdu_index %d rlc_pP->input_sdus[rlc_pP->current_sdu_index].mem_block %p sdu_buffer_occupancy %d\n",
        PROTOCOL_RLC_AM_CTXT_ARGS(ctxt_pP,rlc_pP),
        rlc_pP->current_sdu_index,
        rlc_pP->next_sdu_index,
        rlc_pP->input_sdus[rlc_pP->current_sdu_index].mem_block,
        rlc_pP->sdu_buffer_occupancy);

  if (rlc_pP->sdu_buffer_occupancy <= 0) {
    return;
  }

  //msg ("[FRAME %05d][%s][RLC_AM][MOD %u/%u][RB %u][SEGMENT]\n", rlc_pP->module_id, rlc_pP->rb_id, ctxt_pP->frame);
  list_init (&pdus, NULL);    // param string identifying the list is NULL
  pdu_mem_p = NULL;


  RLC_AM_MUTEX_LOCK(&rlc_pP->lock_input_sdus, ctxt_pP, rlc_pP);

  while ((rlc_pP->input_sdus[rlc_pP->current_sdu_index].mem_block) && (nb_bytes_to_transmit > 0) ) {
    LOG_T(RLC, PROTOCOL_RLC_AM_CTXT_FMT"[SEGMENT] nb_bytes_to_transmit %d BO %d\n",
          PROTOCOL_RLC_AM_CTXT_ARGS(ctxt_pP,rlc_pP),
          nb_bytes_to_transmit,
          rlc_pP->sdu_buffer_occupancy);

    // pdu_p management
    if (!pdu_mem_p) {
      if (rlc_pP->nb_sdu_no_segmented <= 1) {
        max_li_overhead = 0;
      } else {
        max_li_overhead = (((rlc_pP->nb_sdu_no_segmented - 1) * 3) / 2) + ((rlc_pP->nb_sdu_no_segmented - 1) % 2);
      }

      LOG_T(RLC, PROTOCOL_RLC_AM_CTXT_FMT"[SEGMENT] max_li_overhead %d\n",
            PROTOCOL_RLC_AM_CTXT_ARGS(ctxt_pP,rlc_pP),
            max_li_overhead);

      if  (nb_bytes_to_transmit >= (rlc_pP->sdu_buffer_occupancy + RLC_AM_HEADER_MIN_SIZE + max_li_overhead)) {
        data_pdu_size = rlc_pP->sdu_buffer_occupancy + RLC_AM_HEADER_MIN_SIZE + max_li_overhead;
        LOG_T(RLC, PROTOCOL_RLC_AM_CTXT_FMT"[SEGMENT] alloc PDU size %d bytes to contain not all bytes requested by MAC but all BO of RLC@1\n",
              PROTOCOL_RLC_AM_CTXT_ARGS(ctxt_pP,rlc_pP),
              data_pdu_size);
      } else {
        data_pdu_size = nb_bytes_to_transmit;
        LOG_T(RLC, PROTOCOL_RLC_AM_CTXT_FMT"[SEGMENT] alloc PDU size %d bytes to contain all bytes requested by MAC@1\n",
              PROTOCOL_RLC_AM_CTXT_ARGS(ctxt_pP,rlc_pP),
              data_pdu_size);
      }

      if (!(pdu_mem_p = get_free_mem_block (data_pdu_size + sizeof(struct mac_tb_req)))) {
        LOG_C(RLC, PROTOCOL_RLC_AM_CTXT_FMT"[SEGMENT] ERROR COULD NOT GET NEW PDU, EXIT\n",
              PROTOCOL_RLC_AM_CTXT_ARGS(ctxt_pP,rlc_pP));
        RLC_AM_MUTEX_UNLOCK(&rlc_pP->lock_input_sdus);
        return;
      }

      LOG_T(RLC, PROTOCOL_RLC_AM_CTXT_FMT"[SEGMENT] get new PDU %d bytes\n",
            PROTOCOL_RLC_AM_CTXT_ARGS(ctxt_pP,rlc_pP),
            data_pdu_size);
      pdu_remaining_size = data_pdu_size - RLC_AM_HEADER_MIN_SIZE;
      pdu_p        = (rlc_am_pdu_sn_10_t*) (&pdu_mem_p->data[sizeof(struct mac_tb_req)]);
      pdu_tb_req_p = (struct mac_tb_req*) (pdu_mem_p->data);
      pdu_mngt_p   = &rlc_pP->pdu_retrans_buffer[rlc_pP->vt_s % RLC_AM_PDU_RETRANSMISSION_BUFFER_SIZE];
      memset(pdu_mngt_p, 0, sizeof (rlc_am_tx_data_pdu_management_t));

      memset (pdu_mem_p->data, 0, sizeof (rlc_am_pdu_sn_10_t)+sizeof(struct mac_tb_req));
      li_length_in_bytes = 1;
    }

    //----------------------------------------
    // compute how many SDUS can fill the PDU
    //----------------------------------------
    continue_fill_pdu_with_sdu = 1;
    num_fill_sdu               = 0;
    test_num_li                = 0;
    sdu_buffer_index           = rlc_pP->current_sdu_index;
    test_pdu_remaining_size    = pdu_remaining_size;
    test_li_length_in_bytes    = 1;
    test_remaining_size_to_substract   = 0;
    test_remaining_num_li_to_substract = 0;


    while ((rlc_pP->input_sdus[sdu_buffer_index].mem_block) && (continue_fill_pdu_with_sdu > 0)) {
      sdu_mngt_p = &rlc_pP->input_sdus[sdu_buffer_index];

      if (sdu_mngt_p->sdu_remaining_size > test_pdu_remaining_size) {
        // no LI
        continue_fill_pdu_with_sdu = 0;
        num_fill_sdu += 1;
        test_pdu_remaining_size = 0;
        test_remaining_size_to_substract = 0;
        test_remaining_num_li_to_substract = 0;
      } else if (sdu_mngt_p->sdu_remaining_size == test_pdu_remaining_size) {
        // fi will indicate end of PDU is end of SDU, no need for LI
        continue_fill_pdu_with_sdu = 0;
        num_fill_sdu += 1;
        test_pdu_remaining_size = 0;
        test_remaining_size_to_substract = 0;
        test_remaining_num_li_to_substract = 0;
      } else if ((sdu_mngt_p->sdu_remaining_size + (test_li_length_in_bytes ^ 3)) == test_pdu_remaining_size ) {
        // no LI
        continue_fill_pdu_with_sdu = 0;
        num_fill_sdu += 1;
        test_pdu_remaining_size = 0;
        test_remaining_size_to_substract = 0;
        test_remaining_num_li_to_substract = 0;
        pdu_remaining_size = pdu_remaining_size - (test_li_length_in_bytes ^ 3);
      } else if ((sdu_mngt_p->sdu_remaining_size + (test_li_length_in_bytes ^ 3)) < test_pdu_remaining_size ) {
        if (pdu_mngt_p->nb_sdus >= (RLC_AM_MAX_SDU_IN_PDU-1)) {
          continue_fill_pdu_with_sdu = 0;
          //num_fill_sdu += 1;
          test_pdu_remaining_size = 0;
          test_remaining_size_to_substract = 0;
          test_remaining_num_li_to_substract = 0;
          pdu_remaining_size = pdu_remaining_size - 1;
        } else {
          test_num_li += 1;
          num_fill_sdu += 1;
          test_pdu_remaining_size = test_pdu_remaining_size - (sdu_mngt_p->sdu_remaining_size + (test_li_length_in_bytes ^ 3));
          test_remaining_size_to_substract = test_li_length_in_bytes ^ 3;
          test_remaining_num_li_to_substract = 1;
          test_li_length_in_bytes = test_li_length_in_bytes ^ 3;
        }
      } else {
        LOG_T(RLC, PROTOCOL_RLC_AM_CTXT_FMT"[SEGMENT] sdu_mngt_p->sdu_remaining_size=%d test_pdu_remaining_size=%d test_li_length_in_bytes=%d\n",
              PROTOCOL_RLC_AM_CTXT_ARGS(ctxt_pP,rlc_pP),
              sdu_mngt_p->sdu_remaining_size,
              test_pdu_remaining_size,
              test_li_length_in_bytes ^ 3);
        // reduce the size of the PDU
        continue_fill_pdu_with_sdu = 0;
        num_fill_sdu += 1;
        test_pdu_remaining_size = 0;
        test_remaining_size_to_substract = 0;
        test_remaining_num_li_to_substract = 0;
        pdu_remaining_size = pdu_remaining_size - 1;
      }

      pdu_mngt_p->sdus_index[pdu_mngt_p->nb_sdus++] = sdu_buffer_index;
      sdu_mngt_p->pdus_index[sdu_mngt_p->nb_pdus++] = rlc_pP->vt_s % RLC_AM_PDU_RETRANSMISSION_BUFFER_SIZE;
      assert(sdu_mngt_p->nb_pdus < RLC_AM_MAX_SDU_FRAGMENTS);
      sdu_buffer_index = (sdu_buffer_index + 1) % RLC_AM_SDU_CONTROL_BUFFER_SIZE;
    }

    if (test_remaining_num_li_to_substract > 0) {
      // there is a LI that is not necessary
      test_num_li = test_num_li - 1;
      pdu_remaining_size = pdu_remaining_size - test_remaining_size_to_substract;
    }

    //----------------------------------------
    // Do the real filling of the pdu_p
    //----------------------------------------
    LOG_T(RLC, PROTOCOL_RLC_AM_CTXT_FMT" data shift %d Bytes num_li %d\n",
          PROTOCOL_RLC_AM_CTXT_ARGS(ctxt_pP,rlc_pP),
          ((test_num_li*3) +1) >> 1,
          test_num_li);
    data = ((unsigned char*)(&pdu_p->data[((test_num_li*3) +1) >> 1]));
    pdu_mngt_p->payload = data;
    e_li_p = (rlc_am_e_li_t*)(pdu_p->data);
    continue_fill_pdu_with_sdu          = 1;
    li_length_in_bytes                  = 1;
    fill_num_li                         = 0;
    fi_first_byte_pdu_is_first_byte_sdu = 0;
    fi_last_byte_pdu_is_last_byte_sdu   = 0;

    if (rlc_pP->input_sdus[rlc_pP->current_sdu_index].sdu_remaining_size ==
        rlc_pP->input_sdus[rlc_pP->current_sdu_index].sdu_size) {
      fi_first_byte_pdu_is_first_byte_sdu = 1;
    }

    while ((rlc_pP->input_sdus[rlc_pP->current_sdu_index].mem_block) && (continue_fill_pdu_with_sdu > 0)) {
      sdu_mngt_p = &rlc_pP->input_sdus[rlc_pP->current_sdu_index];

      if (sdu_mngt_p->sdu_segmented_size == 0) {
        LOG_T(RLC, PROTOCOL_RLC_AM_CTXT_FMT"[SEGMENT] GET NEW SDU %p AVAILABLE SIZE %d Bytes\n",
              PROTOCOL_RLC_AM_CTXT_ARGS(ctxt_pP,rlc_pP),
              sdu_mngt_p,
              sdu_mngt_p->sdu_remaining_size);
      } else {
        LOG_T(RLC, PROTOCOL_RLC_AM_CTXT_FMT"[SEGMENT] GET AGAIN SDU %p REMAINING AVAILABLE SIZE %d Bytes / %d Bytes LENGTH \n",
              PROTOCOL_RLC_AM_CTXT_ARGS(ctxt_pP,rlc_pP),
              sdu_mngt_p,
              sdu_mngt_p->sdu_remaining_size,
              sdu_mngt_p->sdu_size);
      }

      data_sdu_p = &sdu_mngt_p->first_byte[sdu_mngt_p->sdu_segmented_size];

      if (sdu_mngt_p->sdu_remaining_size > pdu_remaining_size) {
        LOG_T(RLC, PROTOCOL_RLC_AM_CTXT_FMT"[SEGMENT] Filling all remaining PDU with %d bytes\n",
              PROTOCOL_RLC_AM_CTXT_ARGS(ctxt_pP,rlc_pP),
              pdu_remaining_size);
        //msg ("[FRAME %05d][%s][RLC_AM][MOD %u/%u][RB %u][SEGMENT] pdu_mem_p %p pdu_p %p pdu_p->data %p data %p data_sdu_p %p pdu_remaining_size %d\n", rlc_pP->module_id, rlc_pP->rb_id, ctxt_pP->frame, pdu_mem_p, pdu_p, pdu_p->data, data, data_sdu_p,pdu_remaining_size);

        memcpy(data, data_sdu_p, pdu_remaining_size);
        pdu_mngt_p->payload_size += pdu_remaining_size;
        sdu_mngt_p->sdu_remaining_size = sdu_mngt_p->sdu_remaining_size - pdu_remaining_size;
        sdu_mngt_p->sdu_segmented_size = sdu_mngt_p->sdu_segmented_size + pdu_remaining_size;
        fi_last_byte_pdu_is_last_byte_sdu = 0;
        // no LI
        rlc_pP->sdu_buffer_occupancy -= pdu_remaining_size;
        continue_fill_pdu_with_sdu = 0;
        pdu_remaining_size = 0;
        LOG_T(RLC, PROTOCOL_RLC_AM_CTXT_FMT"[SEGMENT] sdu_remaining_size %d bytes sdu_segmented_size %d bytes\n",
              PROTOCOL_RLC_AM_CTXT_ARGS(ctxt_pP,rlc_pP),
              sdu_mngt_p->sdu_remaining_size,
              sdu_mngt_p->sdu_segmented_size);
      } else if (sdu_mngt_p->sdu_remaining_size == pdu_remaining_size) {
        LOG_T(RLC, PROTOCOL_RLC_AM_CTXT_FMT"[SEGMENT] Exactly Filling remaining PDU with %d remaining bytes of SDU\n",
              PROTOCOL_RLC_AM_CTXT_ARGS(ctxt_pP,rlc_pP),
              pdu_remaining_size);
        memcpy(data, data_sdu_p, pdu_remaining_size);
        pdu_mngt_p->payload_size += pdu_remaining_size;

        // free SDU
        rlc_pP->sdu_buffer_occupancy -= sdu_mngt_p->sdu_remaining_size;
        rlc_am_free_in_sdu_data(ctxt_pP, rlc_pP, rlc_pP->current_sdu_index);
        //free_mem_block (rlc_pP->input_sdus[rlc_pP->current_sdu_index]);
        //rlc_pP->input_sdus[rlc_pP->current_sdu_index] = NULL;
        //rlc_pP->nb_sdu -= 1;
        rlc_pP->current_sdu_index = (rlc_pP->current_sdu_index + 1) % RLC_AM_SDU_CONTROL_BUFFER_SIZE;

        fi_last_byte_pdu_is_last_byte_sdu = 1;
        // fi will indicate end of PDU is end of SDU, no need for LI
        continue_fill_pdu_with_sdu = 0;
        pdu_remaining_size = 0;
      } else if ((sdu_mngt_p->sdu_remaining_size + (li_length_in_bytes ^ 3)) < pdu_remaining_size ) {
        if (fill_num_li == (RLC_AM_MAX_SDU_IN_PDU - 1)) {
          LOG_T(RLC, PROTOCOL_RLC_AM_CTXT_FMT"[SEGMENT] [SIZE %d] REACHING RLC_AM_MAX_SDU_IN_PDU LIs -> STOP SEGMENTATION FOR THIS PDU SDU\n",
                PROTOCOL_RLC_AM_CTXT_ARGS(ctxt_pP,rlc_pP),
                sdu_mngt_p->sdu_remaining_size);
          memcpy(data, data_sdu_p, sdu_mngt_p->sdu_remaining_size);
          pdu_mngt_p->payload_size += sdu_mngt_p->sdu_remaining_size;
          pdu_remaining_size = 0; //Forced to 0 pdu_remaining_size - sdu_mngt_p->sdu_remaining_size;
          // free SDU
          rlc_pP->sdu_buffer_occupancy -= sdu_mngt_p->sdu_remaining_size;
          rlc_am_free_in_sdu_data(ctxt_pP, rlc_pP, rlc_pP->current_sdu_index);
          //rlc_pP->input_sdus[rlc_pP->current_sdu_index] = NULL;
          //rlc_pP->nb_sdu -= 1;
          rlc_pP->current_sdu_index = (rlc_pP->current_sdu_index + 1) % RLC_AM_SDU_CONTROL_BUFFER_SIZE;

          // reduce the size of the PDU
          continue_fill_pdu_with_sdu = 0;
          fi_last_byte_pdu_is_last_byte_sdu = 1;
        } else {
          LOG_T(RLC, PROTOCOL_RLC_AM_CTXT_FMT"[SEGMENT] Filling  PDU with %d all remaining bytes of SDU\n",
                PROTOCOL_RLC_AM_CTXT_ARGS(ctxt_pP,rlc_pP),
                sdu_mngt_p->sdu_remaining_size);
          memcpy(data, data_sdu_p, sdu_mngt_p->sdu_remaining_size);
          pdu_mngt_p->payload_size += sdu_mngt_p->sdu_remaining_size;
          data = &data[sdu_mngt_p->sdu_remaining_size];
          li_length_in_bytes = li_length_in_bytes ^ 3;
          fill_num_li += 1;

          if (li_length_in_bytes  == 2) {
            if (fill_num_li == test_num_li) {
              //e_li_p->e1  = 0;
              e_li_p->b1 = 0;
            } else {
              //e_li_p->e1  = 1;
              e_li_p->b1 =  0x80;
            }

            //e_li_p->li1 = sdu_mngt_p->sdu_remaining_size;
            e_li_p->b1 = e_li_p->b1 | (sdu_mngt_p->sdu_remaining_size >> 4);
            e_li_p->b2 = sdu_mngt_p->sdu_remaining_size << 4;
            LOG_T(RLC, PROTOCOL_RLC_AM_CTXT_FMT"[SEGMENT] set e_li_p->b1=0x%02X set e_li_p->b2=0x%02X fill_num_li=%d test_num_li=%d\n",
                  PROTOCOL_RLC_AM_CTXT_ARGS(ctxt_pP,rlc_pP),
                  e_li_p->b1,
                  e_li_p->b2,
                  fill_num_li,
                  test_num_li);
          } else {
            if (fill_num_li != test_num_li) {
              //e_li_p->e2  = 1;
              e_li_p->b2  = e_li_p->b2 | 0x08;
            }

            //e_li_p->li2 = sdu_mngt_p->sdu_remaining_size;
            e_li_p->b2 = e_li_p->b2 | (sdu_mngt_p->sdu_remaining_size >> 8);
            e_li_p->b3 = sdu_mngt_p->sdu_remaining_size & 0xFF;
            LOG_T(RLC, PROTOCOL_RLC_AM_CTXT_FMT"[SEGMENT] set e_li_p->b2=0x%02X set e_li_p->b3=0x%02X fill_num_li=%d test_num_li=%d\n",
                  PROTOCOL_RLC_AM_CTXT_ARGS(ctxt_pP,rlc_pP),
                  e_li_p->b2,
                  e_li_p->b3,
                  fill_num_li,
                  test_num_li);
            e_li_p++;
          }

          pdu_remaining_size = pdu_remaining_size - (sdu_mngt_p->sdu_remaining_size + li_length_in_bytes);
          // free SDU
          rlc_pP->sdu_buffer_occupancy  -= sdu_mngt_p->sdu_remaining_size;
          sdu_mngt_p->sdu_remaining_size = 0;

          rlc_am_free_in_sdu_data(ctxt_pP, rlc_pP, rlc_pP->current_sdu_index);
          //free_mem_block (rlc_pP->input_sdus[rlc_pP->current_sdu_index]);
          //rlc_pP->input_sdus[rlc_pP->current_sdu_index] = NULL;
          //rlc_pP->nb_sdu -= 1;
          rlc_pP->current_sdu_index = (rlc_pP->current_sdu_index + 1) % RLC_AM_SDU_CONTROL_BUFFER_SIZE;
        }
      } else {
        LOG_T(RLC, PROTOCOL_RLC_AM_CTXT_FMT"[SEGMENT] Filling  PDU with %d all remaining bytes of SDU and reduce TB size by %d bytes\n",
              PROTOCOL_RLC_AM_CTXT_ARGS(ctxt_pP,rlc_pP),
              sdu_mngt_p->sdu_remaining_size,
              pdu_remaining_size - sdu_mngt_p->sdu_remaining_size);
#if USER_MODE
        assert(1!=1);
#endif
        memcpy(data, data_sdu_p, sdu_mngt_p->sdu_remaining_size);
        pdu_mngt_p->payload_size += sdu_mngt_p->sdu_remaining_size;
        pdu_remaining_size = pdu_remaining_size - sdu_mngt_p->sdu_remaining_size;
        // free SDU
        rlc_pP->sdu_buffer_occupancy -= sdu_mngt_p->sdu_remaining_size;
        rlc_am_free_in_sdu_data(ctxt_pP, rlc_pP, rlc_pP->current_sdu_index);
        //rlc_pP->input_sdus[rlc_pP->current_sdu_index] = NULL;
        //rlc_pP->nb_sdu -= 1;
        rlc_pP->current_sdu_index = (rlc_pP->current_sdu_index + 1) % RLC_AM_SDU_CONTROL_BUFFER_SIZE;

        // reduce the size of the PDU
        continue_fill_pdu_with_sdu = 0;
        fi_last_byte_pdu_is_last_byte_sdu = 1;
      }
    }

    // set framing info
    if (fi_first_byte_pdu_is_first_byte_sdu) {
      fi = 0;
    } else {
      fi = 2;
    }

    if (!fi_last_byte_pdu_is_last_byte_sdu) {
      fi = fi + 1;
    }

    pdu_p->b1 = pdu_p->b1 | (fi << 3);

    // set fist e bit
    if (fill_num_li > 0) {
      pdu_p->b1 = pdu_p->b1 | 0x04;
    }

    LOG_T(RLC, PROTOCOL_RLC_AM_CTXT_FMT"[SEGMENT] SEND PDU SN %04d  SIZE %d BYTES PAYLOAD SIZE %d BYTES\n",
          PROTOCOL_RLC_AM_CTXT_ARGS(ctxt_pP,rlc_pP),
          rlc_pP->vt_s,
          data_pdu_size - pdu_remaining_size,
          pdu_mngt_p->payload_size);

    rlc_pP->stat_tx_data_pdu   += 1;
    rlc_pP->stat_tx_data_bytes += (data_pdu_size - pdu_remaining_size);

    //pdu_p->sn = rlc_pP->vt_s;
    pdu_p->b1 = pdu_p->b1 | 0x80; // DATA/CONTROL field is DATA PDU
    pdu_p->b1 = pdu_p->b1 | (rlc_pP->vt_s >> 8);
    pdu_p->b2 = rlc_pP->vt_s & 0xFF;
    rlc_pP->vt_s = (rlc_pP->vt_s+1) & RLC_AM_SN_MASK;

    pdu_tb_req_p->data_ptr        = (unsigned char*)pdu_p;
    pdu_tb_req_p->tb_size         = data_pdu_size - pdu_remaining_size;
//#warning "why 3000: changed to RLC_SDU_MAX_SIZE "
    assert(pdu_tb_req_p->tb_size < RLC_SDU_MAX_SIZE );
    rlc_am_pdu_polling(ctxt_pP, rlc_pP, pdu_p, pdu_mngt_p->payload_size);

    //list_add_tail_eurecom (pdu_mem_p, &rlc_pP->segmentation_pdu_list);
    pdu_mngt_p->mem_block  = pdu_mem_p;
    pdu_mngt_p->first_byte = (unsigned char*)pdu_p;
    pdu_mngt_p->header_and_payload_size  = data_pdu_size - pdu_remaining_size;
    pdu_mngt_p->retx_count = -1;

    rlc_pP->retrans_num_pdus  += 1;
    rlc_pP->retrans_num_bytes += pdu_mngt_p->header_and_payload_size;

    pdu_p = NULL;
    pdu_mem_p = NULL;

    //nb_bytes_to_transmit = nb_bytes_to_transmit - data_pdu_size;
    nb_bytes_to_transmit = 0; // 1 PDU only

    mem_block_t* copy = rlc_am_retransmit_get_copy (ctxt_pP, rlc_pP, (rlc_pP->vt_s-1) & RLC_AM_SN_MASK);
    list_add_tail_eurecom (copy, &rlc_pP->segmentation_pdu_list);

  }

  RLC_AM_MUTEX_UNLOCK(&rlc_pP->lock_input_sdus);
}
