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

/*! \file ra_procedures.c
 * \brief Routines for UE MAC-layer Random Access procedures (TS 38.321, Release 15)
 * \author R. Knopp, Navid Nikaein, Guido Casati
 * \date 2019
 * \version 0.1
 * \company Eurecom
 * \email: knopp@eurecom.fr navid.nikaein@eurecom.fr, guido.casati@iis.fraunhofer.de
 * \note
 * \warning
 */

#include "mac.h"

/*
#include "common/utils/LOG/vcd_signal_dumper.h"
#include "PHY_INTERFACE/phy_interface_extern.h"
#include "SCHED_UE/sched_UE.h"
#include "COMMON/mac_rrc_primitives.h"
#include "RRC/LTE/rrc_extern.h"
#include "RRC/L2_INTERFACE/openair_rrc_L2_interface.h"
#include "common/utils/LOG/log.h"
#include "UTIL/OPT/opt.h"
#include "OCG.h"
#include "OCG_extern.h"
#include "PHY/LTE_ESTIMATION/lte_estimation.h"*/

/* Tools */
#include "SIMULATION/TOOLS/sim.h"	// for taus

/* RRC */
#include "NR_RACH-ConfigCommon.h"

/* PHY */
#include "PHY/NR_TRANSPORT/nr_transport_common_proto.h"
#include "PHY/defs_common.h"
#include "PHY/defs_nr_common.h"
#include "PHY/NR_UE_ESTIMATION/nr_estimation.h"

/* MAC */
#include "LAYER2/NR_MAC_COMMON/nr_mac_extern.h"
#include "NR_MAC_COMMON/nr_mac.h"
#include "LAYER2/NR_MAC_UE/mac_proto.h"

extern int64_t table_6_3_3_2_3_prachConfig_Index [256][9];

//extern uint8_t  nfapi_mode;

void nr_get_RA_window(NR_UE_MAC_INST_t *mac);

// WIP
// This routine implements Section 5.1.2 (UE Random Access Resource Selection)
// and Section 5.1.3 (Random Access Preamble Transmission) from 3GPP TS 38.321
void nr_get_prach_resources(module_id_t mod_id,
                            int CC_id,
                            uint8_t gNB_id,
                            uint8_t first_Msg3,
                            NR_PRACH_RESOURCES_t *prach_resources,
                            fapi_nr_ul_config_prach_pdu *prach_pdu,
                            NR_RACH_ConfigDedicated_t * rach_ConfigDedicated){

  NR_UE_MAC_INST_t *mac = get_mac_inst(mod_id);
  NR_ServingCellConfigCommon_t *scc = mac->scc;
  NR_RACH_ConfigCommon_t *nr_rach_ConfigCommon;
  NR_RACH_ConfigGeneric_t *rach_ConfigGeneric;

  // NR_BeamFailureRecoveryConfig_t *beam_failure_recovery_config = &mac->RA_BeamFailureRecoveryConfig; // todo

  int messagePowerOffsetGroupB = 0, messageSizeGroupA, PLThreshold, sizeOfRA_PreamblesGroupA = 0, numberOfRA_Preambles, deltaPreamble_Msg3 = 0;
  uint8_t noGroupB = 0, s_id, t_id, f_id, ul_carrier_id, Msg3_size;

  AssertFatal(scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup != NULL, "[UE %d] FATAL nr_rach_ConfigCommon is NULL !!!\n", mod_id);
  nr_rach_ConfigCommon = scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup;
  rach_ConfigGeneric = &nr_rach_ConfigCommon->rach_ConfigGeneric;

  // NR_RSRP_Range_t rsrp_ThresholdSSB; // todo

  ///////////////////////////////////////////////////////////
  //////////* UE Random Access Resource Selection *//////////
  ///////////////////////////////////////////////////////////


  // todo: 
  // - switch initialisation cases
  // -- RA initiated by beam failure recovery operation (subclause 5.17 TS 38.321)
  // --- SSB selection, set prach_resources->ra_PreambleIndex
  // -- RA initiated by PDCCH: ra_preamble_index provided by PDCCH && ra_PreambleIndex != 0b000000 
  // --- set PREAMBLE_INDEX to ra_preamble_index
  // --- select the SSB signalled by PDCCH
  // -- RA initiated for SI request:
  // --- SSB selection, set prach_resources->ra_PreambleIndex

  if (rach_ConfigDedicated) {
    //////////* Contention free RA *//////////
    // - the PRACH preamble for the UE to transmit is set through RRC configuration
    // - this is the default mode in current implementation!
    // Operation for contention-free RA resources when:
    // - available SSB with SS-RSRP above rsrp-ThresholdSSB: SSB selection
    // - available CSI-RS with CSI-RSRP above rsrp-ThresholdCSI-RS: CSI-RS selection
    // - network controlled Mobility
    uint8_t cfra_ssb_resource_idx = 0;
    prach_resources->ra_PreambleIndex = rach_ConfigDedicated->cfra->resources.choice.ssb->ssb_ResourceList.list.array[cfra_ssb_resource_idx]->ra_PreambleIndex;
    LOG_D(MAC, "[RAPROC] - Selected RA preamble index %d for contention-free random access procedure... \n", prach_resources->ra_PreambleIndex);
  } else {
    //////////* Contention-based RA preamble selection *//////////
    // todo:
    // - selection of SSB with SS-RSRP above rsrp-ThresholdSSB else select any SSB
    // - todo determine next available PRACH occasion

    // rsrp_ThresholdSSB = *nr_rach_ConfigCommon->rsrp_ThresholdSSB;

    // Determine the SSB to RACH mapping ratio
    // =======================================
    NR_RACH_ConfigCommon__ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR ssb_perRACH_config = nr_rach_ConfigCommon->ssb_perRACH_OccasionAndCB_PreamblesPerSSB->present;
    boolean_t multiple_ssb_per_ro; // true if more than one or exactly one SSB per RACH occasion, false if more than one RO per SSB
    uint8_t ssb_rach_ratio; // Nb of SSBs per RACH or RACHs per SSB
    long cb_preambles_per_ssb; // Nb of preambles per SSB
    int total_preambles_per_ssb;
    int starting_preamble_nb = 0;
    uint8_t ssb_nb_in_ro;

    switch (ssb_perRACH_config){
      case NR_RACH_ConfigCommon__ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR_oneEighth:
        multiple_ssb_per_ro = false;
        ssb_rach_ratio = 8;
        cb_preambles_per_ssb = 4 * (nr_rach_ConfigCommon->ssb_perRACH_OccasionAndCB_PreamblesPerSSB->choice.oneEighth + 1);
        break;
      case NR_RACH_ConfigCommon__ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR_oneFourth:
        multiple_ssb_per_ro = false;
        ssb_rach_ratio = 4;
        cb_preambles_per_ssb = 4 * (nr_rach_ConfigCommon->ssb_perRACH_OccasionAndCB_PreamblesPerSSB->choice.oneFourth + 1);
        break;
      case NR_RACH_ConfigCommon__ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR_oneHalf:
        multiple_ssb_per_ro = false;
        ssb_rach_ratio = 2;
        cb_preambles_per_ssb = 4 * (nr_rach_ConfigCommon->ssb_perRACH_OccasionAndCB_PreamblesPerSSB->choice.oneHalf + 1);
        break;
      case NR_RACH_ConfigCommon__ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR_one:
        multiple_ssb_per_ro = true;
        ssb_rach_ratio = 1;
        cb_preambles_per_ssb = 4 * (nr_rach_ConfigCommon->ssb_perRACH_OccasionAndCB_PreamblesPerSSB->choice.one + 1);
        break;
      case NR_RACH_ConfigCommon__ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR_two:
        multiple_ssb_per_ro = true;
        ssb_rach_ratio = 2;
        cb_preambles_per_ssb = 4 * (nr_rach_ConfigCommon->ssb_perRACH_OccasionAndCB_PreamblesPerSSB->choice.two + 1);
        break;
      case NR_RACH_ConfigCommon__ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR_four:
        multiple_ssb_per_ro = true;
        ssb_rach_ratio = 4;
        cb_preambles_per_ssb = nr_rach_ConfigCommon->ssb_perRACH_OccasionAndCB_PreamblesPerSSB->choice.four;
        break;
      case NR_RACH_ConfigCommon__ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR_eight:
        multiple_ssb_per_ro = true;
        ssb_rach_ratio = 8;
        cb_preambles_per_ssb = nr_rach_ConfigCommon->ssb_perRACH_OccasionAndCB_PreamblesPerSSB->choice.eight;
        break;
      case NR_RACH_ConfigCommon__ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR_sixteen:
        multiple_ssb_per_ro = true;
        ssb_rach_ratio = 16;
        cb_preambles_per_ssb = nr_rach_ConfigCommon->ssb_perRACH_OccasionAndCB_PreamblesPerSSB->choice.sixteen;
        break;
      default:
        AssertFatal(1 == 0, "Unsupported ssb_perRACH_config %d\n", ssb_perRACH_config);
        break;
    }

    Msg3_size = mac->RA_Msg3_size;

    numberOfRA_Preambles = 64;
    if(nr_rach_ConfigCommon->totalNumberOfRA_Preambles != NULL)
      numberOfRA_Preambles = *(nr_rach_ConfigCommon->totalNumberOfRA_Preambles);

    // Compute the proper Preamble selection params according to the selected SSB and the ssb_perRACH_OccasionAndCB_PreamblesPerSSB configuration
    if ((true == multiple_ssb_per_ro) &&
        (ssb_rach_ratio > 1)) {
      total_preambles_per_ssb = numberOfRA_Preambles / ssb_rach_ratio;

      ssb_nb_in_ro = prach_pdu->ssb_nb_in_ro;
      starting_preamble_nb = total_preambles_per_ssb * ssb_nb_in_ro;
    }
    else {
      total_preambles_per_ssb = numberOfRA_Preambles;
      starting_preamble_nb = 0;
    }

    if (!nr_rach_ConfigCommon->groupBconfigured) {
      noGroupB = 1;
    } else {
      // RA preambles group B is configured 
      // - Defining the number of RA preambles in RA Preamble Group A for each SSB */
      sizeOfRA_PreamblesGroupA = nr_rach_ConfigCommon->groupBconfigured->numberOfRA_PreamblesGroupA;
      switch (nr_rach_ConfigCommon->groupBconfigured->ra_Msg3SizeGroupA){
      /* - Threshold to determine the groups of RA preambles */
      case 0:
      messageSizeGroupA = 56;
      break;
      case 1:
      messageSizeGroupA = 144;
      break;
      case 2:
      messageSizeGroupA = 208;
      break;
      case 3:
      messageSizeGroupA = 256;
      break;
      case 4:
      messageSizeGroupA = 282;
      break;
      case 5:
      messageSizeGroupA = 480;
      break;
      case 6:
      messageSizeGroupA = 640;
      break;
      case 7:
      messageSizeGroupA = 800;
      break;
      case 8:
      messageSizeGroupA = 1000;
      break;
      case 9:
      messageSizeGroupA = 72;
      break;
      default:
      AssertFatal(1 == 0,"Unknown ra_Msg3SizeGroupA %lu\n", nr_rach_ConfigCommon->groupBconfigured->ra_Msg3SizeGroupA);
      /* todo cases 10 -15*/
      }

      /* Power offset for preamble selection in dB */
      messagePowerOffsetGroupB = -9999;
      switch (nr_rach_ConfigCommon->groupBconfigured->messagePowerOffsetGroupB){
      case 0:
      messagePowerOffsetGroupB = -9999;
      break;
      case 1:
      messagePowerOffsetGroupB = 0;
      break;
      case 2:
      messagePowerOffsetGroupB = 5;
      break;
      case 3:
      messagePowerOffsetGroupB = 8;
      break;
      case 4:
      messagePowerOffsetGroupB = 10;
      break;
      case 5:
      messagePowerOffsetGroupB = 12;
      break;
      case 6:
      messagePowerOffsetGroupB = 15;
      break;
      case 7:
      messagePowerOffsetGroupB = 18;
      break;
      default:
      AssertFatal(1 == 0,"Unknown messagePowerOffsetGroupB %lu\n", nr_rach_ConfigCommon->groupBconfigured->messagePowerOffsetGroupB);
      }

      // todo Msg3-DeltaPreamble should be provided from higher layers, otherwise is 0
      mac->deltaPreamble_Msg3 = 0;
      deltaPreamble_Msg3 = mac->deltaPreamble_Msg3;
    }

    PLThreshold = prach_resources->RA_PCMAX - rach_ConfigGeneric->preambleReceivedTargetPower - deltaPreamble_Msg3 - messagePowerOffsetGroupB;

    /* Msg3 has not been transmitted yet */
    if (first_Msg3 == 1) {
      if (noGroupB == 1) {
        // use Group A preamble
        prach_resources->ra_PreambleIndex = starting_preamble_nb + ((taus()) % cb_preambles_per_ssb);
        mac->RA_usedGroupA = 1;
      } else if ((Msg3_size < messageSizeGroupA) && (get_nr_PL(mod_id, CC_id, gNB_id) > PLThreshold)) {
        // Group B is configured and RA preamble Group A is used
        // - todo add condition on CCCH_sdu_size for initiation by CCCH
        prach_resources->ra_PreambleIndex = starting_preamble_nb + ((taus()) % sizeOfRA_PreamblesGroupA);
        mac->RA_usedGroupA = 1;
      } else {
        // Group B preamble is configured and used
        // the first sizeOfRA_PreamblesGroupA RA preambles belong to RA Preambles Group A
        // the remaining belong to RA Preambles Group B
        prach_resources->ra_PreambleIndex = starting_preamble_nb + sizeOfRA_PreamblesGroupA + ((taus()) % (cb_preambles_per_ssb - sizeOfRA_PreamblesGroupA));
        mac->RA_usedGroupA = 0;
      }
    } else { // Msg3 is being retransmitted
      if (mac->RA_usedGroupA == 1 && noGroupB == 1) {
        prach_resources->ra_PreambleIndex = starting_preamble_nb + ((taus()) % cb_preambles_per_ssb);
      } else if (mac->RA_usedGroupA == 1 && noGroupB == 0){
        prach_resources->ra_PreambleIndex = starting_preamble_nb + ((taus()) % sizeOfRA_PreamblesGroupA);
      } else {
        prach_resources->ra_PreambleIndex = starting_preamble_nb + sizeOfRA_PreamblesGroupA + ((taus()) % (cb_preambles_per_ssb - sizeOfRA_PreamblesGroupA));
      }
    }

    LOG_D(MAC, "[RAPROC] - Selected RA preamble index %d for contention-based random access procedure... \n", prach_resources->ra_PreambleIndex);
  }

  // todo determine next available PRACH occasion
  // - if RA initiated for SI request and ra_AssociationPeriodIndex and si-RequestPeriod are configured
  // - else if SSB is selected above
  // - else if CSI-RS is selected above

  /////////////////////////////////////////////////////////////////////////////
  //////////* Random Access Preamble Transmission (5.1.3 TS 38.321) *//////////
  /////////////////////////////////////////////////////////////////////////////
  // todo:
  // - condition on notification of suspending power ramping counter from lower layer (5.1.3 TS 38.321)
  // - check if SSB or CSI-RS have not changed since the selection in the last RA Preamble tranmission
  // - Extend RA_rnti computation (e.g. f_id selection, ul_carrier_id are hardcoded)

  if (mac->RA_PREAMBLE_TRANSMISSION_COUNTER > 1)
    mac->RA_PREAMBLE_POWER_RAMPING_COUNTER++;

  prach_resources->ra_PREAMBLE_RECEIVED_TARGET_POWER = nr_get_Po_NOMINAL_PUSCH(prach_resources, mod_id, CC_id);

   // RA-RNTI computation (associated to PRACH occasion in which the RA Preamble is transmitted)
   // 1) this does not apply to contention-free RA Preamble for beam failure recovery request
   // 2) getting star_symb, SFN_nbr from table 6.3.3.2-3 (TDD and FR1 scenario)

   // ra_RNTI computation
   // - ul_carrier_id: UL carrier used for RA preamble transmission, hardcoded for NUL carrier
   // - f_id: index of the PRACH occasion in the frequency domain
   // - s_id is starting symbol of the PRACH occasion [0...14]
   // - t_id is the first slot of the PRACH occasion in a system frame [0...80]
   ul_carrier_id = 0; // NUL
   f_id = prach_pdu->num_ra;
   t_id = prach_pdu->prach_slot;
   s_id = prach_pdu->prach_start_symbol;

   prach_resources->ra_RNTI = 1 + s_id + 14 * t_id + 1120 * f_id + 8960 * ul_carrier_id;
   mac->ra_rnti = prach_resources->ra_RNTI;

   LOG_D(MAC, "Computed ra_RNTI is %x \n", prach_resources->ra_RNTI);
}

// TbD: RA_attempt_number not used
void nr_Msg1_transmitted(module_id_t mod_id, uint8_t CC_id, frame_t frameP, uint8_t gNB_id){
  AssertFatal(CC_id == 0, "Transmission on secondary CCs is not supported yet\n");
  NR_UE_MAC_INST_t *mac = get_mac_inst(mod_id);
  mac->ra_state = WAIT_RAR;
  // Start contention resolution timer
  mac->RA_attempt_number++;
}

void nr_Msg3_transmitted(module_id_t mod_id, uint8_t CC_id, frame_t frameP, uint8_t gNB_id){
  AssertFatal(CC_id == 0, "Transmission on secondary CCs is not supported yet\n");
  LOG_D(MAC,"[UE %d][RAPROC] Frame %d : Msg3_tx: Starting contention resolution timer\n", mod_id, frameP);
  NR_UE_MAC_INST_t *mac = get_mac_inst(mod_id);
  // start contention resolution timer
  mac->RA_contention_resolution_cnt = 0;
  mac->RA_contention_resolution_timer_active = 1;
}

/////////////////////////////////////////////////////////////////////////
///////* Random Access Preamble Initialization (5.1.1 TS 38.321) *///////
/////////////////////////////////////////////////////////////////////////
/// Handling inizialization by PDCCH order, MAC entity or RRC (TS 38.300)
/// Only one RA procedure is ongoing at any point in time in a MAC entity
/// the RA procedure on a SCell shall only be initiated by PDCCH order
/// in the current implementation, RA is contention free only

// WIP
// todo TS 38.321:
// - check if carrier to use is explicitly signalled then do (1) RA CARRIER SELECTION (SUL, NUL) (2) set PCMAX
// - BWP operation (subclause 5.15 TS 38.321)
// - handle initialization by beam failure recovery
// - handle initialization by handover
// - handle transmission on DCCH using PRACH (during handover, or sending SR for example)
// - take into account MAC CEs in size_sdu (currently hardcoded size to 1 MAC subPDU and 1 padding subheader)
// - fix rrc data req logic
// - retrieve TBS
// - add mac_rrc_nr_data_req_ue, etc ...
// - add the backoff condition here if we have it from a previous RA reponse which failed (i.e. backoff indicator)

uint8_t nr_ue_get_rach(NR_PRACH_RESOURCES_t *prach_resources,
                       fapi_nr_ul_config_prach_pdu *prach_pdu,
                       module_id_t mod_id,
                       int CC_id,
                       UE_MODE_t UE_mode,
                       frame_t frame,
                       uint8_t gNB_id,
                       int nr_slot_tx){

  NR_UE_MAC_INST_t *mac = get_mac_inst(mod_id);
  uint8_t mac_sdus[MAX_NR_ULSCH_PAYLOAD_BYTES];
  uint8_t lcid = UL_SCH_LCID_CCCH_MSG3, *payload;
  //uint8_t ra_ResponseWindow;
  uint16_t size_sdu = 0;
  unsigned short post_padding;
  //fapi_nr_config_request_t *cfg = &mac->phy_config.config_req;
  NR_ServingCellConfigCommon_t *scc = mac->scc;
  NR_RACH_ConfigCommon_t *setup = scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup;
  NR_RACH_ConfigGeneric_t *rach_ConfigGeneric = &setup->rach_ConfigGeneric;
  //NR_FrequencyInfoDL_t *frequencyInfoDL = scc->downlinkConfigCommon->frequencyInfoDL;
  NR_RACH_ConfigDedicated_t *rach_ConfigDedicated = mac->rach_ConfigDedicated;

  // int32_t frame_diff = 0;

  uint8_t sdu_lcids[NB_RB_MAX] = {0};
  uint16_t sdu_lengths[NB_RB_MAX] = {0};
  int TBS_bytes = 848, header_length_total=0, num_sdus, offset, preambleTransMax, mac_ce_len;

  AssertFatal(CC_id == 0,"Transmission on secondary CCs is not supported yet\n");

  if (prach_resources->init_msg1) {

    LOG_D(MAC, "nr_ue_get_rach, RA_active value: %d", mac->RA_active);

    AssertFatal(setup != NULL, "[UE %d] FATAL nr_rach_ConfigCommon is NULL !!!\n", mod_id);

    if (mac->RA_active == 0) {
      /* RA not active - checking if RRC is ready to initiate the RA procedure */

      LOG_I(MAC, "RA not active. Starting RA preamble initialization.\n");

      mac->RA_RAPID_found = 0;

      /* Set RA_PREAMBLE_POWER_RAMPING_STEP */
      switch (rach_ConfigGeneric->powerRampingStep){ // in dB
       case 0:
       prach_resources->RA_PREAMBLE_POWER_RAMPING_STEP = 0;
       break;
       case 1:
       prach_resources->RA_PREAMBLE_POWER_RAMPING_STEP = 2;
       break;
       case 2:
       prach_resources->RA_PREAMBLE_POWER_RAMPING_STEP = 4;
       break;
       case 3:
       prach_resources->RA_PREAMBLE_POWER_RAMPING_STEP = 6;
       break;
      }

      prach_resources->RA_PREAMBLE_BACKOFF = 0;
      prach_resources->RA_SCALING_FACTOR_BI = 1;
      prach_resources->RA_PCMAX = 0; // currently hardcoded to 0

      payload = (uint8_t*) &mac->CCCH_pdu.payload;

      mac_ce_len = 0;
      num_sdus = 1;
      post_padding = 1;

      if (0){
        // initialisation by RRC
        // CCCH PDU
        // size_sdu = (uint16_t) mac_rrc_data_req_ue(mod_id,
        //                                           CC_id,
        //                                           frame,
        //                                           CCCH,
        //                                           1,
        //                                           mac_sdus,
        //                                           gNB_id,
        //                                           0);
        LOG_D(MAC,"[UE %d] Frame %d: Requested RRCConnectionRequest, got %d bytes\n", mod_id, frame, size_sdu);
      } else {
        // fill ulsch_buffer with random data
        for (int i = 0; i < TBS_bytes; i++){
          mac_sdus[i] = (unsigned char) (lrand48()&0xff);
        }
        //Sending SDUs with size 1
        //Initialize elements of sdu_lcids and sdu_lengths
        sdu_lcids[0] = lcid;
        sdu_lengths[0] = TBS_bytes - 3 - post_padding - mac_ce_len;
        header_length_total += 2 + (sdu_lengths[0] >= 128);
        size_sdu += sdu_lengths[0];
      }

      //mac->RA_tx_frame = frame;
      //mac->RA_tx_slot  = nr_slot_tx;
      //mac->RA_backoff_frame = frame;
      //mac->RA_backoff_slot  = nr_slot_tx;

      if (size_sdu > 0) {

        LOG_I(MAC, "[UE %d] Frame %d: Initialisation Random Access Procedure\n", mod_id, frame);

        mac->RA_PREAMBLE_TRANSMISSION_COUNTER = 1;
        mac->RA_PREAMBLE_POWER_RAMPING_COUNTER = 1;
        mac->RA_Msg3_size = size_sdu + sizeof(NR_MAC_SUBHEADER_SHORT) + sizeof(NR_MAC_SUBHEADER_SHORT);
        mac->RA_prachMaskIndex = 0;
        // todo: add the backoff condition here
        mac->RA_backoff_cnt = 0;
        mac->RA_active = 1;
        prach_resources->Msg3 = payload;

        nr_get_RA_window(mac);

        // Fill in preamble and PRACH resources
        if (mac->generate_nr_prach == 1)
          nr_get_prach_resources(mod_id, CC_id, gNB_id, nr_slot_tx, prach_resources, prach_pdu, rach_ConfigDedicated);

        offset = nr_generate_ulsch_pdu((uint8_t *) mac_sdus,              // sdus buffer
                                       (uint8_t *) payload,               // UL MAC pdu pointer
                                       num_sdus,                          // num sdus
                                       sdu_lengths,                       // sdu length
                                       sdu_lcids,                         // sdu lcid
                                       0,                                 // power headroom
                                       0,                                 // crnti
                                       0,                                 // truncated bsr
                                       0,                                 // short bsr
                                       0,                                 // long_bsr
                                       post_padding,
                                       0);

        // Padding: fill remainder with 0
        if (post_padding > 0){
          for (int j = 0; j < (TBS_bytes - offset); j++)
            payload[offset + j] = 0; // mac_pdu[offset + j] = 0;
        }
      } 
    } else if (mac->RA_window_cnt != -1) { // RACH is active

      ////////////////////////////////////////////////////////////////
      /////* Random Access Response reception (5.1.4 TS 38.321) */////
      ////////////////////////////////////////////////////////////////
      // Handling ra_responseWindow, RA_PREAMBLE_TRANSMISSION_COUNTER
      // and RA_backoff_cnt
      // todo:
      // - handle beam failure recovery request
      // - handle DL assignment on PDCCH for RA-RNTI
      // - handle backoff and raResponseWindow params

      // LOG_D(MAC, "[MAC][UE %d][RAPROC] frame %d, subframe %d: RA Active, window cnt %d (RA_tx_frame %d, RA_tx_subframe %d)\n",
      //   mod_id, frame, nr_slot_tx, mac->RA_window_cnt, mac->RA_tx_frame, mac->RA_tx_subframe);

      if (mac->RA_BI_found){
        prach_resources->RA_PREAMBLE_BACKOFF = prach_resources->RA_SCALING_FACTOR_BI * mac->RA_backoff_indicator;
      } else {
        prach_resources->RA_PREAMBLE_BACKOFF = 0;
      }

      if (mac->RA_window_cnt >= 0 && mac->RA_RAPID_found == 1) {

        // Reset RA_active flag: it disables Msg3 retransmission (8.3 of TS 38.213)
        // TbD Msg3 Retransmissions to be scheduled by DCI 0_0
        mac->RA_active = 0;
        mac->RA_window_cnt = -1;
        mac->ra_state = RA_SUCCEEDED;
        mac->generate_nr_prach = 2;
        LOG_I(MAC, "[MAC][UE %d][RAPROC]: frame %d slot %d RAR successfully received\n", mod_id, frame, nr_slot_tx);

      } else if (mac->RA_window_cnt == 0 && !mac->RA_RAPID_found) {

        LOG_I(MAC, "[MAC][UE %d][RAPROC] Frame %d: nr_slot_tx %d: RAR reception failed \n", mod_id, frame, nr_slot_tx);

        mac->ra_state = RA_UE_IDLE;
        mac->RA_PREAMBLE_TRANSMISSION_COUNTER++;

        preambleTransMax = -1;
        switch (rach_ConfigGeneric->preambleTransMax) {
        case 0:
          preambleTransMax = 3;
          break;
        case 1:
          preambleTransMax = 4;
          break;
        case 2:
          preambleTransMax = 5;
          break;
        case 3:
          preambleTransMax = 6;
          break;
        case 4:
          preambleTransMax = 7;
          break;
        case 5:
          preambleTransMax = 8;
          break;
        case 6:
          preambleTransMax = 10;
          break;
        case 7:
          preambleTransMax = 20;
          break;
        case 8:
          preambleTransMax = 50;
          break;
        case 9:
          preambleTransMax = 100;
          break;
        case 10:
          preambleTransMax = 200;
          break;
        }

        // Resetting RA window
        nr_get_RA_window(mac);

        if (mac->RA_PREAMBLE_TRANSMISSION_COUNTER == preambleTransMax + 1){
          LOG_D(MAC, "[UE %d] Frame %d: Maximum number of RACH attempts (%d)\n", mod_id, frame, preambleTransMax);
          mac->RA_backoff_cnt = rand() % (prach_resources->RA_PREAMBLE_BACKOFF + 1);
          mac->RA_PREAMBLE_TRANSMISSION_COUNTER = 1;
          prach_resources->RA_PREAMBLE_POWER_RAMPING_STEP += prach_resources->RA_PREAMBLE_POWER_RAMPING_STEP << 1; // 2 dB increment
          prach_resources->ra_PREAMBLE_RECEIVED_TARGET_POWER = nr_get_Po_NOMINAL_PUSCH(prach_resources, mod_id, CC_id);
        }

        // compute backoff parameters
        // if (mac->RA_backoff_cnt > 0){
        //   frame_diff = (sframe_t) frame - mac->RA_backoff_frame;
        //   if (frame_diff < 0) frame_diff = -frame_diff;
        //   mac->RA_backoff_frame = frame;
        //   mac->RA_backoff_slot  = nr_slot_tx;
        // }
        // compute RA window parameters
        // if (mac->RA_window_cnt > 0){
        //   frame_diff = (frame_t) frame - mac->RA_tx_frame;
        //   if (frame_diff < 0) frame_diff = -frame_diff;
        //   mac->RA_window_cnt -= ((10 * frame_diff) + (nr_slot_tx - mac->RA_tx_subframe));
        //   LOG_D(MAC, "[MAC][UE %d][RAPROC] Frame %d, subframe %d: RA Active, adjusted window cnt %d\n", mod_id, frame, nr_slot_tx, mac->RA_window_cnt);
        // }

        // mac->RA_tx_frame = frame;
        // mac->RA_tx_slot  = nr_slot_tx;

        // Fill in preamble and PRACH resources
        if (mac->generate_nr_prach == 1)
          nr_get_prach_resources(mod_id, CC_id, gNB_id, nr_slot_tx, prach_resources, prach_pdu, rach_ConfigDedicated);

      } else {

        mac->RA_window_cnt--;

        LOG_D(MAC, "[MAC][UE %d][RAPROC][%d.%d]: RAR not received yet (RA window count %d) \n",
          mod_id,
          frame,
          nr_slot_tx,
          mac->RA_window_cnt);

        // Fill in preamble and PRACH resources
        if (mac->generate_nr_prach == 1)
          nr_get_prach_resources(mod_id, CC_id, gNB_id, nr_slot_tx, prach_resources, prach_pdu, rach_ConfigDedicated);

      }
    }
  } else if (UE_mode == PUSCH) {
    LOG_D(MAC, "[UE %d] FATAL: Should not have checked for RACH in PUSCH yet ...", mod_id);
    AssertFatal(1 == 0, "");
  }
 return mac->generate_nr_prach;
}

void nr_get_RA_window(NR_UE_MAC_INST_t *mac){

  uint8_t mu, ra_ResponseWindow;
  NR_ServingCellConfigCommon_t *scc = mac->scc;
  NR_RACH_ConfigCommon_t *setup = scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup;
  NR_RACH_ConfigGeneric_t *rach_ConfigGeneric = &setup->rach_ConfigGeneric;
  NR_FrequencyInfoDL_t *frequencyInfoDL = scc->downlinkConfigCommon->frequencyInfoDL;

  ra_ResponseWindow = rach_ConfigGeneric->ra_ResponseWindow;

  if (setup->msg1_SubcarrierSpacing)
    mu = *setup->msg1_SubcarrierSpacing;
  else
    mu = frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing;

  mac->RA_window_cnt = mac->RA_offset*nr_slots_per_frame[mu]; // taking into account the 2 frames gap introduced by OAI gNB

  switch (ra_ResponseWindow) {
    case NR_RACH_ConfigGeneric__ra_ResponseWindow_sl1:
      mac->RA_window_cnt += 1;
      break;
    case NR_RACH_ConfigGeneric__ra_ResponseWindow_sl2:
      mac->RA_window_cnt += 2;
      break;
    case NR_RACH_ConfigGeneric__ra_ResponseWindow_sl4:
      mac->RA_window_cnt += 4;
      break;
    case NR_RACH_ConfigGeneric__ra_ResponseWindow_sl8:
      mac->RA_window_cnt += 8;
      break;
    case NR_RACH_ConfigGeneric__ra_ResponseWindow_sl10:
      mac->RA_window_cnt += 10;
      break;
    case NR_RACH_ConfigGeneric__ra_ResponseWindow_sl20:
      mac->RA_window_cnt += 20;
      break;
    case NR_RACH_ConfigGeneric__ra_ResponseWindow_sl40:
      mac->RA_window_cnt += 40;
      break;
    case NR_RACH_ConfigGeneric__ra_ResponseWindow_sl80:
      mac->RA_window_cnt += 80;
      break;
  }
}
