/******************************************************************************
 *
 * Copyright (C) 2023 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
 *
 * 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.
 *
 *****************************************************************************
 * Originally developed and contributed by Ittiam Systems Pvt. Ltd, Bangalore
*/
#include <string.h>
#include "ixheaac_type_def.h"
#include "ixheaacd_memory_standards.h"
#include "ixheaacd_mps_struct_def.h"
#include "ixheaacd_mps_res_rom.h"
#include "ixheaacd_mps_aac_struct.h"
#include "ixheaac_constants.h"
#include "ixheaac_basic_ops32.h"
#include "ixheaac_basic_ops40.h"
#include "ixheaacd_bitbuffer.h"
#include "ixheaacd_common_rom.h"
#include "ixheaacd_sbrdecsettings.h"
#include "ixheaacd_sbr_scale.h"
#include "ixheaacd_env_extr_part.h"
#include "ixheaacd_sbr_rom.h"
#include "ixheaacd_hybrid.h"
#include "ixheaacd_ps_dec.h"
#include "ixheaacd_mps_polyphase.h"
#include "ixheaacd_config.h"
#include "ixheaacd_qmf_dec.h"
#include "ixheaacd_mps_dec.h"
#include "ixheaacd_mps_decor.h"
#include "ixheaacd_mps_hybfilter.h"
#include "ixheaacd_mps_mdct_2_qmf.h"
#include "ixheaacd_mps_get_index.h"
#include "ixheaacd_mps_macro_def.h"
#include "ixheaacd_mps_basic_op.h"
#include "ixheaac_error_standards.h"

VOID ixheaacd_mdct_2_qmf(ia_heaac_mps_state_struct *pstr_mps_state) {
  ia_mps_dec_reuse_array_struct *p_array_struct = pstr_mps_state->array_struct;
  WORD32 ch, rfpsf, qb;
  WORD32 qmf_global_offset;
  WORD32 time_slots = pstr_mps_state->time_slots;
  WORD32 time_slots_x4 = (time_slots << 2);
  WORD32 qmf_bands = pstr_mps_state->qmf_bands;
  WORD32 *p_qmf_residual_real_post, *p_qmf_residual_imag_post;
  VOID *scratch = pstr_mps_state->mps_scratch_mem_v;

  if (pstr_mps_state->up_mix_type != 2) {
    WORD32 num_ch = pstr_mps_state->num_ott_boxes + pstr_mps_state->num_ttt_boxes;
    WORD32 rfpsf_max = pstr_mps_state->residual_frames_per_spatial_frame;
    WORD32 upd_qmf = pstr_mps_state->upd_qmf;

    WORD32 *qmf_residual_real_pre = p_array_struct->qmf_residual_real_pre;
    WORD32 *qmf_residual_real_post = p_array_struct->qmf_residual_real_post;

    WORD32 *qmf_residual_imag_pre = p_array_struct->qmf_residual_imag_pre;
    WORD32 *qmf_residual_imag_post = p_array_struct->qmf_residual_imag_post;

    WORD32 *p_res_mdct = p_array_struct->res_mdct;

    for (ch = 0; ch < num_ch; ch++) {
      if (pstr_mps_state->bs_config.bs_residual_present[ch]) {
        WORD32 *res_mdct = p_res_mdct;
        qmf_global_offset = 0;

        p_qmf_residual_real_post = qmf_residual_real_post;
        p_qmf_residual_imag_post = qmf_residual_imag_post;
        for (qb = 0; qb < qmf_bands; qb++) {
          memset(p_qmf_residual_real_post, 0, time_slots_x4);
          memset(p_qmf_residual_imag_post, 0, time_slots_x4);

          p_qmf_residual_real_post += MAX_TIME_SLOTS;
          p_qmf_residual_imag_post += MAX_TIME_SLOTS;
        }

        for (rfpsf = 0; rfpsf < rfpsf_max; rfpsf++) {
          ixheaacd_mdct2qmf_process(upd_qmf, res_mdct, qmf_residual_real_pre,
                                    qmf_residual_real_post, qmf_residual_imag_pre,
                                    qmf_residual_imag_post,
                                    pstr_mps_state->res_block_type[ch][rfpsf], qmf_global_offset,
                                    &(pstr_mps_state->ia_mps_dec_mps_table), scratch, time_slots);
          qmf_global_offset += upd_qmf;
          res_mdct += MDCTCOEFX2;
        }
      }

      qmf_residual_real_pre += QBXTS;
      qmf_residual_imag_pre += QBXTS;

      qmf_residual_real_post += QBXTS;
      qmf_residual_imag_post += QBXTS;

      p_res_mdct += RFX2XMDCTCOEF;
    }
  }

  if (pstr_mps_state->arbitrary_downmix == 2) {
    WORD32 arbdmx_upd_qmf = pstr_mps_state->arbdmx_upd_qmf;
    WORD32 offset = pstr_mps_state->num_ott_boxes + pstr_mps_state->num_ttt_boxes;
    WORD32 in_ch = pstr_mps_state->num_input_channels;
    WORD32 rfpsf_max = pstr_mps_state->arbdmx_frames_per_spatial_frame;

    WORD32 *qmf_residual_real_pre = p_array_struct->qmf_residual_real_pre + offset * QBXTS;
    WORD32 *qmf_residual_imag_pre = p_array_struct->qmf_residual_imag_pre + offset * QBXTS;

    WORD32 *qmf_residual_real_post = p_array_struct->qmf_residual_real_post + offset * QBXTS;
    WORD32 *qmf_residual_imag_post = p_array_struct->qmf_residual_imag_post + offset * QBXTS;

    WORD32 *p_res_mdct = p_array_struct->res_mdct + offset * RFX2XMDCTCOEF;

    for (ch = 0; ch < in_ch; ch++) {
      WORD32 *res_mdct = p_res_mdct;
      qmf_global_offset = 0;

      p_qmf_residual_real_post = qmf_residual_real_post;
      p_qmf_residual_imag_post = qmf_residual_imag_post;
      for (qb = 0; qb < qmf_bands; qb++) {
        memset(p_qmf_residual_real_post, 0, time_slots_x4);
        memset(p_qmf_residual_imag_post, 0, time_slots_x4);

        p_qmf_residual_real_post += MAX_TIME_SLOTS;
        p_qmf_residual_imag_post += MAX_TIME_SLOTS;
      }

      for (rfpsf = 0; rfpsf < rfpsf_max; rfpsf++) {
        ixheaacd_mdct2qmf_process(
            arbdmx_upd_qmf, res_mdct, qmf_residual_real_pre, qmf_residual_real_post,
            qmf_residual_imag_pre, qmf_residual_imag_post,
            pstr_mps_state->res_block_type[offset + ch][rfpsf], qmf_global_offset,
            &(pstr_mps_state->ia_mps_dec_mps_table), scratch, time_slots);
        qmf_global_offset += arbdmx_upd_qmf;
        res_mdct += MDCTCOEFX2;
      }

      qmf_residual_real_pre += QBXTS;
      qmf_residual_imag_pre += QBXTS;

      qmf_residual_imag_post += QBXTS;
      qmf_residual_real_post += QBXTS;

      p_res_mdct += RFX2XMDCTCOEF;
    }
  }
  return;
}

VOID ixheaacd_hybrid_qmf_analysis(ia_heaac_mps_state_struct *pstr_mps_state) {
  WORD32 ch;
  WORD32 in_ch = pstr_mps_state->num_input_channels;
  WORD32 num_ott_boxes = pstr_mps_state->num_ott_boxes;
  WORD32 num_ttt_boxes = pstr_mps_state->num_ttt_boxes;
  WORD32 num_input_channels = in_ch;
  WORD32 qmf_bands = pstr_mps_state->qmf_bands;
  WORD32 time_slots = pstr_mps_state->time_slots;
  WORD32 hybrid_bands = pstr_mps_state->hybrid_bands;
  WORD32 num_parameter_bands = pstr_mps_state->num_parameter_bands;
  SIZE_T *kernels = pstr_mps_state->kernels;
  WORD32 *res_bands = pstr_mps_state->res_bands;
  WORD32 *index = pstr_mps_state->index;

  ia_mps_dec_thyb_filter_state_struct *hyb_filter_state =
      pstr_mps_state->mps_persistent_mem.hyb_filter_state;
  ia_mps_dec_reuse_array_struct *p_array_struct = pstr_mps_state->array_struct;

  ia_mps_dec_hybrid_tables_struct *hybrid_table_ptr =
      pstr_mps_state->ia_mps_dec_mps_table.hybrid_table_ptr;

  WORD32 *p_buf_real = p_array_struct->buf_real;
  WORD32 *p_buf_imag = p_array_struct->buf_imag;

  WORD32 *p_x_real = p_array_struct->x_real;
  WORD32 *p_x_imag = p_array_struct->x_imag;

  for (ch = 0; ch < in_ch; ch++) {
    ixheaacd_apply_ana_hyb_filt_bank_create_x(&hyb_filter_state[ch], p_buf_real, p_buf_imag,
                                              qmf_bands, time_slots, p_x_real, p_x_imag,
                                              hybrid_table_ptr);
    pstr_mps_state->index[ch] = hybrid_bands;

    p_buf_real += TSXHB;
    p_buf_imag += TSXHB;

    p_x_real += TSXHB;
    p_x_imag += TSXHB;
  }

  if ((pstr_mps_state->residual_coding) && (pstr_mps_state->up_mix_type != 2)) {
    WORD32 *qmf_residual_real = p_array_struct->qmf_residual_real_pre;
    WORD32 *qmf_residual_imag = p_array_struct->qmf_residual_imag_pre;

    WORD32 *p_dry_real = p_array_struct->w_dry_real;
    WORD32 *p_dry_imag = p_array_struct->w_dry_imag;

    for (ch = 0; ch < num_ott_boxes; ch++) {
      if (res_bands[ch] > 0) {
        ixheaacd_apply_ana_hyb_filt_bank_merge_res_decor(
            &hyb_filter_state[ch + num_input_channels], qmf_residual_real, qmf_residual_imag,
            qmf_bands, time_slots, p_dry_real, p_dry_imag, hybrid_table_ptr);
      }
      qmf_residual_real += QBXTS;
      qmf_residual_imag += QBXTS;

      p_dry_real += TSXHB;
      p_dry_imag += TSXHB;
    }

    for (ch = num_ott_boxes; ch < num_ott_boxes + num_ttt_boxes; ch++, in_ch++) {
      if (res_bands[ch] > 0) {
        ixheaacd_apply_ana_hyb_filt_bank_create_x_res(
            &hyb_filter_state[ch + num_input_channels], qmf_residual_real, qmf_residual_imag,
            qmf_bands, time_slots, p_x_real, p_x_imag, kernels, res_bands[ch], hybrid_bands,
            num_parameter_bands, &index[in_ch], hybrid_table_ptr);
      } else
        index[in_ch] = 0;

      qmf_residual_real += QBXTS;
      qmf_residual_imag += QBXTS;

      p_x_real += TSXHB;
      p_x_imag += TSXHB;
    }
  }

  in_ch = num_input_channels + num_ttt_boxes;
  if (pstr_mps_state->arbitrary_downmix == 2) {
    WORD32 offset = num_ott_boxes + num_ttt_boxes;

    WORD32 *qmf_residual_real = p_array_struct->qmf_residual_real_pre + offset * QBXTS;
    WORD32 *qmf_residual_imag = p_array_struct->qmf_residual_imag_pre + offset * QBXTS;

    p_x_real = p_array_struct->x_real + in_ch * TSXHB;
    p_x_imag = p_array_struct->x_imag + in_ch * TSXHB;
    for (ch = 0; ch < num_input_channels; ch++, in_ch++) {
      ixheaacd_apply_ana_hyb_filt_bank_create_x_res(
          &hyb_filter_state[offset + ch + num_input_channels], qmf_residual_real,
          qmf_residual_imag, qmf_bands, time_slots, p_x_real, p_x_imag, kernels,
          pstr_mps_state->arbdmx_residual_bands, hybrid_bands, num_parameter_bands, &index[in_ch],
          hybrid_table_ptr);

      qmf_residual_real += QBXTS;
      qmf_residual_imag += QBXTS;

      p_x_real += TSXHB;
      p_x_imag += TSXHB;
    }
  }
}

VOID ixheaacd_merge_res_decor(ia_heaac_mps_state_struct *pstr_mps_state) {
  WORD32 ts, qs, row, res;

  WORD32 temp_1;
  SIZE_T *idx;

  ia_mps_dec_reuse_array_struct *p_array_struct = pstr_mps_state->array_struct;
  ia_mps_dec_auxilary_struct *p_aux_struct = pstr_mps_state->aux_struct;
  WORD32 time_slots = pstr_mps_state->time_slots;
  WORD32 hybrid_bands = pstr_mps_state->hybrid_bands;
  WORD32 num_direct_signals = pstr_mps_state->num_direct_signals;
  WORD32 num_w_channels = pstr_mps_state->num_w_channels;
  WORD32 num_parameter_bands = pstr_mps_state->num_parameter_bands;
  SIZE_T *kernels_ptr = pstr_mps_state->kernels;

  WORD32 *p_buf_real, *p_buf_imag, *p_buf_re, *p_buf_im;
  WORD32 *buf_real_ch4, *buf_imag_ch4;
  WORD32 *buf_real_ch3, *buf_imag_ch3;

  p_buf_real = p_array_struct->buffer_real + TSXHBX5;
  p_buf_imag = p_array_struct->buffer_imag + TSXHBX5;

  for (ts = 0; ts < time_slots; ts++) {
    p_buf_re = p_buf_real;
    p_buf_im = p_buf_imag;

    buf_real_ch4 = p_buf_real - TSXHB;
    buf_imag_ch4 = p_buf_imag - TSXHB;

    buf_real_ch3 = buf_real_ch4 - TSXHB;
    buf_imag_ch3 = buf_imag_ch4 - TSXHB;

    for (qs = 0; qs < hybrid_bands; qs++) {
      if ((kernels_ptr[qs] < ((UWORD32)(p_aux_struct->ttt_config[0][0].stop_band)) &&
           p_aux_struct->ttt_config[0][0].use_ttt_decorr) ||
          (kernels_ptr[qs] >= ((UWORD32)p_aux_struct->ttt_config[1][0].start_band) &&
           p_aux_struct->ttt_config[1][0].use_ttt_decorr)) {
        temp_1 = (WORD32)ONE_BY_SQRT_TWO_Q30;

        *p_buf_re = ixheaacd_mps_mult32_shr_30(*p_buf_re, temp_1);
        *p_buf_re += (*buf_real_ch3 + *buf_real_ch4);

        *p_buf_im = ixheaacd_mps_mult32_shr_30(*p_buf_im, temp_1);
        *p_buf_im += (*buf_imag_ch3 + *buf_imag_ch4);
      }
      p_buf_re++;
      p_buf_im++;

      buf_real_ch4++;
      buf_imag_ch4++;

      buf_real_ch3++;
      buf_imag_ch3++;
    }
    p_buf_real += MAX_HYBRID_BANDS;
    p_buf_imag += MAX_HYBRID_BANDS;
  }

  if (pstr_mps_state->residual_coding) {
    for (row = num_direct_signals; row < num_w_channels; row++) {
      WORD32 resband;
      res = ixheaacd_get_res_idx(pstr_mps_state, row);
      resband = pstr_mps_state->res_bands[res];

      if (resband == 1 && (num_parameter_bands == 20 || num_parameter_bands == 28))
        pstr_mps_state->index[res] = 3;
      else {
        idx = &kernels_ptr[0];
        for (qs = 0; qs < hybrid_bands; qs++) {
          if (*idx++ >= (SIZE_T)resband) {
            pstr_mps_state->index[res] = qs;
            qs = hybrid_bands;
          }
        }
      }
    }
  }
}

VOID ixheaacd_create_w(ia_heaac_mps_state_struct *pstr_mps_state) {
  WORD32 k;
  ia_mps_dec_reuse_array_struct *p_array_struct = pstr_mps_state->array_struct;
  WORD32 num_direct_signals = pstr_mps_state->num_direct_signals;
  WORD32 counter = num_direct_signals + pstr_mps_state->num_decor_signals;
  WORD32 time_slots = pstr_mps_state->time_slots;
  WORD32 offset = num_direct_signals * TSXHB;
  WORD32 *p_buffer_real = p_array_struct->buf_real + offset;
  WORD32 *p_buffer_imag = p_array_struct->buf_imag + offset;

  WORD32 *p_buf_real = p_array_struct->buffer_real + offset;
  WORD32 *p_buf_imag = p_array_struct->buffer_imag + offset;

  for (k = num_direct_signals; k < counter; k++) {
    ixheaacd_decorr_apply(pstr_mps_state, time_slots, p_buffer_real, p_buffer_imag, p_buf_real,
                          p_buf_imag, k);

    p_buffer_real += TSXHB;
    p_buffer_imag += TSXHB;

    p_buf_real += TSXHB;
    p_buf_imag += TSXHB;
  }
  ixheaacd_merge_res_decor(pstr_mps_state);
}

VOID ixheaacd_update_buffers(ia_heaac_mps_state_struct *pstr_mps_state) {
  ia_mps_dec_reuse_array_struct *p_array_struct = pstr_mps_state->array_struct;
  WORD32 *temp_addr = p_array_struct->qmf_residual_real_post;
  p_array_struct->qmf_residual_real_post = p_array_struct->qmf_residual_real_pre;
  p_array_struct->qmf_residual_real_pre = temp_addr;

  temp_addr = p_array_struct->qmf_residual_imag_post;
  p_array_struct->qmf_residual_imag_post = p_array_struct->qmf_residual_imag_pre;
  p_array_struct->qmf_residual_imag_pre = temp_addr;

  p_array_struct->buffer_real = p_array_struct->qmf_residual_real_post;
  p_array_struct->buffer_imag = p_array_struct->qmf_residual_imag_post;

  p_array_struct->m1_param = (ia_mps_dec_m1_param_struct *)p_array_struct->buffer_real;
}
