/*
 * 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 phy_procedures_lte_eNB.c
 * \brief Implementation of eNB procedures from 36.213 LTE specifications
 * \author R. Knopp, F. Kaltenberger, N. Nikaein, X. Foukas
 * \date 2011
 * \version 0.1
 * \company Eurecom
 * \email: knopp@eurecom.fr,florian.kaltenberger@eurecom.fr,navid.nikaein@eurecom.fr, x.foukas@sms.ed.ac.uk
 * \note
 * \warning
 */

#include "PHY/defs.h"
#include "PHY/extern.h"
#include "SCHED/defs.h"
#include "SCHED/extern.h"
#include "nfapi_interface.h"
#include "fapi_l1.h"
#include "UTIL/LOG/log.h"
#include "UTIL/LOG/vcd_signal_dumper.h"

#include "T.h"

#include "assertions.h"
#include "msc.h"

#include <time.h>

#if defined(ENABLE_ITTI)
#   include "intertask_interface.h"
#endif

extern uint32_t nfapi_mode;

void prach_procedures(PHY_VARS_eNB *eNB,
#ifdef Rel14
		      int br_flag
#endif
		      ) {
  uint16_t max_preamble[4],max_preamble_energy[4],max_preamble_delay[4];
  uint16_t i;
  int frame,subframe;

#ifdef Rel14
  if (br_flag==1) {
    subframe = eNB->proc.subframe_prach_br;
    frame = eNB->proc.frame_prach_br;
    pthread_mutex_lock(&eNB->UL_INFO_mutex);
    eNB->UL_INFO.rach_ind_br.rach_indication_body.number_of_preambles=0;
    pthread_mutex_unlock(&eNB->UL_INFO_mutex);
  }
  else
#endif
    {
      pthread_mutex_lock(&eNB->UL_INFO_mutex);
      eNB->UL_INFO.rach_ind.rach_indication_body.number_of_preambles=0;
      pthread_mutex_unlock(&eNB->UL_INFO_mutex);
      subframe = eNB->proc.subframe_prach;
      frame = eNB->proc.frame_prach;
    }
  RU_t *ru;
  int aa=0;
  int ru_aa;

 
  VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_ENB_PRACH_RX,1);



  for (i=0;i<eNB->num_RU;i++) {
    ru=eNB->RU_list[i];
    for (ru_aa=0,aa=0;ru_aa<ru->nb_rx;ru_aa++,aa++) {
      eNB->prach_vars.rxsigF[0][aa] = eNB->RU_list[i]->prach_rxsigF[ru_aa];
#ifdef Rel14
      int ce_level;

      if (br_flag==1)
	for (ce_level=0;ce_level<4;ce_level++) eNB->prach_vars_br.rxsigF[ce_level][aa] = eNB->RU_list[i]->prach_rxsigF_br[ce_level][ru_aa];
#endif
    }
  }

  rx_prach(eNB,
	   eNB->RU_list[0],
	   &max_preamble[0],
	   &max_preamble_energy[0],
	   &max_preamble_delay[0],
	   frame,
	   0
#ifdef Rel14
	   ,br_flag
#endif
	   );

 #ifdef DEBUG_PHY_PROC
  LOG_D(PHY,"[RAPROC] Frame %d, subframe %d : Most likely preamble %d, energy %d dB delay %d (prach_energy counter %d)\n",
        frame,subframe,
        max_preamble[0],
        max_preamble_energy[0]/10,
        max_preamble_delay[0],
	eNB->prach_energy_counter);
 #endif

#ifdef Rel14
  if (br_flag==1) {

    int prach_mask;
      
    prach_mask = is_prach_subframe(&eNB->frame_parms,eNB->proc.frame_prach_br,eNB->proc.subframe_prach_br);
    
    eNB->UL_INFO.rach_ind_br.rach_indication_body.preamble_list                              = eNB->preamble_list_br;
    int ind=0;
    int ce_level=0;
    /* Save for later, it doesn't work    
    for (int ind=0,ce_level=0;ce_level<4;ce_level++) {
      
      if ((eNB->frame_parms.prach_emtc_config_common.prach_ConfigInfo.prach_CElevel_enable[ce_level]==1)&&
	  (prach_mask&(1<<(1+ce_level)) > 0) && // prach is active and CE level has finished its repetitions
	  (eNB->prach_vars_br.repetition_number[ce_level]==
	   eNB->frame_parms.prach_emtc_config_common.prach_ConfigInfo.prach_numRepetitionPerPreambleAttempt[ce_level])) {
    */ 
    if (eNB->frame_parms.prach_emtc_config_common.prach_ConfigInfo.prach_CElevel_enable[0]==1){ 
      if ((eNB->prach_energy_counter == 100) && 
          (max_preamble_energy[0] > eNB->measurements.prach_I0 + 100)) {
	eNB->UL_INFO.rach_ind_br.rach_indication_body.number_of_preambles++;
	
	eNB->preamble_list_br[ind].preamble_rel8.timing_advance        = max_preamble_delay[ind];//
	eNB->preamble_list_br[ind].preamble_rel8.preamble              = max_preamble[ind];
	// note: fid is implicitly 0 here, this is the rule for eMTC RA-RNTI from 36.321, Section 5.1.4
	eNB->preamble_list_br[ind].preamble_rel8.rnti                  = 1+subframe+(eNB->prach_vars_br.first_frame[ce_level]%40);  
	eNB->preamble_list_br[ind].instance_length                     = 0; //don't know exactly what this is
	eNB->preamble_list_br[ind].preamble_rel13.rach_resource_type   = 1+ce_level;  // CE Level
	LOG_D(PHY,"Filling NFAPI indication for RACH %d CELevel %d (mask %x) : TA %d, Preamble %d, rnti %x, rach_resource_type %d\n",
	      ind,
	      ce_level,
	      prach_mask,
	      eNB->preamble_list_br[ind].preamble_rel8.timing_advance,
	      eNB->preamble_list_br[ind].preamble_rel8.preamble,
	      eNB->preamble_list_br[ind].preamble_rel8.rnti,
	      eNB->preamble_list_br[ind].preamble_rel13.rach_resource_type);
      }
      /*
	ind++;
      }
      } */// ce_level
    }
  }
  else
#endif

    {
      if ((eNB->prach_energy_counter == 100) && 
          (max_preamble_energy[0] > eNB->measurements.prach_I0+100)) {

	LOG_I(PHY,"[eNB %d/%d][RAPROC] Frame %d, subframe %d Initiating RA procedure with preamble %d, energy %d.%d dB, delay %d\n",
	      eNB->Mod_id,
	      eNB->CC_id,
	      frame,
	      subframe,
	      max_preamble[0],
	      max_preamble_energy[0]/10,
	      max_preamble_energy[0]%10,
	      max_preamble_delay[0]);
	
	    T(T_ENB_PHY_INITIATE_RA_PROCEDURE, T_INT(eNB->Mod_id), T_INT(frame), T_INT(subframe),
	      T_INT(max_preamble[0]), T_INT(max_preamble_energy[0]), T_INT(max_preamble_delay[0]));
	    
	    pthread_mutex_lock(&eNB->UL_INFO_mutex);
	    
	    eNB->UL_INFO.rach_ind.rach_indication_body.number_of_preambles  = 1;
	    eNB->UL_INFO.rach_ind.rach_indication_body.preamble_list        = &eNB->preamble_list[0];
	    eNB->UL_INFO.rach_ind.rach_indication_body.tl.tag               = NFAPI_RACH_INDICATION_BODY_TAG;
            eNB->UL_INFO.rach_ind.header.message_id                         = NFAPI_RACH_INDICATION;
            eNB->UL_INFO.rach_ind.sfn_sf                                    = frame<<4 | subframe;
	    
	    eNB->preamble_list[0].preamble_rel8.tl.tag                = NFAPI_PREAMBLE_REL8_TAG;
	    eNB->preamble_list[0].preamble_rel8.timing_advance        = max_preamble_delay[0];
	    eNB->preamble_list[0].preamble_rel8.preamble              = max_preamble[0];
	    eNB->preamble_list[0].preamble_rel8.rnti                  = 1+subframe;  // note: fid is implicitly 0 here
	    eNB->preamble_list[0].preamble_rel13.rach_resource_type   = 0;
	    eNB->preamble_list[0].instance_length                     = 0; //don't know exactly what this is
	    
            if (nfapi_mode == 1) {  // If NFAPI PNF then we need to send the message to the VNF

              LOG_D(PHY,"Filling NFAPI indication for RACH : SFN_SF:%d TA %d, Preamble %d, rnti %x, rach_resource_type %d\n",
                  NFAPI_SFNSF2DEC(eNB->UL_INFO.rach_ind.sfn_sf),
                  eNB->preamble_list[0].preamble_rel8.timing_advance,
                  eNB->preamble_list[0].preamble_rel8.preamble,
                  eNB->preamble_list[0].preamble_rel8.rnti,
                  eNB->preamble_list[0].preamble_rel13.rach_resource_type);	    

              oai_nfapi_rach_ind(&eNB->UL_INFO.rach_ind);

              eNB->UL_INFO.rach_ind.rach_indication_body.number_of_preambles = 0;
            }

	    pthread_mutex_unlock(&eNB->UL_INFO_mutex);
      
      } // max_preamble_energy > prach_I0 + 100 
      else {
         eNB->measurements.prach_I0 = ((eNB->measurements.prach_I0*900)>>10) + ((max_preamble_energy[0]*124)>>10); 
         if (frame==0) LOG_I(PHY,"prach_I0 = %d.%d dB\n",eNB->measurements.prach_I0/10,eNB->measurements.prach_I0%10);
         if (eNB->prach_energy_counter < 100) eNB->prach_energy_counter++;
      }
    } // else br_flag

  VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_ENB_PRACH_RX,0);
}