/*
 * SPDX-License-Identifier: LicenseRef-CSSL-1.0
 */

/*
 * \brief functions for generating dci search procedures based on RRC Serving Cell Group Configuration
 */

#include "mac_proto.h"
#include "mac_defs.h"
#include "assertions.h"
#include "mac_defs.h"
#include "common/utils/nr/nr_common.h"
#include "executables/softmodem-common.h"
#include <stdio.h>

static void fill_dci_search_candidates(const NR_SearchSpace_t *ss, fapi_nr_dl_config_dci_dl_pdu_rel15_t *rel15, const uint32_t Y)
{
  LOG_T(NR_MAC_DCI, "Filling search candidates for DCI\n");

  int i = 0;
  for (int maxL = 16; maxL > 0; maxL >>= 1) {
    int aggregation, max_number_of_candidates;
    find_aggregation_candidates(&aggregation, &max_number_of_candidates, ss, maxL);
    if (max_number_of_candidates == 0)
      continue;
    LOG_T(NR_MAC_DCI, "L %d, max number of candidates %d, aggregation %d\n", maxL, max_number_of_candidates, aggregation);
    int N_cce_sym = 0; // nb of rbs of coreset per symbol
    for (int f = 0; f < 6; f++) {
      for (int t = 0; t < 8; t++) {
        N_cce_sym += ((rel15->coreset.frequency_domain_resource[f] >> t) & 1);
      }
    }
    int N_cces = N_cce_sym * rel15->coreset.duration;
    if (N_cces < aggregation)
      continue;
    for (int j = 0; j < max_number_of_candidates; j++) {
      int first_cce = aggregation * ((Y + ((j * N_cces) / (aggregation * max_number_of_candidates)) + 0) % (N_cces / aggregation));
      LOG_T(NR_MAC_DCI,
            "Candidate %d of %d first_cce %d (L %d N_cces %d Y %d)\n",
            j,
            max_number_of_candidates,
            first_cce,
            aggregation,
            N_cces,
            Y);
      // to avoid storing more than one candidate with the same aggregation and starting CCE (duplicated candidate)
      bool duplicated = false;
      for (int k = 0; k < i; k++) {
        if (rel15->CCE[k] == first_cce && rel15->L[k] == aggregation) {
          duplicated = true;
          LOG_T(NR_MAC_DCI, "Candidate %d of %d is duplicated\n", j, max_number_of_candidates);
        }
      }
      if (!duplicated) {
        rel15->CCE[i] = first_cce;
        rel15->L[i] = aggregation;
        i++;
      }
    }
  }
  rel15->number_of_candidates = i;
}

static NR_ControlResourceSet_t *ue_get_coreset(const NR_BWP_PDCCH_t *config, const int coreset_id)
{
  if (config->commonControlResourceSet && coreset_id == config->commonControlResourceSet->controlResourceSetId)
    return config->commonControlResourceSet;
  NR_ControlResourceSet_t *coreset = NULL;
  for (int i = 0; i < config->list_Coreset.count; i++) {
    if (config->list_Coreset.array[i]->controlResourceSetId == coreset_id) {
      coreset = config->list_Coreset.array[i];
      break;
    }
  }
  AssertFatal(coreset, "Couldn't find coreset with id %d\n", coreset_id);
  return coreset;
}

static void config_dci_pdu(NR_UE_MAC_INST_t *mac,
                           fapi_nr_dl_config_request_t *dl_config,
                           const int rnti_type,
                           const int slot,
                           const NR_SearchSpace_t *ss)
{
  const NR_UE_DL_BWP_t *current_DL_BWP = mac->current_DL_BWP;
  const NR_UE_UL_BWP_t *current_UL_BWP = mac->current_UL_BWP;
  NR_BWP_Id_t dl_bwp_id = current_DL_BWP ? current_DL_BWP->bwp_id : 0;
  NR_BWP_PDCCH_t *pdcch_config = &mac->config_BWP_PDCCH[dl_bwp_id];

  fapi_nr_dl_config_dci_dl_pdu_rel15_t *rel15 = &dl_config->dl_config_list[dl_config->number_pdus].dci_config_pdu.dci_config_rel15;

  const int coreset_id = *ss->controlResourceSetId;
  NR_ControlResourceSet_t *coreset;
  if(coreset_id > 0) {
    coreset = ue_get_coreset(pdcch_config, coreset_id);
    rel15->coreset.CoreSetType = NFAPI_NR_CSET_CONFIG_PDCCH_CONFIG;
    if (coreset->ext1 && coreset->ext1->rb_Offset_r16)
      rel15->coreset.rb_offset = *coreset->ext1->rb_Offset_r16;
    else {
      // first common RB of the first group of 6 PRBs has common RB index equal to
      // 6 * ⌈BWP_start / 6⌉ if rb-Offset is not provided
      int start_common = (current_DL_BWP->BWPStart + 5) / 6 * 6;
      rel15->coreset.rb_offset = start_common - current_DL_BWP->BWPStart;
    }
  } else {
    coreset = mac->coreset0;
    rel15->coreset.CoreSetType = NFAPI_NR_CSET_CONFIG_MIB_SIB1;
    rel15->coreset.rb_offset = 0;
  }

  rel15->coreset.duration = coreset->duration;

  for (int i = 0; i < 6; i++)
    rel15->coreset.frequency_domain_resource[i] = coreset->frequencyDomainResources.buf[i];
  rel15->coreset.CceRegMappingType = coreset->cce_REG_MappingType.present ==
    NR_ControlResourceSet__cce_REG_MappingType_PR_interleaved ? FAPI_NR_CCE_REG_MAPPING_TYPE_INTERLEAVED : FAPI_NR_CCE_REG_MAPPING_TYPE_NON_INTERLEAVED;
  if (rel15->coreset.CceRegMappingType == FAPI_NR_CCE_REG_MAPPING_TYPE_INTERLEAVED) {
    struct NR_ControlResourceSet__cce_REG_MappingType__interleaved *interleaved = coreset->cce_REG_MappingType.choice.interleaved;
    rel15->coreset.RegBundleSize = (interleaved->reg_BundleSize == NR_ControlResourceSet__cce_REG_MappingType__interleaved__reg_BundleSize_n6) ? 6 : (2 + interleaved->reg_BundleSize);
    rel15->coreset.InterleaverSize = (interleaved->interleaverSize == NR_ControlResourceSet__cce_REG_MappingType__interleaved__interleaverSize_n6) ? 6 : (2 + interleaved->interleaverSize);
    rel15->coreset.ShiftIndex = interleaved->shiftIndex != NULL ? *interleaved->shiftIndex : mac->physCellId;
  } else {
    rel15->coreset.RegBundleSize = 0;
    rel15->coreset.InterleaverSize = 0;
    rel15->coreset.ShiftIndex = 0;
  }

  rel15->coreset.precoder_granularity = coreset->precoderGranularity;

  // Scrambling RNTI
  rel15->coreset.scrambling_rnti = 0;
  if (coreset->pdcch_DMRS_ScramblingID) {
    rel15->coreset.pdcch_dmrs_scrambling_id = *coreset->pdcch_DMRS_ScramblingID;
    if (ss->searchSpaceType->present == NR_SearchSpace__searchSpaceType_PR_ue_Specific)
      rel15->coreset.scrambling_rnti = mac->crnti;
  } else {
    rel15->coreset.pdcch_dmrs_scrambling_id = mac->physCellId;
  }

  int temp_num_dci_options = (mac->ra.ra_state == nrRA_WAIT_RAR || rnti_type == TYPE_SI_RNTI_) ? 1 : 2;
  int dci_format[2] = {0};
  if (ss->searchSpaceType->present == NR_SearchSpace__searchSpaceType_PR_ue_Specific) {
    if (ss->searchSpaceType->choice.ue_Specific->dci_Formats ==
        NR_SearchSpace__searchSpaceType__ue_Specific__dci_Formats_formats0_0_And_1_0) {
      dci_format[0] = NR_DL_DCI_FORMAT_1_0;
      dci_format[1] = NR_UL_DCI_FORMAT_0_0;
    }
    else {
      dci_format[0] = NR_DL_DCI_FORMAT_1_1;
      dci_format[1] = NR_UL_DCI_FORMAT_0_1;
    }
  }
  else { // common
    AssertFatal(ss->searchSpaceType->choice.common->dci_Format0_0_AndFormat1_0,
                "Only supporting format 10 and 00 for common SS\n");
    dci_format[0] = NR_DL_DCI_FORMAT_1_0;
    dci_format[1] = NR_UL_DCI_FORMAT_0_0;
  }

  NR_UE_ServingCell_Info_t *sc_info = &mac->sc_info;
  // loop over DCI options and configure resource allocation
  // need to configure mac->def_dci_pdu_rel15 for all possible format options
  for (int i = 0; i < temp_num_dci_options; i++) {
    rel15->ss_type_options[i] = ss->searchSpaceType->present;
    if (dci_format[i] == NR_DL_DCI_FORMAT_1_0 || dci_format[i] == NR_UL_DCI_FORMAT_0_0)
      rel15->dci_format_options[i] = NFAPI_NR_FORMAT_0_0_AND_1_0;
    else
      rel15->dci_format_options[i] = NFAPI_NR_FORMAT_0_1_AND_1_1;
    uint16_t alt_size = 0;
    if(current_DL_BWP) {
      // computing alternative size for padding or truncation
      dci_pdu_rel15_t temp_pdu;
      if (dci_format[i] == NR_DL_DCI_FORMAT_1_0)
        alt_size = nr_dci_size(current_DL_BWP,
                               current_UL_BWP,
                               sc_info,
                               mac->pdsch_HARQ_ACK_Codebook,
                               &temp_pdu,
                               NR_UL_DCI_FORMAT_0_0,
                               rnti_type,
                               coreset,
                               ss->searchSpaceType->present,
                               mac->type0_PDCCH_CSS_config.num_rbs,
                               0);
      if (dci_format[i] == NR_UL_DCI_FORMAT_0_0)
        alt_size = nr_dci_size(current_DL_BWP,
                               current_UL_BWP,
                               sc_info,
                               mac->pdsch_HARQ_ACK_Codebook,
                               &temp_pdu,
                               NR_DL_DCI_FORMAT_1_0,
                               rnti_type,
                               coreset,
                               ss->searchSpaceType->present,
                               mac->type0_PDCCH_CSS_config.num_rbs,
                               0);
    }

    uint16_t dci_size = nr_dci_size(current_DL_BWP,
                                    current_UL_BWP,
                                    sc_info,
                                    mac->pdsch_HARQ_ACK_Codebook,
                                    &mac->def_dci_pdu_rel15[dl_config->slot][dci_format[i]],
                                    dci_format[i],
                                    rnti_type,
                                    coreset,
                                    ss->searchSpaceType->present,
                                    mac->type0_PDCCH_CSS_config.num_rbs,
                                    alt_size);
    if (dci_size == 0)
      return;
    rel15->dci_length_options[i] = dci_size;
  }

  // DCI 0_0 and 1_0 are same size, L1 just needs to look for 1 option
  // L2 decides format based on format indicator in payload
  if (rel15->dci_format_options[0] == NFAPI_NR_FORMAT_0_0_AND_1_0)
    rel15->num_dci_options = 1;
  else
    rel15->num_dci_options = 2;

  rel15->BWPStart = coreset_id == 0 ? mac->type0_PDCCH_CSS_config.cset_start_rb : current_DL_BWP->BWPStart;
  rel15->BWPSize = coreset_id == 0 ? mac->type0_PDCCH_CSS_config.num_rbs : current_DL_BWP->BWPSize;

  uint16_t monitoringSymbolsWithinSlot = 0;
  int sps = 0;

  switch(rnti_type) {
    case TYPE_C_RNTI_:
      // we use DL BWP dedicated
      sps = current_DL_BWP->cyclicprefix ? 12 : 14;
      // for SPS=14 8 MSBs in positions 13 down to 6
      monitoringSymbolsWithinSlot = (ss->monitoringSymbolsWithinSlot->buf[0]<<(sps-8)) | (ss->monitoringSymbolsWithinSlot->buf[1]>>(16-sps));
      rel15->rnti = mac->crnti;
      rel15->SubcarrierSpacing = current_DL_BWP->scs;
      break;
    case TYPE_RA_RNTI_:
      // we use the initial DL BWP
      sps = current_DL_BWP->cyclicprefix == NULL ? 14 : 12;
      monitoringSymbolsWithinSlot = (ss->monitoringSymbolsWithinSlot->buf[0]<<(sps-8)) | (ss->monitoringSymbolsWithinSlot->buf[1]>>(16-sps));
      rel15->rnti = mac->ra.ra_rnti;
      rel15->SubcarrierSpacing = current_DL_BWP->scs;
      break;
    case TYPE_MSGB_RNTI_:
      // we use the initial DL BWP
      sps = current_DL_BWP->cyclicprefix == NULL ? 14 : 12;
      monitoringSymbolsWithinSlot =
          (ss->monitoringSymbolsWithinSlot->buf[0] << (sps - 8)) | (ss->monitoringSymbolsWithinSlot->buf[1] >> (16 - sps));
      rel15->rnti = mac->ra.MsgB_rnti;
      rel15->SubcarrierSpacing = current_DL_BWP->scs;
      break;
    case TYPE_P_RNTI_:
      break;
    case TYPE_CS_RNTI_:
      break;
    case TYPE_TC_RNTI_:
      // we use the initial DL BWP
      sps = current_DL_BWP->cyclicprefix == NULL ? 14 : 12;
      monitoringSymbolsWithinSlot = (ss->monitoringSymbolsWithinSlot->buf[0]<<(sps-8)) | (ss->monitoringSymbolsWithinSlot->buf[1]>>(16-sps));
      rel15->rnti = mac->ra.t_crnti;
      rel15->SubcarrierSpacing = current_DL_BWP->scs;
      break;
    case TYPE_SP_CSI_RNTI_:
      break;
    case TYPE_SI_RNTI_:
      sps = 14;
      // for SPS=14 8 MSBs in positions 13 down to 6
      monitoringSymbolsWithinSlot = (ss->monitoringSymbolsWithinSlot->buf[0]<<(sps-8)) | (ss->monitoringSymbolsWithinSlot->buf[1]>>(16-sps));
      rel15->rnti = SI_RNTI; // SI-RNTI - 3GPP TS 38.321 Table 7.1-1: RNTI values
      rel15->SubcarrierSpacing = mac->mib->subCarrierSpacingCommon;
      if(mac->frequency_range == FR2)
        rel15->SubcarrierSpacing = mac->mib->subCarrierSpacingCommon + 2;
      break;
    case TYPE_SFI_RNTI_:
      break;
    case TYPE_INT_RNTI_:
      break;
    case TYPE_TPC_PUSCH_RNTI_:
      break;
    case TYPE_TPC_PUCCH_RNTI_:
      break;
    case TYPE_TPC_SRS_RNTI_:
      break;
    default:
      break;
  }

  rel15->coreset.StartSymbolBitmap = monitoringSymbolsWithinSlot;
  uint32_t Y = 0;
  if (ss->searchSpaceType->present == NR_SearchSpace__searchSpaceType_PR_ue_Specific)
    Y = get_Y(ss, slot, rel15->rnti);
  fill_dci_search_candidates(ss, rel15, Y);
  // not scheduling DCI reception if there are no candidates
  if (rel15->number_of_candidates == 0) {
    LOG_E(NR_MAC, "No candidates in this DCI, not scheduling it");
    return;
  }

  #ifdef DEBUG_DCI
    for (int i = 0; i < rel15->num_dci_options; i++) {
      LOG_T(NR_MAC_DCI,
            "[DCI_CONFIG] Configure DCI PDU: rnti_type %d BWPSize %d BWPStart %d rel15->SubcarrierSpacing %d rel15->dci_format %d "
            "rel15->dci_length %d sps %d monitoringSymbolsWithinSlot %d \n",
            rnti_type,
            rel15->BWPSize,
            rel15->BWPStart,
            rel15->SubcarrierSpacing,
            rel15->dci_format_options[i],
            rel15->dci_length_options[i],
            sps,
            monitoringSymbolsWithinSlot);
    }
  #endif
  // add DCI
  dl_config->dl_config_list[dl_config->number_pdus].pdu_type = FAPI_NR_DL_CONFIG_TYPE_DCI;
  dl_config->number_pdus += 1;
}

bool is_ss_monitor_occasion(const int frame, const int slot, const int slots_per_frame, const NR_SearchSpace_t *ss)
{
  if (!ss)
    return false;
  const int duration = ss->duration ? *ss->duration : 1;
  bool monitor = false;
  int period, offset;
  get_monitoring_period_offset(ss, &period, &offset);
  // The UE monitors PDCCH candidates for search space set ss for 'duration' consecutive slots
  for (int i = 0; i < duration; i++) {
    if (((frame * slots_per_frame + slot - offset - i) % period) == 0) {
      monitor = true;
      break;
    }
  }
  return monitor;
}

static bool search_space_monitoring_ocasion_other_si(NR_UE_MAC_INST_t *mac,
                                                     const NR_SearchSpace_t *ss,
                                                     const int abs_slot,
                                                     const int frame,
                                                     const int slot,
                                                     const int slots_per_frame)
{
  const int duration = ss->duration ? *ss->duration : 1;
  int period, offset;
  get_monitoring_period_offset(ss, &period, &offset);
  for (int i = 0; i < duration; i++) {
    if (((frame * slots_per_frame + slot - offset - i) % period) == 0) {
      int N = mac->ssb_list.nb_tx_ssb;
      int K = mac->ssb_list.nb_ssb_per_index[mac->mib_ssb];
      // numbering current frame and slot in terms of monitoring occasions in window
      int rel_slot = abs_slot - mac->si_SchedInfo.si_window_start;
      int current_monitor_occasion = (rel_slot % period) + (duration * rel_slot / period);
      return current_monitor_occasion % N == K;
    }
  }

  return false;
}

static bool is_window_valid(NR_UE_MAC_INST_t *mac, int window_slots, int abs_slot, int slots_per_frame)
{
  if (mac->si_SchedInfo.si_window_start == -1) {
    // out of window
    return false;
  } else if ((abs_slot - mac->si_SchedInfo.si_window_start + MAX_FRAME_NUMBER * slots_per_frame)
                 % (MAX_FRAME_NUMBER * slots_per_frame)
             >= window_slots) {
    // window expired
    mac->si_SchedInfo.si_window_start = -1;
    return false;
  }
  return true;
}

static bool monitor_dci_for_other_SI(NR_UE_MAC_INST_t *mac,
                                     const NR_SearchSpace_t *ss,
                                     const int si_idx,
                                     const int slots_per_frame,
                                     const int frame,
                                     const int slot)
{
  // according to 5.2.2.3.2 in 331
  const int abs_slot = frame * slots_per_frame + slot;
  const si_schedinfo_config_t *config = mac->si_SchedInfo.si_SchedInfo_list.array[si_idx];
  const int window_slots = 5 << mac->si_SchedInfo.si_WindowLength;
  const int x = (config->si_WindowPosition - 1) * window_slots;
  const int T = 8 << config->si_Periodicity; // radio frame periodicity

  if (mac->si_SchedInfo.si_window_start == -1) {
    if ((frame % T) == (x / slots_per_frame) && (slot == x % slots_per_frame))
      mac->si_SchedInfo.si_window_start = abs_slot; // in terms of absolute slot number
  }
  if (is_window_valid(mac, window_slots, abs_slot, slots_per_frame))
    return search_space_monitoring_ocasion_other_si(mac, ss, abs_slot, frame, slot, slots_per_frame);

  return false;
}

void update_pdcch_config(NR_UE_MAC_INST_t *mac)
{
  int scs = mac->current_DL_BWP ? mac->current_DL_BWP->scs : mac->numerology;
  const int slots_per_frame = get_slots_per_frame_from_scs(scs);
  int ssb_sc_offset_norm;
  if (mac->ssb_subcarrier_offset < 24 && mac->frequency_range == FR1)
    ssb_sc_offset_norm = mac->ssb_subcarrier_offset >> scs;
  else
    ssb_sc_offset_norm = mac->ssb_subcarrier_offset;
  uint16_t ssb_offset_point_a = (mac->ssb_start_subcarrier - ssb_sc_offset_norm) / 12;
  int ssb_start_symbol = get_ssb_start_symbol(mac->nr_band, scs, mac->mib_ssb);
  get_type0_PDCCH_CSS_config_parameters(&mac->type0_PDCCH_CSS_config,
                                        mac->mib_frame,
                                        mac->mib,
                                        slots_per_frame,
                                        ssb_sc_offset_norm,
                                        ssb_start_symbol,
                                        scs,
                                        mac->frequency_range,
                                        mac->nr_band,
                                        273,  // at this point UE in principle doesn't know the grid size (we assume the largest)
                                        mac->mib_ssb,
                                        1, // If the UE is not configured with a periodicity, the UE assumes a periodicity of a half frame
                                        ssb_offset_point_a);
  if (mac->search_space_zero == NULL)
    mac->search_space_zero = calloc(1, sizeof(*mac->search_space_zero));
  if (mac->coreset0 == NULL)
    mac->coreset0 = calloc(1, sizeof(*mac->coreset0));
  fill_coresetZero(mac->coreset0, &mac->type0_PDCCH_CSS_config);
  fill_searchSpaceZero(mac->search_space_zero, slots_per_frame, &mac->type0_PDCCH_CSS_config);
}

void ue_dci_configuration(NR_UE_MAC_INST_t *mac, fapi_nr_dl_config_request_t *dl_config, const frame_t frame, const int slot)
{
  const NR_UE_DL_BWP_t *current_DL_BWP = mac->current_DL_BWP;
  NR_BWP_Id_t dl_bwp_id = current_DL_BWP ? current_DL_BWP->bwp_id : 0;
  NR_BWP_PDCCH_t *pdcch_config = &mac->config_BWP_PDCCH[dl_bwp_id];
  int scs = current_DL_BWP ? current_DL_BWP->scs : mac->numerology;
  const int slots_per_frame = get_slots_per_frame_from_scs(scs);
  if (mac->get_sib1 || mac->update_pdcch_config) {
    update_pdcch_config(mac);
    mac->update_pdcch_config = false;
  }
  if (mac->get_sib1) {
    bool is_occasion = is_ss_monitor_occasion(frame, slot, slots_per_frame, mac->search_space_zero);
    if (is_occasion) {
      LOG_D(NR_MAC_DCI, "Monitoring DCI for SIB1 in frame %d slot %d\n", frame, slot);
      config_dci_pdu(mac, dl_config, TYPE_SI_RNTI_, slot, mac->search_space_zero);
    }
  }

  for (int i = 0; i < MAX_SI_GROUPS; i++) {
    if (!mac->get_otherSI[i])
      continue;
    // If searchSpaceOtherSystemInformation is set to zero,
    // PDCCH monitoring occasions for SI message reception in SI-window
    // are same as PDCCH monitoring occasions for SIB1
    const NR_SearchSpace_t *ss = get_common_search_space(mac, pdcch_config->otherSI_SS_id == -1 ? 0 : pdcch_config->otherSI_SS_id);
    // TODO configure SI-window
    if (monitor_dci_for_other_SI(mac, ss, i, slots_per_frame, frame, slot)) {
      LOG_D(NR_MAC_DCI, "Monitoring DCI for other SIs in frame %d slot %d\n", frame, slot);
      config_dci_pdu(mac, dl_config, TYPE_SI_RNTI_, slot, ss);
    }
  }
  RA_config_t *ra = &mac->ra;
  if (mac->state == UE_PERFORMING_RA && ra->ra_state >= nrRA_WAIT_RAR) {
    const NR_SearchSpace_t *ra_SS = get_common_search_space(mac, pdcch_config->ra_SS_id);
    // if RA is ongoing use RA search space
    if (is_ss_monitor_occasion(frame, slot, slots_per_frame, ra_SS)) {
      nr_rnti_type_t rnti_type = 0;
      if (ra->ra_type == RA_4_STEP) {
        rnti_type = ra->ra_state == nrRA_WAIT_RAR ? TYPE_RA_RNTI_ : TYPE_TC_RNTI_;
      } else {
        rnti_type = TYPE_MSGB_RNTI_;
      }
      config_dci_pdu(mac, dl_config, rnti_type, slot, ra_SS);
    }
    // If Msg3 has C-RNTI MAC CE, also monitor dedicated search spaces during contention resolution
    // according to TS 38.321 section 5.1.5
    if (ra->ra_state == nrRA_WAIT_CONTENTION_RESOLUTION && mac->msg3_C_RNTI) {
      for (int i = 0; i < pdcch_config->list_SS.count; i++) {
        NR_SearchSpace_t *ss = pdcch_config->list_SS.array[i];
        if (is_ss_monitor_occasion(frame, slot, slots_per_frame, ss))
          config_dci_pdu(mac, dl_config, TYPE_C_RNTI_, slot, ss);
      }
    }
  } else if (mac->state == UE_CONNECTED) {
    /*
      In RRC Connected state, the UE monitors for fallback DCI with C-RNTI in common and UE-specific SS.
      First the UE check all UE specific SS. If no fallback DCI is configured, it configures one of following
      common SS with the shown priority
      1. RA SS
      2. Other SI SS
      3. Paging SS
      4. SIB1 SS

      There is a possibility that gNB could configure multiple UE-specific SS for fallback DCI or/and common SS,
      and in a given slot more than one SS become active. For the moment, we assume such configuration wouldn't happen
      and hence don't handle it for the sake of implementation simplicity.
    */
    bool configured_fallback_dci = false;
    for (int i = 0; i < pdcch_config->list_SS.count; i++) {
      NR_SearchSpace_t *ss = pdcch_config->list_SS.array[i];
      if (is_ss_monitor_occasion(frame, slot, slots_per_frame, ss)) {
        config_dci_pdu(mac, dl_config, TYPE_C_RNTI_, slot, ss);
        configured_fallback_dci |= (ss->searchSpaceType->present == NR_SearchSpace__searchSpaceType_PR_common);
      }
    }
    const NR_SearchSpace_t *css = NULL;
    if (!configured_fallback_dci) {
#define CSS_VALID_CUR_SLOT(CSS) \
  ((CSS > -1) && is_ss_monitor_occasion(frame, slot, slots_per_frame, get_common_search_space(mac, CSS)))
      if (CSS_VALID_CUR_SLOT(pdcch_config->ra_SS_id))
        css = get_common_search_space(mac, pdcch_config->ra_SS_id);
      else if (CSS_VALID_CUR_SLOT(pdcch_config->otherSI_SS_id))
        css = get_common_search_space(mac, pdcch_config->otherSI_SS_id);
      else if (CSS_VALID_CUR_SLOT(pdcch_config->paging_SS_id))
        css = get_common_search_space(mac, pdcch_config->paging_SS_id);
#undef CSS_VALID_CUR_SLOT
      else
        css = mac->search_space_zero;
      AssertFatal(css, "Atleast one CSS should be present in connected state\n");
      config_dci_pdu(mac, dl_config, TYPE_C_RNTI_, slot, css);
    }
  }
}
