/*
 * 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 gNB_scheduler_uci.c
 * \brief MAC procedures related to UCI
 * \date 2020
 * \version 1.0
 * \company Eurecom
 */

#include "LAYER2/MAC/mac.h"
#include "NR_MAC_gNB/nr_mac_gNB.h"
#include "NR_MAC_COMMON/nr_mac_extern.h"
#include "NR_MAC_gNB/mac_proto.h"
#include "common/ran_context.h"

extern RAN_CONTEXT_t RC;

void nr_schedule_pucch(int Mod_idP,
                       int UE_id,
                       int nr_ulmix_slots,
                       frame_t frameP,
                       sub_frame_t slotP) {
  NR_UE_info_t *UE_info = &RC.nrmac[Mod_idP]->UE_info;
  AssertFatal(UE_info->active[UE_id],"Cannot find UE_id %d is not active\n",UE_id);

  for (int k=0; k<nr_ulmix_slots; k++) {
    for (int l=0; l<2; l++) {
      NR_sched_pucch *curr_pucch = &UE_info->UE_sched_ctrl[UE_id].sched_pucch[k][l];
      const uint16_t O_ack = curr_pucch->dai_c;
      const uint16_t O_csi = curr_pucch->csi_bits;
      const uint8_t O_sr = 0; // no SR in PUCCH implemented for now
      if (O_ack + O_csi + O_sr == 0
          || frameP != curr_pucch->frame
          || slotP != curr_pucch->ul_slot)
        continue;

      nfapi_nr_ul_tti_request_t *future_ul_tti_req =
          &RC.nrmac[Mod_idP]->UL_tti_req_ahead[0][curr_pucch->ul_slot];
      AssertFatal(future_ul_tti_req->SFN == curr_pucch->frame
                  && future_ul_tti_req->Slot == curr_pucch->ul_slot,
                  "future UL_tti_req's frame.slot %d.%d does not match PUCCH %d.%d\n",
                  future_ul_tti_req->SFN,
                  future_ul_tti_req->Slot,
                  curr_pucch->frame,
                  curr_pucch->ul_slot);
      future_ul_tti_req->pdus_list[future_ul_tti_req->n_pdus].pdu_type = NFAPI_NR_UL_CONFIG_PUCCH_PDU_TYPE;
      future_ul_tti_req->pdus_list[future_ul_tti_req->n_pdus].pdu_size = sizeof(nfapi_nr_pucch_pdu_t);
      nfapi_nr_pucch_pdu_t *pucch_pdu = &future_ul_tti_req->pdus_list[future_ul_tti_req->n_pdus].pucch_pdu;
      memset(pucch_pdu, 0, sizeof(nfapi_nr_pucch_pdu_t));
      future_ul_tti_req->n_pdus += 1;

      LOG_D(MAC,
            "%4d.%2d Scheduling pucch reception in %4d.%2d: bits SR %d, ACK %d, CSI %d, k %d l %d\n",
            frameP,
            slotP,
            curr_pucch->frame,
            curr_pucch->ul_slot,
            O_sr,
            O_ack,
            O_csi,
            k, l);

      NR_ServingCellConfigCommon_t *scc = RC.nrmac[Mod_idP]->common_channels->ServingCellConfigCommon;
      nr_configure_pucch(pucch_pdu,
                         scc,
                         UE_info->UE_sched_ctrl[UE_id].active_ubwp,
                         UE_info->rnti[UE_id],
                         curr_pucch->resource_indicator,
                         O_csi,
                         O_ack,
                         O_sr);

      memset(&UE_info->UE_sched_ctrl[UE_id].sched_pucch[k][l],
             0,
             sizeof(NR_sched_pucch));
    }
  }
}


//!TODO : same function can be written to handle csi_resources
void compute_csi_bitlen (NR_CellGroupConfig_t *secondaryCellGroup, NR_UE_info_t *UE_info, int UE_id) {
  uint8_t csi_report_id = 0;
  uint8_t csi_resourceidx =0;
  uint8_t csi_ssb_idx =0;

  NR_CSI_MeasConfig_t *csi_MeasConfig = secondaryCellGroup->spCellConfig->spCellConfigDedicated->csi_MeasConfig->choice.setup;
  NR_CSI_ResourceConfigId_t csi_ResourceConfigId;
  for (csi_report_id=0; csi_report_id < csi_MeasConfig->csi_ReportConfigToAddModList->list.count; csi_report_id++){
    csi_ResourceConfigId=csi_MeasConfig->csi_ReportConfigToAddModList->list.array[csi_report_id]->resourcesForChannelMeasurement;
    UE_info->csi_report_template[UE_id][csi_report_id].reportQuantity_type = csi_MeasConfig->csi_ReportConfigToAddModList->list.array[csi_report_id]->reportQuantity.present;

    for ( csi_resourceidx = 0; csi_resourceidx < csi_MeasConfig->csi_ResourceConfigToAddModList->list.count; csi_resourceidx++) {
      if ( csi_MeasConfig->csi_ResourceConfigToAddModList->list.array[csi_resourceidx]->csi_ResourceConfigId != csi_ResourceConfigId)
	continue;
      else {
      //Finding the CSI_RS or SSB Resources
        UE_info->csi_report_template[UE_id][csi_report_id].CSI_Resource_type= csi_MeasConfig->csi_ResourceConfigToAddModList->list.array[csi_resourceidx]->csi_RS_ResourceSetList.present;
        if (NR_CSI_ResourceConfig__csi_RS_ResourceSetList_PR_nzp_CSI_RS_SSB ==UE_info->csi_report_template[UE_id][csi_report_id].CSI_Resource_type){
          struct NR_CSI_ResourceConfig__csi_RS_ResourceSetList__nzp_CSI_RS_SSB * nzp_CSI_RS_SSB = csi_MeasConfig->csi_ResourceConfigToAddModList->list.array[csi_resourceidx]->csi_RS_ResourceSetList.choice.nzp_CSI_RS_SSB;

          UE_info->csi_report_template[UE_id][csi_report_id].nb_of_nzp_csi_report = nzp_CSI_RS_SSB->nzp_CSI_RS_ResourceSetList!=NULL ? nzp_CSI_RS_SSB->nzp_CSI_RS_ResourceSetList->list.count:0;
          UE_info->csi_report_template[UE_id][csi_report_id].nb_of_csi_ssb_report = nzp_CSI_RS_SSB->csi_SSB_ResourceSetList!=NULL ? nzp_CSI_RS_SSB->csi_SSB_ResourceSetList->list.count:0;
        }

        if (0 != UE_info->csi_report_template[UE_id][csi_report_id].nb_of_csi_ssb_report){
	  uint8_t nb_ssb_resources =0;
          for ( csi_ssb_idx = 0; csi_ssb_idx < csi_MeasConfig->csi_SSB_ResourceSetToAddModList->list.count; csi_ssb_idx++) {
            if (csi_MeasConfig->csi_SSB_ResourceSetToAddModList->list.array[csi_ssb_idx]->csi_SSB_ResourceSetId ==
                *(csi_MeasConfig->csi_ResourceConfigToAddModList->list.array[csi_resourceidx]->csi_RS_ResourceSetList.choice.nzp_CSI_RS_SSB->csi_SSB_ResourceSetList->list.array[0])) { 
              ///We can configure only one SSB resource set from spec 38.331 IE CSI-ResourceConfig
              if (NR_CSI_ReportConfig__groupBasedBeamReporting_PR_disabled ==
                csi_MeasConfig->csi_ReportConfigToAddModList->list.array[csi_report_id]->groupBasedBeamReporting.present ) {
	        if (NULL != csi_MeasConfig->csi_ReportConfigToAddModList->list.array[csi_report_id]->groupBasedBeamReporting.choice.disabled->nrofReportedRS)
                  UE_info->csi_report_template[UE_id][csi_report_id].CSI_report_bitlen[0].nb_ssbri_cri = *(csi_MeasConfig->csi_ReportConfigToAddModList->list.array[csi_report_id]->groupBasedBeamReporting.choice.disabled->nrofReportedRS)+1;
                else
                  /*! From Spec 38.331
                  * nrofReportedRS
                  * The number (N) of measured RS resources to be reported per report setting in a non-group-based report. N <= N_max, where N_max is either 2 or 4 depending on UE
                  * capability. FFS: The signaling mechanism for the gNB to select a subset of N beams for the UE to measure and report.
                  * When the field is absent the UE applies the value 1
                  */
                  UE_info->csi_report_template[UE_id][csi_report_id].CSI_report_bitlen[0].nb_ssbri_cri= 1;
              } else
                UE_info->csi_report_template[UE_id][csi_report_id].CSI_report_bitlen[0].nb_ssbri_cri= 2;

              nb_ssb_resources=  csi_MeasConfig->csi_SSB_ResourceSetToAddModList->list.array[csi_ssb_idx]->csi_SSB_ResourceList.list.count;
              if (nb_ssb_resources){
                UE_info->csi_report_template[UE_id][csi_report_id].CSI_report_bitlen[0].cri_ssbri_bitlen =ceil(log2 (nb_ssb_resources));
                UE_info->csi_report_template[UE_id][csi_report_id].CSI_report_bitlen[0].rsrp_bitlen = 7; //From spec 38.212 Table 6.3.1.1.2-6: CRI, SSBRI, and RSRP 
                UE_info->csi_report_template[UE_id][csi_report_id].CSI_report_bitlen[0].diff_rsrp_bitlen =4; //From spec 38.212 Table 6.3.1.1.2-6: CRI, SSBRI, and RSRP
              }
              else{
                UE_info->csi_report_template[UE_id][csi_report_id].CSI_report_bitlen[0].cri_ssbri_bitlen =0;
                UE_info->csi_report_template[UE_id][csi_report_id].CSI_report_bitlen[0].rsrp_bitlen = 0;
                UE_info->csi_report_template[UE_id][csi_report_id].CSI_report_bitlen[0].diff_rsrp_bitlen =0;
              }

              LOG_I (MAC, "UCI: CSI_bit len : ssbri %d, rsrp: %d, diff_rsrp: %d",
                     UE_info->csi_report_template[UE_id][csi_report_id].CSI_report_bitlen[0].cri_ssbri_bitlen,
                     UE_info->csi_report_template[UE_id][csi_report_id].CSI_report_bitlen[0].rsrp_bitlen,
                     UE_info->csi_report_template[UE_id][csi_report_id].CSI_report_bitlen[0].diff_rsrp_bitlen);
              break ;
            }
          }
        }
        if (0 != UE_info->csi_report_template[UE_id][csi_report_id].nb_of_nzp_csi_report)
          AssertFatal(1==0,"Currently configuring only SSB beamreporting.");
        break;
      }
    }
  }
}


uint16_t nr_get_csi_bitlen(int Mod_idP,
                           int UE_id,
                           uint8_t csi_report_id) {

  uint16_t csi_bitlen =0;
  NR_UE_info_t *UE_info = &RC.nrmac[Mod_idP]->UE_info;
  CRI_SSBRI_RSRP_bitlen_t * CSI_report_bitlen = NULL;

  CSI_report_bitlen = &(UE_info->csi_report_template[UE_id][csi_report_id].CSI_report_bitlen[0]);
  csi_bitlen = ((CSI_report_bitlen->cri_ssbri_bitlen * CSI_report_bitlen->nb_ssbri_cri) +
               CSI_report_bitlen->rsrp_bitlen +(CSI_report_bitlen->diff_rsrp_bitlen *
               (CSI_report_bitlen->nb_ssbri_cri -1 )) *UE_info->csi_report_template[UE_id][csi_report_id].nb_of_csi_ssb_report);

  return csi_bitlen;
}


void nr_csi_meas_reporting(int Mod_idP,
                           int UE_id,
                           frame_t frame,
                           sub_frame_t slot,
                           int slots_per_tdd,
                           int ul_slots,
                           int n_slots_frame) {

  NR_UE_info_t *UE_info = &RC.nrmac[Mod_idP]->UE_info;
  NR_sched_pucch *curr_pucch;
  NR_PUCCH_ResourceSet_t *pucchresset;
  NR_CSI_ReportConfig_t *csirep;
  NR_CellGroupConfig_t *secondaryCellGroup = UE_info->secondaryCellGroup[UE_id];
  NR_CSI_MeasConfig_t *csi_measconfig = secondaryCellGroup->spCellConfig->spCellConfigDedicated->csi_MeasConfig->choice.setup;
  NR_BWP_Uplink_t *ubwp=secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList->list.array[0];
  NR_PUCCH_Config_t *pucch_Config = ubwp->bwp_Dedicated->pucch_Config->choice.setup;

  AssertFatal(csi_measconfig->csi_ReportConfigToAddModList->list.count>0,"NO CSI report configuration available");

  for (int csi_report_id = 0; csi_report_id < csi_measconfig->csi_ReportConfigToAddModList->list.count; csi_report_id++){

    csirep = csi_measconfig->csi_ReportConfigToAddModList->list.array[csi_report_id];

    AssertFatal(csirep->reportConfigType.choice.periodic!=NULL,"Only periodic CSI reporting is implemented currently");
    int period, offset, sched_slot;
    csi_period_offset(csirep,&period,&offset);
    sched_slot = (period+offset)%n_slots_frame;
    // prepare to schedule csi measurement reception according to 5.2.1.4 in 38.214
    // preparation is done in first slot of tdd period
    if ( (frame%(period/n_slots_frame)==(offset/n_slots_frame)) && (slot==((sched_slot/slots_per_tdd)*slots_per_tdd))) {

      // we are scheduling pucch for csi in the first pucch occasion (this comes before ack/nack)
      curr_pucch = &UE_info->UE_sched_ctrl[UE_id].sched_pucch[sched_slot-slots_per_tdd+ul_slots][0];

      NR_PUCCH_CSI_Resource_t *pucchcsires = csirep->reportConfigType.choice.periodic->pucch_CSI_ResourceList.list.array[0];

      int found = -1;
      pucchresset = pucch_Config->resourceSetToAddModList->list.array[1]; // set with formats >1
      int n_list = pucchresset->resourceList.list.count;
      for (int i=0; i<n_list; i++) {
        if (*pucchresset->resourceList.list.array[i] == pucchcsires->pucch_Resource)
          found = i;
      }
      AssertFatal(found>-1,"CSI resource not found among PUCCH resources");

      curr_pucch->resource_indicator = found;

      n_list = pucch_Config->resourceToAddModList->list.count;

      // going through the list of PUCCH resources to find the one indexed by resource_id
      for (int i=0; i<n_list; i++) {
        NR_PUCCH_Resource_t *pucchres = pucch_Config->resourceToAddModList->list.array[i];
        if (pucchres->pucch_ResourceId == *pucchresset->resourceList.list.array[found]) {
          switch(pucchres->format.present){
            case NR_PUCCH_Resource__format_PR_format2:
              if (pucch_Config->format2->choice.setup->simultaneousHARQ_ACK_CSI == NULL)
                curr_pucch->simultaneous_harqcsi = false;
              else
                curr_pucch->simultaneous_harqcsi = true;
              break;
            case NR_PUCCH_Resource__format_PR_format3:
              if (pucch_Config->format3->choice.setup->simultaneousHARQ_ACK_CSI == NULL)
                curr_pucch->simultaneous_harqcsi = false;
              else
                curr_pucch->simultaneous_harqcsi = true;
              break;
            case NR_PUCCH_Resource__format_PR_format4:
              if (pucch_Config->format4->choice.setup->simultaneousHARQ_ACK_CSI == NULL)
                curr_pucch->simultaneous_harqcsi = false;
              else
                curr_pucch->simultaneous_harqcsi = true;
              break;
          default:
            AssertFatal(1==0,"Invalid PUCCH format type");
          }
        }
      }
      curr_pucch->csi_bits += nr_get_csi_bitlen(Mod_idP,UE_id,csi_report_id); // TODO function to compute CSI meas report bit size
      curr_pucch->frame = frame;
      curr_pucch->ul_slot = sched_slot;
    }
  }
}


void nr_rx_acknack(nfapi_nr_uci_pusch_pdu_t *uci_pusch,
                   nfapi_nr_uci_pucch_pdu_format_0_1_t *uci_01,
                   nfapi_nr_uci_pucch_pdu_format_2_3_4_t *uci_234,
                   NR_UL_IND_t *UL_info, NR_UE_sched_ctrl_t *sched_ctrl, NR_mac_stats_t *stats) {

  // TODO
  int max_harq_rounds = 4; // TODO define macro

  if (uci_01 != NULL) {
    // handle harq
    int harq_idx_s = 0;

    // iterate over received harq bits
    for (int harq_bit = 0; harq_bit < uci_01->harq->num_harq; harq_bit++) {
      // search for the right harq process
      for (int harq_idx = harq_idx_s; harq_idx < NR_MAX_NB_HARQ_PROCESSES; harq_idx++) {
        // if the gNB received ack with a good confidence
        if ((UL_info->slot-1) == sched_ctrl->harq_processes[harq_idx].feedback_slot) {
          sched_ctrl->harq_processes[harq_idx].feedback_slot = -1;
          if ((uci_01->harq->harq_list[harq_bit].harq_value == 1) &&
              (uci_01->harq->harq_confidence_level == 0)) {
            // toggle NDI and reset round
            sched_ctrl->harq_processes[harq_idx].ndi ^= 1;
            sched_ctrl->harq_processes[harq_idx].round = 0;
          }
          else
            sched_ctrl->harq_processes[harq_idx].round++;
          sched_ctrl->harq_processes[harq_idx].is_waiting = 0;
          harq_idx_s = harq_idx + 1;
          // if the max harq rounds was reached
          if (sched_ctrl->harq_processes[harq_idx].round == max_harq_rounds) {
            sched_ctrl->harq_processes[harq_idx].ndi ^= 1;
            sched_ctrl->harq_processes[harq_idx].round = 0;
            stats->dlsch_errors++;
          }
          break;
        }
        // if feedback slot processing is aborted
        else if (sched_ctrl->harq_processes[harq_idx].feedback_slot != -1
                 && (UL_info->slot-1) > sched_ctrl->harq_processes[harq_idx].feedback_slot
                 && sched_ctrl->harq_processes[harq_idx].is_waiting) {
          sched_ctrl->harq_processes[harq_idx].feedback_slot = -1;
          sched_ctrl->harq_processes[harq_idx].round++;
          if (sched_ctrl->harq_processes[harq_idx].round == max_harq_rounds) {
            sched_ctrl->harq_processes[harq_idx].ndi ^= 1;
            sched_ctrl->harq_processes[harq_idx].round = 0;
          }
          sched_ctrl->harq_processes[harq_idx].is_waiting = 0;
        }
      }
    }
  }


  if (uci_234 != NULL) {
    int harq_idx_s = 0;
    int acknack;

    // iterate over received harq bits
    for (int harq_bit = 0; harq_bit < uci_234->harq.harq_bit_len; harq_bit++) {
      acknack = ((uci_234->harq.harq_payload[harq_bit>>3])>>harq_bit)&0x01;
      for (int harq_idx = harq_idx_s; harq_idx < NR_MAX_NB_HARQ_PROCESSES-1; harq_idx++) {
        // if the gNB received ack with a good confidence or if the max harq rounds was reached
        if ((UL_info->slot-1) == sched_ctrl->harq_processes[harq_idx].feedback_slot) {
          // TODO add some confidence level for when there is no CRC
          sched_ctrl->harq_processes[harq_idx].feedback_slot = -1;
          if ((uci_234->harq.harq_crc != 1) && acknack) {
            // toggle NDI and reset round
            sched_ctrl->harq_processes[harq_idx].ndi ^= 1;
            sched_ctrl->harq_processes[harq_idx].round = 0;
          }
          else
            sched_ctrl->harq_processes[harq_idx].round++;
          sched_ctrl->harq_processes[harq_idx].is_waiting = 0;
          harq_idx_s = harq_idx + 1;
          // if the max harq rounds was reached
          if (sched_ctrl->harq_processes[harq_idx].round == max_harq_rounds) {
            sched_ctrl->harq_processes[harq_idx].ndi ^= 1;
            sched_ctrl->harq_processes[harq_idx].round = 0;
            stats->dlsch_errors++;
          }
          break;
        }
        // if feedback slot processing is aborted
        else if (sched_ctrl->harq_processes[harq_idx].feedback_slot != -1
                 && (UL_info->slot-1) > sched_ctrl->harq_processes[harq_idx].feedback_slot
                 && sched_ctrl->harq_processes[harq_idx].is_waiting) {
          sched_ctrl->harq_processes[harq_idx].feedback_slot = -1;
          sched_ctrl->harq_processes[harq_idx].round++;
          if (sched_ctrl->harq_processes[harq_idx].round == max_harq_rounds) {
            sched_ctrl->harq_processes[harq_idx].ndi ^= 1;
            sched_ctrl->harq_processes[harq_idx].round = 0;
          }
          sched_ctrl->harq_processes[harq_idx].is_waiting = 0;
        }
      }
    }
  }
}


// function to update pucch scheduling parameters in UE list when a USS DL is scheduled
void nr_acknack_scheduling(int Mod_idP,
                           int UE_id,
                           frame_t frameP,
                           sub_frame_t slotP,
                           int slots_per_tdd,
                           int *pucch_id,
                           int *pucch_occ) {

  NR_ServingCellConfigCommon_t *scc = RC.nrmac[Mod_idP]->common_channels->ServingCellConfigCommon;
  NR_UE_info_t *UE_info = &RC.nrmac[Mod_idP]->UE_info;
  NR_sched_pucch *curr_pucch;
  int max_acknacks,pucch_res,first_ul_slot_tdd,k,i,l;
  uint8_t pdsch_to_harq_feedback[8];
  int found = 0;
  int nr_ulmix_slots = scc->tdd_UL_DL_ConfigurationCommon->pattern1.nrofUplinkSlots;
  if (scc->tdd_UL_DL_ConfigurationCommon->pattern1.nrofUplinkSymbols!=0)
    nr_ulmix_slots++;

  bool csi_pres=false;
  for (k=0; k<nr_ulmix_slots; k++) {
    if(UE_info->UE_sched_ctrl[UE_id].sched_pucch[k][0].csi_bits>0)
      csi_pres=true;
  }

  // As a preference always schedule ack nacks in PUCCH0 (max 2 per slots)
  // Unless there is CSI meas reporting scheduled in the period to avoid conflicts in the same slot
  if (csi_pres)
    max_acknacks=10;
  else
    max_acknacks=2;

  // this is hardcoded for now as ue specific
  NR_SearchSpace__searchSpaceType_PR ss_type = NR_SearchSpace__searchSpaceType_PR_ue_Specific;
  get_pdsch_to_harq_feedback(Mod_idP,UE_id,ss_type,pdsch_to_harq_feedback);

  // for each possible ul or mixed slot
  for (k=0; k<nr_ulmix_slots; k++) {
    for (l=0; l<1; l++) { // scheduling 2 PUCCH in a single slot does not work with the phone, currently
      curr_pucch = &UE_info->UE_sched_ctrl[UE_id].sched_pucch[k][l];
      //if it is possible to schedule acknack in current pucch (no exclusive csi pucch)
      if ((curr_pucch->csi_bits == 0) || (curr_pucch->simultaneous_harqcsi==true)) {
        // if there is free room in current pucch structure
        if (curr_pucch->dai_c<max_acknacks) {
          pucch_res = get_pucch_resource(UE_info,UE_id,k,l);
          if (pucch_res>-1){
            curr_pucch->resource_indicator = pucch_res;
            curr_pucch->frame = frameP;
            // first pucch occasion in first UL or MIXED slot
            first_ul_slot_tdd = scc->tdd_UL_DL_ConfigurationCommon->pattern1.nrofDownlinkSlots;
            i = 0;
            while (i<8 && found == 0)  {  // look if timing indicator is among allowed values
              if (pdsch_to_harq_feedback[i]==(first_ul_slot_tdd+k)-(slotP % slots_per_tdd))
                found = 1;
              if (found == 0) i++;
            }
            if (found == 1) {
              // computing slot in which pucch is scheduled
              curr_pucch->dai_c++;
              curr_pucch->ul_slot = first_ul_slot_tdd + k + (slotP - (slotP % slots_per_tdd));
              curr_pucch->timing_indicator = i; // index in the list of timing indicators
              *pucch_id = k;
              *pucch_occ = l;
              return;
            }
          }
        }
      }
    }
  }
  AssertFatal(1==0,"No Uplink slot available in accordance to allowed timing indicator\n");
}


void csi_period_offset(NR_CSI_ReportConfig_t *csirep,
                       int *period, int *offset) {

    NR_CSI_ReportPeriodicityAndOffset_PR p_and_o = csirep->reportConfigType.choice.periodic->reportSlotConfig.present;

    switch(p_and_o){
      case NR_CSI_ReportPeriodicityAndOffset_PR_slots4:
        *period = 4;
        *offset = csirep->reportConfigType.choice.periodic->reportSlotConfig.choice.slots4;
        break;
      case NR_CSI_ReportPeriodicityAndOffset_PR_slots5:
        *period = 5;
        *offset = csirep->reportConfigType.choice.periodic->reportSlotConfig.choice.slots5;
        break;
      case NR_CSI_ReportPeriodicityAndOffset_PR_slots8:
        *period = 8;
        *offset = csirep->reportConfigType.choice.periodic->reportSlotConfig.choice.slots8;
        break;
      case NR_CSI_ReportPeriodicityAndOffset_PR_slots10:
        *period = 10;
        *offset = csirep->reportConfigType.choice.periodic->reportSlotConfig.choice.slots10;
        break;
      case NR_CSI_ReportPeriodicityAndOffset_PR_slots16:
        *period = 16;
        *offset = csirep->reportConfigType.choice.periodic->reportSlotConfig.choice.slots16;
        break;
      case NR_CSI_ReportPeriodicityAndOffset_PR_slots20:
        *period = 20;
        *offset = csirep->reportConfigType.choice.periodic->reportSlotConfig.choice.slots20;
        break;
      case NR_CSI_ReportPeriodicityAndOffset_PR_slots40:
        *period = 40;
        *offset = csirep->reportConfigType.choice.periodic->reportSlotConfig.choice.slots40;
        break;
      case NR_CSI_ReportPeriodicityAndOffset_PR_slots80:
        *period = 80;
        *offset = csirep->reportConfigType.choice.periodic->reportSlotConfig.choice.slots80;
        break;
      case NR_CSI_ReportPeriodicityAndOffset_PR_slots160:
        *period = 160;
        *offset = csirep->reportConfigType.choice.periodic->reportSlotConfig.choice.slots160;
        break;
      case NR_CSI_ReportPeriodicityAndOffset_PR_slots320:
        *period = 320;
        *offset = csirep->reportConfigType.choice.periodic->reportSlotConfig.choice.slots320;
        break;
    default:
      AssertFatal(1==0,"No periodicity and offset resource found in CSI report");
    }
}


int get_pucch_resource(NR_UE_info_t *UE_info,int UE_id,int k,int l) {

  // to be updated later, for now simple implementation
  // use the second allocation just in case there is csi in the first
  // in that case use second resource (for a different symbol) see 9.2 in 38.213
  if (l==1) {
    if (UE_info->UE_sched_ctrl[UE_id].sched_pucch[k][0].csi_bits==0)
      return -1;
    else
      return 1;
  }
  else
    return 0;

}


uint16_t compute_pucch_prb_size(uint8_t format,
                                uint8_t nr_prbs,
                                uint16_t O_tot,
                                uint16_t O_csi,
                                NR_PUCCH_MaxCodeRate_t *maxCodeRate,
                                uint8_t Qm,
                                uint8_t n_symb,
                                uint8_t n_re_ctrl) {

  uint16_t O_crc;

  if (O_tot<12)
    O_crc = 0;
  else{
    if (O_tot<20)
      O_crc = 6;
    else {
      if (O_tot<360)
        O_crc = 11;
      else
        AssertFatal(1==0,"Case for segmented PUCCH not yet implemented");
    }
  }

  int rtimes100;
  switch(*maxCodeRate){
    case NR_PUCCH_MaxCodeRate_zeroDot08 :
      rtimes100 = 8;
      break;
    case NR_PUCCH_MaxCodeRate_zeroDot15 :
      rtimes100 = 15;
      break;
    case NR_PUCCH_MaxCodeRate_zeroDot25 :
      rtimes100 = 25;
      break;
    case NR_PUCCH_MaxCodeRate_zeroDot35 :
      rtimes100 = 35;
      break;
    case NR_PUCCH_MaxCodeRate_zeroDot45 :
      rtimes100 = 45;
      break;
    case NR_PUCCH_MaxCodeRate_zeroDot60 :
      rtimes100 = 60;
      break;
    case NR_PUCCH_MaxCodeRate_zeroDot80 :
      rtimes100 = 80;
      break;
  default :
    AssertFatal(1==0,"Invalid MaxCodeRate");
  }

  float r = (float)rtimes100/100;

  if (O_csi == O_tot) {
    if ((O_tot+O_csi)>(nr_prbs*n_re_ctrl*n_symb*Qm*r))
      AssertFatal(1==0,"MaxCodeRate %.2f can't support %d UCI bits and %d CRC bits with %d PRBs",
                  r,O_tot,O_crc,nr_prbs);
    else
      return nr_prbs;
  }

  if (format==2){
    // TODO fix this for multiple CSI reports
    for (int i=1; i<=nr_prbs; i++){
      if((O_tot+O_crc)<=(i*n_symb*Qm*n_re_ctrl*r) &&
         (O_tot+O_crc)>((i-1)*n_symb*Qm*n_re_ctrl*r))
        return i;
    }
    AssertFatal(1==0,"MaxCodeRate %.2f can't support %d UCI bits and %d CRC bits with at most %d PRBs",
                r,O_tot,O_crc,nr_prbs);
  }
  else{
    AssertFatal(1==0,"Not yet implemented");
  }

}