/*
 * 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 power control procedures
 * \author Francesco Mani
 * \date 2023
 * \version 0.1
 * \email: email@francescomani.it
 * \note
 * \warning
 */

#include "LAYER2/NR_MAC_UE/mac_proto.h"

// Implementation of 6.2.4 Configured ransmitted power
// 3GPP TS 38.101-1 version 16.5.0 Release 16
// -
// The UE is allowed to set its configured maximum output power PCMAX,f,c for carrier f of serving cell c in each slot.
// The configured maximum output power PCMAX,f,c is set within the following bounds: PCMAX_L,f,c <=  PCMAX,f,c <=  PCMAX_H,f,c
// -
// Measurement units:
// - p_max:              dBm
// - delta_TC_c:         dB
// - P_powerclass:       dBm
// - delta_P_powerclass: dB
// - MPR_c:              dB
// - delta_MPR_c:        dB
// - delta_T_IB_c        dB
// - delta_rx_SRS        dB
// note:
// - Assuming:
// -- Powerclass 3 capable UE (which is default power class unless otherwise stated)
// -- Maximum power reduction (MPR_c) for power class 3
// -- no additional MPR (A_MPR_c)
int nr_get_Pcmax(NR_UE_MAC_INST_t *mac, int Qm, bool powerBoostPi2BPSK, int scs, int N_RB_UL, bool is_transform_precoding, int n_prbs, int start_prb)
{
  int nr_band = mac->nr_band;
  if(mac->frequency_range == FR1) {

    //TODO configure P-MAX from the upper layers according to 38.331
    int p_powerclass = 23; // dBm assuming poweclass 3 UE
    int p_emax = mac->p_Max ? *mac->p_Max : 0;
    int delta_P_powerclass = 0; // for powerclass 2 needs to be changed
    if(mac->p_Max && Qm == 1 && powerBoostPi2BPSK && (nr_band == 40 || nr_band == 41 || nr_band == 77 || nr_band == 78 || nr_band == 79)) {
      p_emax += 3;
      delta_P_powerclass -= 3;
    }

    // TODO to be set for CA and DC
    int delta_T_IB = 0;

    // TODO in case of band 41 and PRB allocation within 4MHz of the upper or lower limit of the band -> delta_TC = 1.5
    if(nr_band == 41)
      LOG_E(NR_MAC, "Need to implement delta_TC for band 41\n");
    int delta_TC = 0;

    float MPR = 0;
    frame_type_t frame_type = get_frame_type(nr_band, scs);
    if(compare_relative_ul_channel_bw(nr_band, scs, N_RB_UL, frame_type)) {
      int rb_low = (n_prbs / 2) > 1 ? (n_prbs / 2) : 1;
      int rb_high = N_RB_UL - rb_low - n_prbs;
      bool is_inner_rb = start_prb >= rb_low && start_prb <= rb_high && n_prbs <= ((N_RB_UL / 2) + (N_RB_UL & 1));
      // Table 6.2.2-1 in 38.101
      switch (Qm) {
        case 1 :
          AssertFatal(false, "MPR for Pi/2 BPSK not implemented yet\n");
          break;
        case 2 :
          if (is_transform_precoding) {
            if(!is_inner_rb)
              MPR = 1;
          }
          else {
            if(is_inner_rb)
              MPR = 1.5;
            else
              MPR = 3;
          }
          break;
        case 4 :
          if (is_transform_precoding) {
            if(is_inner_rb)
              MPR = 1;
            else
              MPR = 2;
          }
          else {
            if(is_inner_rb)
              MPR = 2;
            else
              MPR = 3;
          }
          break;
        case 6 :
          if (is_transform_precoding)
            MPR = 2.5;
          else
            MPR = 3.5;
          break;
        case 8 :
          if (is_transform_precoding)
            MPR = 4.5;
          else
            MPR = 6.5;
          break;
          break;
        default:
          AssertFatal(false, "Invalid Qm %d\n", Qm);
      }
    }

    int A_MPR = 0; // TODO too complicated to implement for now (see 6.2.3 in 38.101-1)
    int delta_rx_SRS = 0; // TODO for SRS
    int P_MPR = 0; // to ensure compliance with applicable electromagnetic energy absorption requirements

    float total_reduction = (MPR > A_MPR ? MPR : A_MPR) + delta_T_IB + delta_TC + delta_rx_SRS;
    if (P_MPR > total_reduction)
      total_reduction = P_MPR;
    int pcmax_high, pcmax_low;
    if(mac->p_Max) {
      pcmax_high = p_emax < (p_powerclass - delta_P_powerclass) ? p_emax : (p_powerclass - delta_P_powerclass);
      pcmax_low = (p_emax - delta_TC) < (p_powerclass - delta_P_powerclass - total_reduction) ?
                  (p_emax - delta_TC) : (p_powerclass - delta_P_powerclass - total_reduction);
    }
    else {
      pcmax_high = p_powerclass - delta_P_powerclass;
      pcmax_low = p_powerclass - delta_P_powerclass - total_reduction;
    }
    // TODO we need a strategy to select a value between minimum and maximum allowed PC_max
    int pcmax = (pcmax_low + pcmax_high) / 2;
    LOG_D(MAC, "Configured maximum output power:  %d dBm <= PCMAX %d dBm <= %d dBm \n", pcmax_low, pcmax, pcmax_high);
    return pcmax;
  }
  else {
    // FR2 TODO it is even more complex because it is radiated power
    return 23;
  }
}

