/**
 * Copyright 2013-2023 Software Radio Systems Limited
 *
 * This file is part of srsRAN.
 *
 * srsRAN is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of
 * the License, or (at your option) any later version.
 *
 * srsRAN is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * A copy of the GNU Affero General Public License can be found in
 * the LICENSE file in the top-level directory of this distribution
 * and at http://www.gnu.org/licenses/.
 *
 */

#include <srsran/phy/phch/pucch_proc.h>
#include <srsran/phy/utils/debug.h>

static bool pucch_proc_tx_sr(const srsran_uci_cfg_t* uci_cfg, const srsran_uci_value_t* uci_value)
{
  // Check SR transmission
  if (uci_value) {
    // If UCI value is provided, ignore scheduling request TTI condition
    return uci_value->scheduling_request;
  }

  return uci_cfg->is_scheduling_request_tti;
}

srsran_pucch_format_t srsran_pucch_proc_select_format(const srsran_cell_t*      cell,
                                                      const srsran_pucch_cfg_t* cfg,
                                                      const srsran_uci_cfg_t*   uci_cfg,
                                                      const srsran_uci_value_t* uci_value)
{
  srsran_pucch_format_t format    = SRSRAN_PUCCH_FORMAT_ERROR;
  uint32_t              total_ack = srsran_uci_cfg_total_ack(uci_cfg);

  // No CQI data
  if (!uci_cfg->cqi.data_enable && uci_cfg->cqi.ri_len == 0) {
    if (cfg->ack_nack_feedback_mode == SRSRAN_PUCCH_ACK_NACK_FEEDBACK_MODE_PUCCH3 &&
        total_ack > uci_cfg->ack[0].nof_acks) {
      format = SRSRAN_PUCCH_FORMAT_3;
    }
    // 1-bit ACK + optional SR
    else if (total_ack == 1) {
      format = SRSRAN_PUCCH_FORMAT_1A;
    }
    // 2-bit ACK + optional SR
    else if (total_ack >= 2 && total_ack <= 4) {
      format = SRSRAN_PUCCH_FORMAT_1B; // with channel selection if > 2
    }
    // If UCI value is provided, use SR signal only, otherwise SR request opportunity
    else if (pucch_proc_tx_sr(uci_cfg, uci_value)) {
      format = SRSRAN_PUCCH_FORMAT_1;
    } else {
      ERROR("Error selecting PUCCH format: Unsupported number of ACK bits %d", total_ack);
    }
  }
  // CQI data
  else {
    // CQI and no ack
    if (total_ack == 0) {
      format = SRSRAN_PUCCH_FORMAT_2;
    }
    // CQI + 1-bit ACK
    else if (total_ack == 1 && SRSRAN_CP_ISNORM(cell->cp)) {
      format = SRSRAN_PUCCH_FORMAT_2A;
    }
    // CQI + 2-bit ACK
    else if (total_ack == 2) {
      format = SRSRAN_PUCCH_FORMAT_2B;
    }
    // CQI + 1-bit ACK + extended cyclic prefix
    else if (total_ack == 1 && SRSRAN_CP_ISEXT(cell->cp)) {
      format = SRSRAN_PUCCH_FORMAT_2B;
    }
  }
  if (format == SRSRAN_PUCCH_FORMAT_ERROR) {
    ERROR("Returned Error while selecting PUCCH format");
  }

  return format;
}

static int pucch_cs_resources(const srsran_pucch_cfg_t* cfg,
                              const srsran_uci_cfg_t*   uci_cfg,
                              uint32_t                  n_pucch_i[SRSRAN_PUCCH_MAX_ALLOC])
{
  // Check inputs
  if (!cfg || !uci_cfg || !n_pucch_i) {
    return SRSRAN_ERROR_INVALID_INPUTS;
  }

  // Determine up to 4 PUCCH resources n_pucch_j associated with HARQ-ACK(j)
  int k = 0;
  for (int i = 0; i < SRSRAN_PUCCH_CS_MAX_CARRIERS && k < SRSRAN_PUCCH_CS_MAX_ACK; i++) {
    if (uci_cfg->ack[i].grant_cc_idx == 0) {
      // - for a PDSCH transmission indicated by the detection of a corresponding PDCCH in subframe n − 4 on the primary
      //   cell, or for a PDCCH indicating downlink SPS release (defined in subclause 9.2) in subframe n − 4 on the
      //   primary cell, the PUCCH resource is n_pucch_i = n_cce + N_pucch_1, and for transmission mode that supports up
      //   to two transport blocks, the PUCCH resource n_pucch_i+1 = n_cce + N_pucch_1 + 1
      for (uint32_t j = 0; j < uci_cfg->ack[i].nof_acks && k < SRSRAN_PUCCH_CS_MAX_ACK; j++) {
        n_pucch_i[k++] = uci_cfg->ack[i].ncce[0] + cfg->N_pucch_1 + j;
      }
    } else if (i == 0) {
      // - for a PDSCH transmission on the primary cell where there is not a corresponding PDCCH detected in subframe
      //   n − 4 , the value of n_pucch_i is determined according to higher layer configuration and Table 9.2-2. For
      //   transmission mode that supports up to two transport blocks, the PUCCH resource n_pucch_i+1 = n_pucch_i + 1
      for (uint32_t j = 0; j < uci_cfg->ack[i].nof_acks && k < SRSRAN_PUCCH_CS_MAX_ACK; j++) {
        n_pucch_i[k++] = cfg->n1_pucch_an_cs[uci_cfg->ack[i].tpc_for_pucch % SRSRAN_PUCCH_SIZE_AN_CS][0] + j;
      }
    } else {
      // - for a PDSCH transmission indicated by the detection of a corresponding PDCCH in subframe n − 4 on the
      //   secondary cell, the value of n_pucch_i, and the value of n_pucch_i+1 for the transmission mode that supports
      //   up to two transport blocks is determined according to higher layer configuration and Table 10.1.2.2.1-2. The
      //   TPC field in the DCI format of the corresponding PDCCH shall be used to determine the PUCCH resource values
      //   from one of the four resource values configured by higher layers, with the mapping defined in Table
      //   10.1.2.2.1-2. For a UE configured for a transmission mode that supports up to two transport blocks a PUCCH
      //   resource value in Table 10.1.2.2.1-2 maps to two PUCCH resources (n_pucch_i, n_pucch_i + 1), otherwise the
      //   PUCCH resource value maps to a single PUCCH resource n_pucch_i.
      for (uint32_t j = 0; j < uci_cfg->ack[i].nof_acks && k < SRSRAN_PUCCH_CS_MAX_ACK; j++) {
        n_pucch_i[k++] =
            cfg->n1_pucch_an_cs[uci_cfg->ack[i].tpc_for_pucch % SRSRAN_PUCCH_SIZE_AN_CS][j % SRSRAN_PUCCH_NOF_AN_CS];
      }
    }
  }

  return k;
}

#define PUCCH_CS_SET_ACK(J, B0, B1, ...)                                                                               \
  do {                                                                                                                 \
    if (j == J && b[0] == B0 && b[1] == B1) {                                                                          \
      uint8_t pos[] = {__VA_ARGS__};                                                                                   \
      for (uint32_t i = 0; i < sizeof(pos) && pos[i] < SRSRAN_PUCCH_CS_MAX_ACK; i++) {                                 \
        uci_value[pos[i]] = 1;                                                                                         \
      }                                                                                                                \
      ret = SRSRAN_SUCCESS;                                                                                            \
    }                                                                                                                  \
  } while (false)

static int puccch_cs_get_ack_a2(uint32_t j, const uint8_t b[2], uint8_t uci_value[SRSRAN_UCI_MAX_ACK_BITS])
{
  int ret = SRSRAN_ERROR;

  PUCCH_CS_SET_ACK(1, 1, 1, 0, 1);
  PUCCH_CS_SET_ACK(0, 1, 1, 0);
  PUCCH_CS_SET_ACK(1, 0, 0, 1);
  PUCCH_CS_SET_ACK(1, 0, 0, SRSRAN_PUCCH_CS_MAX_ACK);

  return ret;
}

static int puccch_cs_get_ack_a3(uint32_t j, const uint8_t b[2], uint8_t uci_value[SRSRAN_UCI_MAX_ACK_BITS])
{
  int ret = SRSRAN_ERROR;

  PUCCH_CS_SET_ACK(1, 1, 1, 0, 1, 2);
  PUCCH_CS_SET_ACK(1, 1, 0, 0, 2);
  PUCCH_CS_SET_ACK(1, 0, 1, 1, 2);
  PUCCH_CS_SET_ACK(2, 1, 1, 2);
  PUCCH_CS_SET_ACK(0, 1, 1, 0, 1);
  PUCCH_CS_SET_ACK(0, 1, 0, 0);
  PUCCH_CS_SET_ACK(0, 0, 1, 1);
  PUCCH_CS_SET_ACK(1, 0, 0, SRSRAN_PUCCH_CS_MAX_ACK);

  return ret;
}

static int puccch_cs_get_ack_a4(uint32_t j, const uint8_t b[2], uint8_t uci_value[SRSRAN_UCI_MAX_ACK_BITS])
{
  int ret = SRSRAN_ERROR;

  PUCCH_CS_SET_ACK(1, 1, 1, 0, 1, 2, 3);
  PUCCH_CS_SET_ACK(2, 0, 1, 0, 2, 3);
  PUCCH_CS_SET_ACK(1, 0, 1, 1, 2, 3);
  PUCCH_CS_SET_ACK(3, 1, 1, 2, 3);
  PUCCH_CS_SET_ACK(1, 1, 0, 0, 1, 2);
  PUCCH_CS_SET_ACK(2, 0, 0, 0, 2);
  PUCCH_CS_SET_ACK(1, 0, 0, 1, 2);
  PUCCH_CS_SET_ACK(3, 1, 0, 2);
  PUCCH_CS_SET_ACK(2, 1, 1, 0, 1, 3);
  PUCCH_CS_SET_ACK(2, 1, 0, 0, 3);
  PUCCH_CS_SET_ACK(3, 0, 1, 1, 3);
  PUCCH_CS_SET_ACK(3, 0, 0, 3);
  PUCCH_CS_SET_ACK(0, 1, 1, 0, 1);
  PUCCH_CS_SET_ACK(0, 1, 0, 0);
  PUCCH_CS_SET_ACK(0, 0, 1, 1);
  PUCCH_CS_SET_ACK(0, 0, 0, SRSRAN_PUCCH_CS_MAX_ACK);

  return ret;
}

int srsran_pucch_cs_get_ack(const srsran_pucch_cfg_t* cfg,
                            const srsran_uci_cfg_t*   uci_cfg,
                            uint32_t                  j,
                            const uint8_t             b[2],
                            srsran_uci_value_t*       uci_value)
{
  int ret = SRSRAN_ERROR_INVALID_INPUTS;

  if (cfg && uci_cfg && uci_value) {
    // Set bits to 0 by default
    memset(uci_value->ack.ack_value, 0, SRSRAN_UCI_MAX_ACK_BITS);

    uint32_t nof_ack = srsran_uci_cfg_total_ack(uci_cfg);
    switch (nof_ack) {
      case 2:
        // A = 2
        ret = puccch_cs_get_ack_a2(j, b, uci_value->ack.ack_value);
        break;
      case 3:
        // A = 3
        ret = puccch_cs_get_ack_a3(j, b, uci_value->ack.ack_value);
        break;
      case 4:
        // A = 4
        ret = puccch_cs_get_ack_a4(j, b, uci_value->ack.ack_value);
        break;
      default:
        // Unhandled case
        ERROR("Unexpected number of ACK (%d)", nof_ack);
        ret = SRSRAN_ERROR;
    }
  }

  return ret;
}

static int pucch_f3_resources(const srsran_pucch_cfg_t* cfg, const srsran_uci_cfg_t* uci_cfg, uint32_t* n_pucch_3)
{
  int ret = SRSRAN_ERROR_INVALID_INPUTS;

  if (cfg && uci_cfg && n_pucch_3) {
    n_pucch_3[0] = cfg->n3_pucch_an_list[uci_cfg->ack[0].tpc_for_pucch % SRSRAN_PUCCH_SIZE_AN_CS];
    ret          = 1;
  }

  return ret;
}

static uint32_t get_Np(uint32_t p, uint32_t nof_prb)
{
  if (p == 0) {
    return 0;
  } else {
    return nof_prb * (SRSRAN_NRE * p - 4) / 36;
  }
}

static uint32_t n_pucch_i_tdd(uint32_t ncce, uint32_t N_pucch_1, uint32_t nof_prb, uint32_t M, uint32_t m)
{
  uint32_t Np = 0, Np_1 = 0;
  for (uint32_t p = 0; p < 4; p++) {
    Np   = get_Np(p, nof_prb);
    Np_1 = get_Np(p + 1, nof_prb);
    if (ncce >= Np && ncce < Np_1) {
      uint32_t npucch = (M - m - 1) * Np + m * Np_1 + ncce + N_pucch_1;
      return npucch;
    }
  }
  ERROR("Could not find Np value for ncce=%d", ncce);
  return 0;
}

static int pucch_tdd_resources(const srsran_cell_t*      cell,
                               const srsran_pucch_cfg_t* cfg,
                               const srsran_uci_cfg_t*   uci_cfg,
                               uint32_t                  n_pucch_tdd[SRSRAN_PUCCH_MAX_ALLOC])
{
  int ret = SRSRAN_ERROR_INVALID_INPUTS;

  for (uint32_t i = 0; i < uci_cfg->ack[0].tdd_ack_M; i++) {
    n_pucch_tdd[i] =
        n_pucch_i_tdd(uci_cfg->ack[0].ncce[i], cfg->N_pucch_1, cell->nof_prb, uci_cfg->ack[0].tdd_ack_M, i);
  }

  return ret;
}

int srsran_pucch_proc_get_resources(const srsran_cell_t*      cell,
                                    const srsran_pucch_cfg_t* cfg,
                                    const srsran_uci_cfg_t*   uci_cfg,
                                    const srsran_uci_value_t* uci_value,
                                    uint32_t*                 n_pucch_i)
{
  if (!cfg || !cell || !uci_cfg || !n_pucch_i) {
    ERROR("pucch_resource_selection(): Invalid parameters");
    return SRSRAN_ERROR_INVALID_INPUTS;
  }

  uint32_t total_nof_ack = srsran_uci_cfg_total_ack(uci_cfg);

  // Available scheduling request and PUCCH format is not PUCCH3
  if (pucch_proc_tx_sr(uci_cfg, uci_value) && cfg->format != SRSRAN_PUCCH_FORMAT_3) {
    n_pucch_i[0] = cfg->n_pucch_sr;
    return 1;
  }

  // PUCCH formats 1, 1A and 1B (normal anb channel selection modes)
  if (cfg->format < SRSRAN_PUCCH_FORMAT_2) {
    if (cfg->sps_enabled) {
      n_pucch_i[0] = cfg->n_pucch_1[uci_cfg->ack[0].tpc_for_pucch % 4];
      return 1;
    }

    if (cell->frame_type == SRSRAN_TDD) {
      return pucch_tdd_resources(cell, cfg, uci_cfg, n_pucch_i);
    }

    if (cfg->ack_nack_feedback_mode == SRSRAN_PUCCH_ACK_NACK_FEEDBACK_MODE_CS) {
      return pucch_cs_resources(cfg, uci_cfg, n_pucch_i);
    }

    if (cfg->ack_nack_feedback_mode == SRSRAN_PUCCH_ACK_NACK_FEEDBACK_MODE_NORMAL ||
        (cfg->ack_nack_feedback_mode == SRSRAN_PUCCH_ACK_NACK_FEEDBACK_MODE_PUCCH3 &&
         total_nof_ack == uci_cfg->ack[0].nof_acks)) {
      // If normal or feedback mode PUCCH3 with only data in PCell
      n_pucch_i[0] = uci_cfg->ack[0].ncce[0] + cfg->N_pucch_1;
      return 1;
    }

    // Otherwise an error shall be prompt
    ERROR("Unhandled PUCCH format mode %s", srsran_ack_nack_feedback_mode_string(cfg->ack_nack_feedback_mode));
    return SRSRAN_ERROR;
  }

  // PUCCH format 3
  if (cfg->format == SRSRAN_PUCCH_FORMAT_3) {
    return pucch_f3_resources(cfg, uci_cfg, n_pucch_i);
  }

  // PUCCH format 2
  n_pucch_i[0] = cfg->n_pucch_2;
  return 1;
}

// Selection of n_pucch for PUCCH Format 1a and 1b with channel selection for 1 and 2 CC
static uint32_t get_npucch_cs(const srsran_pucch_cfg_t* cfg,
                              const srsran_uci_cfg_t*   uci_cfg,
                              const uint32_t            n_pucch_i[SRSRAN_PUCCH_MAX_ALLOC],
                              uint8_t                   b[SRSRAN_UCI_MAX_ACK_BITS])
{
  uint32_t n_pucch = 0;

  // Do resource selection and bit mapping according to tables 10.1.2.2.1-3, 10.1.2.2.1-4 and 10.1.2.2.1-5
  uint32_t nof_ack = srsran_uci_cfg_total_ack(uci_cfg);
  switch (nof_ack) {
    case 1:
      // 1-bit is Format1A always
      n_pucch = n_pucch_i[0];
      break;
    case 2:
      if (b[1] != 1) {
        /* n_pucch1_0 */
        n_pucch = n_pucch_i[0];
      } else {
        /* n_pucch1_1 */
        n_pucch = n_pucch_i[1];
      }
      if (b[0] == 1) {
        b[0] = 1;
        b[1] = 1;
      } else {
        b[0] = 0;
        b[1] = 0;
      }
      break;
    case 3:
      if (b[0] != 1 && b[1] != 1) {
        /* n_pucch1_2 */
        n_pucch = n_pucch_i[2];
      } else if (b[2] == 1) {
        /* n_pucch1_1 */
        n_pucch = n_pucch_i[1];
      } else {
        /* n_pucch1_0 */
        n_pucch = n_pucch_i[0];
      }
      if (b[0] != 1 && b[1] != 1 && b[2] != 1) {
        b[0] = 0;
        b[1] = 0;
      } else if (b[0] != 1 && b[1] == 1) {
        b[0] = 0;
        b[1] = 1;
      } else if (b[0] == 1 && b[1] != 1) {
        b[0] = 1;
        b[1] = 0;
      } else {
        b[0] = 1;
        b[1] = 1;
      }
      break;
    case 4:
      if (b[2] != 1 && b[3] != 1) {
        /* n_pucch1_0 */
        n_pucch = n_pucch_i[0];
      } else if (b[1] == 1 && b[2] == 1) {
        /* n_pucch1_1 */
        n_pucch = n_pucch_i[1];
      } else if (b[0] == 1) {
        /* n_pucch1_2 */
        n_pucch = n_pucch_i[2];
      } else {
        /* n_pucch1_3 */
        n_pucch = n_pucch_i[3];
      }
      if (b[2] != 1 && b[3] != 1) {
        /* n_pucch1_0 */
        b[0] = (uint8_t)(b[0] != 1 ? 0 : 1);
        b[1] = (uint8_t)(b[1] != 1 ? 0 : 1);
      } else if (b[1] == 1 && b[2] == 1) {
        /* n_pucch1_1 */
        b[0] = (uint8_t)(b[0] != 1 ? 0 : 1);
        b[1] = (uint8_t)(b[3] != 1 ? 0 : 1);
      } else if (b[0] == 1) {
        /* n_pucch1_2 */
        b[0] = (uint8_t)((b[3] != 1 ? 0 : 1) & (b[2] != 1 ? 1 : 0));
        b[1] = (uint8_t)((b[3] != 1 ? 0 : 1) & ((b[1] != 1 ? 0 : 1) ^ (b[2] != 1 ? 0 : 1)));
      } else {
        /* n_pucch1_3 */
        b[0] = (uint8_t)(b[2] != 1 ? 0 : 1);
        b[1] = (uint8_t)(((b[3] == 1) && ((b[1] == 1) != (b[2] == 1))) ? 1 : 0);
      }
      break;
    default:
      ERROR("Too many (%d) ACK for this CS mode", srsran_uci_cfg_total_ack(uci_cfg));
  }

  return n_pucch;
}

static void set_b01(uint8_t* b, uint8_t x)
{
  switch (x) {
    case 0:
      b[0] = 0;
      b[1] = 0;
      break;
    case 1:
      b[0] = 0;
      b[1] = 1;
      break;
    case 2:
      b[0] = 1;
      b[1] = 0;
      break;
    case 3:
      b[0] = 1;
      b[1] = 1;
      break;
    default:
      ERROR("Unhandled case (%d)", x);
  }
}

#define is_ack(h) (h == 1)
#define is_nack(h) (h == 0)
#define is_nackdtx(h) ((h & 1) == 0)
#define is_dtx(h) (h == 2)

// n_pucch and b0b1 selection for TDD, tables 10.1-2, 10.1-3 and 10.1-4
static uint32_t
get_npucch_tdd(const uint32_t n_pucch[4], const srsran_uci_cfg_t* uci_cfg, uint8_t b[SRSRAN_UCI_MAX_ACK_BITS])
{
  switch (uci_cfg->ack[0].nof_acks) {
    case 1:
      return n_pucch[0];
    case 2:
      if (is_ack(b[0]) && is_ack(b[1])) {
        set_b01(b, 3);
        return n_pucch[1];
      } else if (is_ack(b[0]) && is_nackdtx(b[1])) {
        set_b01(b, 1);
        return n_pucch[0];
      } else if (is_nackdtx(b[0]) && is_ack(b[1])) {
        set_b01(b, 0);
        return n_pucch[1];
      } else if (is_nackdtx(b[0]) && is_nack(b[1])) {
        set_b01(b, 2);
        return n_pucch[1];
      } else if (is_nack(b[0]) && is_dtx(b[1])) {
        set_b01(b, 2);
        return n_pucch[0];
      }
      break;
    case 3:
      if (is_ack(b[0]) && is_ack(b[1]) && is_ack(b[2])) {
        set_b01(b, 3);
        return n_pucch[2];
      } else if (is_ack(b[0]) && is_ack(b[1]) && is_nackdtx(b[2])) {
        set_b01(b, 3);
        return n_pucch[1];
      } else if (is_ack(b[0]) && is_nackdtx(b[1]) && is_ack(b[2])) {
        set_b01(b, 3);
        return n_pucch[0];
      } else if (is_ack(b[0]) && is_nackdtx(b[1]) && is_nackdtx(b[2])) {
        set_b01(b, 1);
        return n_pucch[0];
      } else if (is_nackdtx(b[0]) && is_ack(b[1]) && is_ack(b[2])) {
        set_b01(b, 2);
        return n_pucch[2];
      } else if (is_nackdtx(b[0]) && is_ack(b[1]) && is_nackdtx(b[2])) {
        set_b01(b, 0);
        return n_pucch[1];
      } else if (is_nackdtx(b[0]) && is_nackdtx(b[1]) && is_ack(b[2])) {
        set_b01(b, 0);
        return n_pucch[2];
      } else if (is_dtx(b[0]) && is_dtx(b[1]) && is_nack(b[2])) {
        set_b01(b, 1);
        return n_pucch[2];
      } else if (is_dtx(b[0]) && is_nack(b[1]) && is_nackdtx(b[2])) {
        set_b01(b, 2);
        return n_pucch[1];
      } else if (is_nack(b[0]) && is_nackdtx(b[1]) && is_nackdtx(b[2])) {
        set_b01(b, 2);
        return n_pucch[0];
      }
      break;
    case 4:
      if (is_ack(b[0]) && is_ack(b[1]) && is_ack(b[2]) && is_ack(b[3])) {
        set_b01(b, 3);
        return n_pucch[1];
      } else if (is_ack(b[0]) && is_ack(b[1]) && is_ack(b[2]) && is_nackdtx(b[3])) {
        set_b01(b, 2);
        return n_pucch[1];
      } else if (is_nackdtx(b[0]) && is_nackdtx(b[1]) && is_nack(b[2]) && is_dtx(b[3])) {
        set_b01(b, 3);
        return n_pucch[2];
      } else if (is_ack(b[0]) && is_ack(b[1]) && is_nackdtx(b[2]) && is_ack(b[3])) {
        set_b01(b, 2);
        return n_pucch[1];
      } else if (is_nack(b[0]) && is_dtx(b[1]) && is_dtx(b[2]) && is_dtx(b[3])) {
        set_b01(b, 2);
        return n_pucch[0];
      } else if (is_ack(b[0]) && is_ack(b[1]) && is_nackdtx(b[2]) && is_nackdtx(b[3])) {
        set_b01(b, 2);
        return n_pucch[1];
      } else if (is_ack(b[0]) && is_nackdtx(b[1]) && is_ack(b[2]) && is_ack(b[3])) {
        set_b01(b, 1);
        return n_pucch[3];
      } else if (is_nackdtx(b[0]) && is_nackdtx(b[1]) && is_nackdtx(b[2]) && is_nack(b[3])) {
        set_b01(b, 3);
        return n_pucch[3];
      } else if (is_ack(b[0]) && is_nackdtx(b[1]) && is_ack(b[2]) && is_nack(b[3])) {
        set_b01(b, 2);
        return n_pucch[1];
      } else if (is_ack(b[0]) && is_nackdtx(b[1]) && is_nackdtx(b[2]) && is_ack(b[3])) {
        set_b01(b, 1);
        return n_pucch[0];
      } else if (is_ack(b[0]) && is_nackdtx(b[1]) && is_nackdtx(b[2]) && is_nackdtx(b[3])) {
        set_b01(b, 3);
        return n_pucch[0];
      } else if (is_nackdtx(b[0]) && is_ack(b[1]) && is_ack(b[2]) && is_ack(b[3])) {
        set_b01(b, 1);
        return n_pucch[3];
      } else if (is_nackdtx(b[0]) && is_nack(b[1]) && is_dtx(b[2]) && is_dtx(b[3])) {
        set_b01(b, 0);
        return n_pucch[1];
      } else if (is_nackdtx(b[0]) && is_ack(b[1]) && is_ack(b[2]) && is_nackdtx(b[3])) {
        set_b01(b, 2);
        return n_pucch[2];
      } else if (is_nackdtx(b[0]) && is_ack(b[1]) && is_nackdtx(b[2]) && is_ack(b[3])) {
        set_b01(b, 2);
        return n_pucch[3];
      } else if (is_nackdtx(b[0]) && is_ack(b[1]) && is_nackdtx(b[2]) && is_nackdtx(b[3])) {
        set_b01(b, 1);
        return n_pucch[1];
      } else if (is_nackdtx(b[0]) && is_nackdtx(b[1]) && is_ack(b[2]) && is_ack(b[3])) {
        set_b01(b, 1);
        return n_pucch[3];
      } else if (is_nackdtx(b[0]) && is_nackdtx(b[1]) && is_ack(b[2]) && is_nackdtx(b[3])) {
        set_b01(b, 0);
        return n_pucch[2];
      } else if (is_nackdtx(b[0]) && is_nackdtx(b[1]) && is_nackdtx(b[2]) && is_ack(b[3])) {
        set_b01(b, 0);
        return n_pucch[3];
      }
  }
  return 0;
}

uint32_t srsran_pucch_proc_get_npucch(const srsran_cell_t*      cell,
                                      const srsran_pucch_cfg_t* cfg,
                                      const srsran_uci_cfg_t*   uci_cfg,
                                      const srsran_uci_value_t* uci_value,
                                      uint8_t                   b[SRSRAN_UCI_MAX_ACK_BITS])
{
  uint32_t n_pucch_i[SRSRAN_PUCCH_MAX_ALLOC] = {};
  int      nof_resources = srsran_pucch_proc_get_resources(cell, cfg, uci_cfg, uci_value, n_pucch_i);

  // Copy original bits in b
  memcpy(b, uci_value->ack.ack_value, SRSRAN_UCI_MAX_ACK_BITS);

  // If error occurred, return 0
  if (nof_resources < 1) {
    return 0;
  }

  // Return immediately if only one possible resource
  if (nof_resources == 1) {
    return n_pucch_i[0];
  }

  // Select TDD resource
  if (cell->frame_type == SRSRAN_TDD) {
    return get_npucch_tdd(n_pucch_i, uci_cfg, b);
  }

  // Select Channel Selection resource
  if (cfg->ack_nack_feedback_mode == SRSRAN_PUCCH_ACK_NACK_FEEDBACK_MODE_CS) {
    return get_npucch_cs(cfg, uci_cfg, n_pucch_i, b);
  }

  return 0;
}
