use crate::silk::define::*;
use crate::silk::macros::*;
use crate::silk::sigproc_fix::*;
use crate::silk::structs::*;
use crate::silk::tables::*;

#[derive(Copy, Clone)]
pub struct NSQDelDecStruct {
    pub s_lpc_q14: [i32; MAX_SUB_FRAME_LENGTH + NSQ_LPC_BUF_LENGTH],
    pub rand_state: [i32; DECISION_DELAY],
    pub q_q10: [i32; DECISION_DELAY],
    pub xq_q14: [i32; DECISION_DELAY],
    pub pred_q15: [i32; DECISION_DELAY],
    pub shape_q14: [i32; DECISION_DELAY],
    pub s_ar2_q14: [i32; MAX_SHAPE_LPC_ORDER],
    pub lf_ar_q14: i32,
    pub diff_q14: i32,
    pub seed: i32,
    pub seed_init: i32,
    pub rd_q10: i32,
}

impl Default for NSQDelDecStruct {
    fn default() -> Self {
        Self {
            s_lpc_q14: [0; MAX_SUB_FRAME_LENGTH + NSQ_LPC_BUF_LENGTH],
            rand_state: [0; DECISION_DELAY],
            q_q10: [0; DECISION_DELAY],
            xq_q14: [0; DECISION_DELAY],
            pred_q15: [0; DECISION_DELAY],
            shape_q14: [0; DECISION_DELAY],
            s_ar2_q14: [0; MAX_SHAPE_LPC_ORDER],
            lf_ar_q14: 0,
            diff_q14: 0,
            seed: 0,
            seed_init: 0,
            rd_q10: 0,
        }
    }
}

#[derive(Copy, Clone, Default)]
pub struct NSQSampleStruct {
    pub q_q10: i32,
    pub rd_q10: i32,
    pub xq_q14: i32,
    pub lf_ar_q14: i32,
    pub diff_q14: i32,
    pub s_ltp_shp_q14: i32,
    pub lpc_exc_q14: i32,
}

pub type NSQSamplePair = [NSQSampleStruct; 2];

#[inline]
fn silk_nsq_del_dec_scale_states(
    ps_enc_c: &SilkEncoderStateCommon,
    nsq: &mut SilkNSQState,
    ps_del_dec: &mut [NSQDelDecStruct],
    x16: &[i16],
    x_sc_q10: &mut [i32],
    s_ltp: &[i16],
    s_ltp_q15: &mut [i32],
    subfr: usize,
    n_states_delayed_decision: i32,
    ltp_scale_q14: i32,
    gains_q16: &[i32],
    pitch_l: &[i32],
    signal_type: i32,
    decision_delay: i32,
) {
    let lag = pitch_l[subfr] as usize;
    let inv_gain_q31 = silk_inverse32_varq(gains_q16[subfr].max(1), 47);

    let inv_gain_q26 = silk_rshift_round(inv_gain_q31, 5);
    let n = ps_enc_c.subfr_length as usize;
    for (x_out, &x_in) in x_sc_q10[..n].iter_mut().zip(x16[..n].iter()) {
        *x_out = silk_smulww(x_in as i32, inv_gain_q26);
    }

    if nsq.rewhite_flag != 0 {
        let mut inv_gain_q31_scaled = inv_gain_q31;
        if subfr == 0 {
            inv_gain_q31_scaled = silk_lshift(silk_smulwb(inv_gain_q31, ltp_scale_q14), 2);
        }
        for i in (nsq.s_ltp_buf_idx as usize - lag - LTP_ORDER / 2)..(nsq.s_ltp_buf_idx as usize) {
            s_ltp_q15[i] = silk_smulwb(inv_gain_q31_scaled, s_ltp[i] as i32);
        }
    }

    if gains_q16[subfr] != nsq.prev_gain_q16 {
        let gain_adj_q16 = silk_div32_varq(nsq.prev_gain_q16, gains_q16[subfr], 16);

        for i in (nsq.s_ltp_shp_buf_idx as usize - ps_enc_c.ltp_mem_length as usize)
            ..(nsq.s_ltp_shp_buf_idx as usize)
        {
            nsq.s_ltp_shp_q14[i] = silk_smulww(gain_adj_q16, nsq.s_ltp_shp_q14[i]);
        }

        if signal_type == TYPE_VOICED && nsq.rewhite_flag == 0 {
            let ltp_start = nsq.s_ltp_buf_idx as usize - lag - LTP_ORDER / 2;
            let ltp_end = nsq.s_ltp_buf_idx as usize - decision_delay as usize;
            for v in s_ltp_q15[ltp_start..ltp_end].iter_mut() {
                *v = silk_smulww(gain_adj_q16, *v);
            }
        }

        let n_states = (n_states_delayed_decision as usize).min(NSQ_MAX_STATES_OPERATING);
        for k in 0..n_states {
            let ps_dd = &mut ps_del_dec[k];
            ps_dd.lf_ar_q14 = silk_smulww(gain_adj_q16, ps_dd.lf_ar_q14);
            ps_dd.diff_q14 = silk_smulww(gain_adj_q16, ps_dd.diff_q14);
            for i in 0..NSQ_LPC_BUF_LENGTH {
                ps_dd.s_lpc_q14[i] = silk_smulww(gain_adj_q16, ps_dd.s_lpc_q14[i]);
            }
            for i in 0..MAX_SHAPE_LPC_ORDER {
                ps_dd.s_ar2_q14[i] = silk_smulww(gain_adj_q16, ps_dd.s_ar2_q14[i]);
            }
            for i in 0..DECISION_DELAY {
                ps_dd.pred_q15[i] = silk_smulww(gain_adj_q16, ps_dd.pred_q15[i]);
                ps_dd.shape_q14[i] = silk_smulww(gain_adj_q16, ps_dd.shape_q14[i]);
            }
        }
        nsq.prev_gain_q16 = gains_q16[subfr];
    }
}

#[inline]
fn silk_noise_shape_quantizer_short_prediction(
    ps_lpc_q14: &[i32],
    idx: usize,
    a_q12: &[i16],
    predict_lpc_order: i32,
) -> i32 {
    let mut out = silk_rshift(predict_lpc_order, 1);
    for j in 0..predict_lpc_order as usize {
        out = silk_smlawb(out, ps_lpc_q14[idx - j], a_q12[j] as i32);
    }
    out
}

pub fn silk_noise_shape_quantizer_del_dec(
    nsq: &mut SilkNSQState,
    ps_del_dec: &mut [NSQDelDecStruct],
    signal_type: i32,
    x_q10: &[i32],
    pulses: &mut [i8],
    pulses_offset: i32,
    xq_ptr: i32,
    s_ltp_q15: &mut [i32],
    delayed_ga_q10: &mut [i32],
    a_q12: &[i16],
    b_q14: &[i16],
    ar_shp_q13: &[i16],
    lag: i32,
    harm_shape_fir_packed_q14: i32,
    tilt_q14: i32,
    lf_shp_q14: i32,
    gain_q16: i32,
    lambda_q10: i32,
    offset_q10: i32,
    length: i32,
    subfr: i32,
    shaping_lpc_order: i32,
    predict_lpc_order: i32,
    warping_q16: i32,
    n_states_delayed_decision: i32,
    smpl_buf_idx: &mut i32,
    decision_delay: i32,
    _frame_counter: i32,
) {
    let mut ps_sample_state = [[NSQSampleStruct::default(); 2]; NSQ_MAX_STATES_OPERATING];
    let gain_q10 = silk_rshift(gain_q16, 6);

    let n_states = (n_states_delayed_decision as usize).min(NSQ_MAX_STATES_OPERATING);

    let pred_lag_ptr_base = nsq.s_ltp_buf_idx - lag + LTP_ORDER as i32 / 2;
    let shp_lag_ptr_base = nsq.s_ltp_shp_buf_idx - lag + HARM_SHAPE_FIR_TAPS as i32 / 2;

    for i in 0..length {
        let idx = i as usize;
        let mut ltp_pred_q14 = 0;
        if signal_type == TYPE_VOICED {
            let pred_lag_idx_calc = pred_lag_ptr_base + i;
            if pred_lag_idx_calc >= LTP_ORDER as i32 && pred_lag_idx_calc < s_ltp_q15.len() as i32 {
                let pred_lag_idx = pred_lag_idx_calc as usize;
                ltp_pred_q14 = 2;
                ltp_pred_q14 = silk_smlawb(ltp_pred_q14, s_ltp_q15[pred_lag_idx], b_q14[0] as i32);
                ltp_pred_q14 =
                    silk_smlawb(ltp_pred_q14, s_ltp_q15[pred_lag_idx - 1], b_q14[1] as i32);
                ltp_pred_q14 =
                    silk_smlawb(ltp_pred_q14, s_ltp_q15[pred_lag_idx - 2], b_q14[2] as i32);
                ltp_pred_q14 =
                    silk_smlawb(ltp_pred_q14, s_ltp_q15[pred_lag_idx - 3], b_q14[3] as i32);
                ltp_pred_q14 =
                    silk_smlawb(ltp_pred_q14, s_ltp_q15[pred_lag_idx - 4], b_q14[4] as i32);
                ltp_pred_q14 = silk_lshift(ltp_pred_q14, 1);
            }
        }

        let mut n_ltp_q14 = 0;
        if lag > 0 {
            let shp_lag_idx_calc = shp_lag_ptr_base + i;
            if shp_lag_idx_calc >= HARM_SHAPE_FIR_TAPS as i32
                && shp_lag_idx_calc < nsq.s_ltp_shp_q14.len() as i32
            {
                let shp_lag_idx = shp_lag_idx_calc as usize;
                n_ltp_q14 = silk_smulwb(
                    silk_add_sat32(
                        nsq.s_ltp_shp_q14[shp_lag_idx],
                        nsq.s_ltp_shp_q14[shp_lag_idx - 2],
                    ),
                    harm_shape_fir_packed_q14,
                );
                n_ltp_q14 = silk_smlawt(
                    n_ltp_q14,
                    nsq.s_ltp_shp_q14[shp_lag_idx - 1],
                    harm_shape_fir_packed_q14,
                );
                n_ltp_q14 = silk_sub_lshift32(ltp_pred_q14, n_ltp_q14, 2);
            }
        }

        for k in 0..n_states {
            let ps_dd = &mut ps_del_dec[k];
            let ps_ss = &mut ps_sample_state[k];

            ps_dd.seed = silk_rand(ps_dd.seed);
            let ps_lpc_q14_idx = NSQ_LPC_BUF_LENGTH - 1 + idx;
            let lpc_pred_q14 = silk_lshift(
                silk_noise_shape_quantizer_short_prediction(
                    &ps_dd.s_lpc_q14,
                    ps_lpc_q14_idx,
                    a_q12,
                    predict_lpc_order,
                ),
                4,
            );

            let mut tmp2 = silk_smlawb(ps_dd.diff_q14, ps_dd.s_ar2_q14[0], warping_q16);
            let mut tmp1 = silk_smlawb(
                ps_dd.s_ar2_q14[0],
                silk_sub32_ovflw(ps_dd.s_ar2_q14[1], tmp2),
                warping_q16,
            );
            ps_dd.s_ar2_q14[0] = tmp2;
            let mut n_ar_q14 = silk_rshift(shaping_lpc_order, 1);
            n_ar_q14 = silk_smlawb(n_ar_q14, tmp2, ar_shp_q13[0] as i32);
            for j in (2..shaping_lpc_order as usize).step_by(2) {
                tmp2 = silk_smlawb(
                    ps_dd.s_ar2_q14[j - 1],
                    silk_sub32_ovflw(ps_dd.s_ar2_q14[j], tmp1),
                    warping_q16,
                );
                ps_dd.s_ar2_q14[j - 1] = tmp1;
                n_ar_q14 = silk_smlawb(n_ar_q14, tmp1, ar_shp_q13[j - 1] as i32);
                tmp1 = silk_smlawb(
                    ps_dd.s_ar2_q14[j],
                    silk_sub32_ovflw(ps_dd.s_ar2_q14[j + 1], tmp2),
                    warping_q16,
                );
                ps_dd.s_ar2_q14[j] = tmp2;
                n_ar_q14 = silk_smlawb(n_ar_q14, tmp2, ar_shp_q13[j] as i32);
            }
            ps_dd.s_ar2_q14[shaping_lpc_order as usize - 1] = tmp1;
            n_ar_q14 = silk_smlawb(
                n_ar_q14,
                tmp1,
                ar_shp_q13[shaping_lpc_order as usize - 1] as i32,
            );

            n_ar_q14 = silk_lshift(n_ar_q14, 1);
            n_ar_q14 = silk_smlawb(n_ar_q14, ps_dd.lf_ar_q14, tilt_q14);
            n_ar_q14 = silk_lshift(n_ar_q14, 2);

            let smpl_idx = (*smpl_buf_idx as usize).min(DECISION_DELAY - 1);
            let mut n_lf_q14 = silk_smulwb(ps_dd.shape_q14[smpl_idx], lf_shp_q14);
            n_lf_q14 = silk_smlawt(n_lf_q14, ps_dd.lf_ar_q14, lf_shp_q14);
            n_lf_q14 = silk_lshift(n_lf_q14, 2);

            let tmp1_val = silk_sub_sat32(
                silk_add32_ovflw(n_ltp_q14, lpc_pred_q14),
                silk_add_sat32(n_ar_q14, n_lf_q14),
            );
            let r_q10 = x_q10[idx] - silk_rshift_round(tmp1_val, 4);

            let r_q10_signed = if ps_dd.seed < 0 { -r_q10 } else { r_q10 };
            let r_q10_signed = silk_limit_32(r_q10_signed, -(31 << 10), 30 << 10);

            let q1_q10_in = r_q10_signed - offset_q10;
            let mut q1_q0 = silk_rshift(q1_q10_in, 10);
            if lambda_q10 > 2048 {
                let rdo_offset = lambda_q10 / 2 - 512;
                if q1_q10_in > rdo_offset {
                    q1_q0 = silk_rshift(q1_q10_in - rdo_offset, 10);
                } else if q1_q10_in < -rdo_offset {
                    q1_q0 = silk_rshift(q1_q10_in + rdo_offset, 10);
                } else if q1_q10_in < 0 {
                    q1_q0 = -1;
                } else {
                    q1_q0 = 0;
                }
            }

            let (rd1_q10, rd2_q10, q1_q10_val, q2_q10_val);
            if q1_q0 > 0 {
                q1_q10_val =
                    silk_sub32(silk_lshift(q1_q0, 10), QUANT_LEVEL_ADJUST_Q10) + offset_q10;
                q2_q10_val = q1_q10_val + 1024;
                rd1_q10 = silk_smulbb(q1_q10_val, lambda_q10);
                rd2_q10 = silk_smulbb(q2_q10_val, lambda_q10);
            } else if q1_q0 == 0 {
                q1_q10_val = offset_q10;
                q2_q10_val = q1_q10_val + 1024 - QUANT_LEVEL_ADJUST_Q10;
                rd1_q10 = silk_smulbb(q1_q10_val, lambda_q10);
                rd2_q10 = silk_smulbb(q2_q10_val, lambda_q10);
            } else if q1_q0 == -1 {
                q2_q10_val = offset_q10;
                q1_q10_val = q2_q10_val - (1024 - QUANT_LEVEL_ADJUST_Q10);
                rd1_q10 = silk_smulbb(-q1_q10_val, lambda_q10);
                rd2_q10 = silk_smulbb(q2_q10_val, lambda_q10);
            } else {
                q1_q10_val =
                    silk_add32(silk_lshift(q1_q0, 10), QUANT_LEVEL_ADJUST_Q10) + offset_q10;
                q2_q10_val = q1_q10_val + 1024;
                rd1_q10 = silk_smulbb(-q1_q10_val, lambda_q10);
                rd2_q10 = silk_smulbb(-q2_q10_val, lambda_q10);
            }

            let mut rr_q10 = r_q10_signed - q1_q10_val;
            let rd1_q10_final = silk_rshift(silk_smlabb(rd1_q10, rr_q10, rr_q10), 10);
            rr_q10 = r_q10_signed - q2_q10_val;
            let rd2_q10_final = silk_rshift(silk_smlabb(rd2_q10, rr_q10, rr_q10), 10);

            if rd1_q10_final < rd2_q10_final {
                ps_ss[0].rd_q10 = ps_dd.rd_q10 + rd1_q10_final;
                ps_ss[1].rd_q10 = ps_dd.rd_q10 + rd2_q10_final;
                ps_ss[0].q_q10 = q1_q10_val;
                ps_ss[1].q_q10 = q2_q10_val;
            } else {
                ps_ss[0].rd_q10 = ps_dd.rd_q10 + rd2_q10_final;
                ps_ss[1].rd_q10 = ps_dd.rd_q10 + rd1_q10_final;
                ps_ss[0].q_q10 = q2_q10_val;
                ps_ss[1].q_q10 = q1_q10_val;
            }

            for j in 0..2 {
                let mut exc_q14 = silk_lshift(ps_ss[j].q_q10, 4);
                if ps_dd.seed < 0 {
                    exc_q14 = -exc_q14;
                }
                let lpc_exc_q14 = silk_add32(exc_q14, ltp_pred_q14);
                let xq_q14 = silk_add32_ovflw(lpc_exc_q14, lpc_pred_q14);
                ps_ss[j].diff_q14 = silk_sub32_ovflw(xq_q14, silk_lshift(x_q10[idx], 4));
                let s_lf_ar_shp_q14 = silk_sub32_ovflw(ps_ss[j].diff_q14, n_ar_q14);
                ps_ss[j].s_ltp_shp_q14 = silk_sub_sat32(s_lf_ar_shp_q14, n_lf_q14);
                ps_ss[j].lf_ar_q14 = s_lf_ar_shp_q14;
                ps_ss[j].lpc_exc_q14 = lpc_exc_q14;
                ps_ss[j].xq_q14 = xq_q14;
            }
        }

        *smpl_buf_idx = (*smpl_buf_idx - 1 + DECISION_DELAY as i32) % DECISION_DELAY as i32;
        let last_smple_idx = ((*smpl_buf_idx + decision_delay) % DECISION_DELAY as i32) as usize;

        let mut winner_ind = 0;
        let mut rd_min_q10 = ps_sample_state[0][0].rd_q10;
        for k in 1..n_states {
            if ps_sample_state[k][0].rd_q10 < rd_min_q10 {
                rd_min_q10 = ps_sample_state[k][0].rd_q10;
                winner_ind = k;
            }
        }

        let winner_rand_state = ps_del_dec[winner_ind].rand_state[last_smple_idx];
        for k in 0..n_states {
            if ps_del_dec[k].rand_state[last_smple_idx] != winner_rand_state {
                ps_sample_state[k][0].rd_q10 =
                    ps_sample_state[k][0].rd_q10.saturating_add(i32::MAX >> 4);
                ps_sample_state[k][1].rd_q10 =
                    ps_sample_state[k][1].rd_q10.saturating_add(i32::MAX >> 4);
            }
        }

        let mut rd_max_q10 = ps_sample_state[0][0].rd_q10;
        let mut rd_max_ind = 0;
        let mut rd_min_q10_2 = ps_sample_state[0][1].rd_q10;
        let mut rd_min_ind = 0;
        for k in 1..n_states {
            if ps_sample_state[k][0].rd_q10 > rd_max_q10 {
                rd_max_q10 = ps_sample_state[k][0].rd_q10;
                rd_max_ind = k;
            }
            if ps_sample_state[k][1].rd_q10 < rd_min_q10_2 {
                rd_min_q10_2 = ps_sample_state[k][1].rd_q10;
                rd_min_ind = k;
            }
        }

        if rd_min_q10_2 < rd_max_q10 {
            if rd_min_ind != rd_max_ind {
                let (min_state, max_state) = if rd_min_ind < rd_max_ind {
                    let (left, right) = ps_del_dec.split_at_mut(rd_max_ind);
                    (&left[rd_min_ind], &mut right[0])
                } else {
                    let (left, right) = ps_del_dec.split_at_mut(rd_min_ind);
                    (&right[0], &mut left[rd_max_ind])
                };
                max_state.s_lpc_q14[idx..].copy_from_slice(&min_state.s_lpc_q14[idx..]);
                max_state.rand_state = min_state.rand_state;
                max_state.q_q10 = min_state.q_q10;
                max_state.xq_q14 = min_state.xq_q14;
                max_state.pred_q15 = min_state.pred_q15;
                max_state.shape_q14 = min_state.shape_q14;
                max_state.s_ar2_q14 = min_state.s_ar2_q14;
                max_state.lf_ar_q14 = min_state.lf_ar_q14;
                max_state.diff_q14 = min_state.diff_q14;
                max_state.seed = min_state.seed;
                max_state.seed_init = min_state.seed_init;
                max_state.rd_q10 = min_state.rd_q10;
            }

            ps_sample_state[rd_max_ind][0] = ps_sample_state[rd_min_ind][1];
        }

        let ps_dd = &ps_del_dec[winner_ind];
        if subfr > 0 || i >= decision_delay {
            let pulse_idx = (pulses_offset + i - decision_delay) as isize;
            let xq_idx = (xq_ptr + i - decision_delay) as isize;
            let shp_idx = (nsq.s_ltp_shp_buf_idx - decision_delay) as isize;
            let ltp_idx = (nsq.s_ltp_buf_idx - decision_delay) as isize;

            if pulse_idx >= 0 && pulse_idx < pulses.len() as isize {
                pulses[pulse_idx as usize] =
                    silk_rshift_round(ps_dd.q_q10[last_smple_idx], 10) as i8;
            }
            if xq_idx >= 0 && xq_idx < nsq.xq.len() as isize {
                nsq.xq[xq_idx as usize] = silk_sat16(silk_rshift_round(
                    silk_smulww(ps_dd.xq_q14[last_smple_idx], delayed_ga_q10[last_smple_idx]),
                    8,
                )) as i16;
            }
            if shp_idx >= 0 && shp_idx < nsq.s_ltp_shp_q14.len() as isize {
                nsq.s_ltp_shp_q14[shp_idx as usize] = ps_dd.shape_q14[last_smple_idx];
            }
            if ltp_idx >= 0 && ltp_idx < s_ltp_q15.len() as isize {
                s_ltp_q15[ltp_idx as usize] = ps_dd.pred_q15[last_smple_idx];
            }
        }
        nsq.s_ltp_shp_buf_idx += 1;
        nsq.s_ltp_buf_idx += 1;

        for k in 0..n_states {
            let ps_ss = &ps_sample_state[k][0];
            let ps_dd = &mut ps_del_dec[k];
            ps_dd.lf_ar_q14 = ps_ss.lf_ar_q14;
            ps_dd.diff_q14 = ps_ss.diff_q14;

            let lpc_idx = NSQ_LPC_BUF_LENGTH + idx;
            if lpc_idx < ps_dd.s_lpc_q14.len() {
                ps_dd.s_lpc_q14[lpc_idx] = ps_ss.xq_q14;
            }
            let smpl_idx = (*smpl_buf_idx as usize).min(DECISION_DELAY - 1);
            ps_dd.xq_q14[smpl_idx] = ps_ss.xq_q14;
            ps_dd.q_q10[smpl_idx] = ps_ss.q_q10;
            ps_dd.pred_q15[smpl_idx] = silk_lshift(ps_ss.lpc_exc_q14, 1);
            ps_dd.shape_q14[smpl_idx] = ps_ss.s_ltp_shp_q14;
            ps_dd.seed = silk_add32_ovflw(ps_dd.seed, silk_rshift_round(ps_ss.q_q10, 10));
            ps_dd.rand_state[smpl_idx] = ps_dd.seed;
            ps_dd.rd_q10 = ps_ss.rd_q10;
        }
        let smpl_idx = (*smpl_buf_idx as usize).min(DECISION_DELAY - 1);
        delayed_ga_q10[smpl_idx] = gain_q10;
    }
    for k in 0..n_states {
        let ps_dd = &mut ps_del_dec[k];
        let mut tmp = [0i32; NSQ_LPC_BUF_LENGTH];
        tmp.copy_from_slice(
            &ps_dd.s_lpc_q14[length as usize..length as usize + NSQ_LPC_BUF_LENGTH],
        );
        ps_dd.s_lpc_q14[..NSQ_LPC_BUF_LENGTH].copy_from_slice(&tmp);
    }
}

pub fn silk_nsq_del_dec(
    ps_common: &SilkEncoderStateCommon,
    ps_nsq: &mut SilkNSQState,
    ps_indices: &SideInfoIndices,

    x16: &[i16],
    pulses: &mut [i8],
    pred_coef_q12: &[i16],
    ltp_coef_q14: &[i16],
    ar_q13: &[i16],
    harm_shape_gain_q14: &[i32],
    tilt_q14: &[i32],
    lf_shp_q14: &[i32],
    gains_q16: &[i32],
    pitch_l: &[i32],
    lambda_q10: i32,
    ltp_scale_q14: i32,
) -> i32 {
    let mut x_sc_q10 = [0i32; MAX_SUB_FRAME_LENGTH];
    let mut delayed_ga_q10 = [0i32; DECISION_DELAY];
    let mut s_ltp_q15 = [0i32; LTP_MEM_LENGTH_MS * MAX_FS_KHZ + MAX_FRAME_LENGTH];
    let mut s_ltp = [0i16; LTP_MEM_LENGTH_MS * MAX_FS_KHZ + MAX_FRAME_LENGTH];
    let mut ps_del_dec = [NSQDelDecStruct::default(); NSQ_MAX_STATES_OPERATING];

    let mut lag = ps_nsq.lag_prev;

    let n_states = (ps_common.n_states_delayed_decision as usize).min(NSQ_MAX_STATES_OPERATING);

    for k in 0..n_states {
        let ps_dd = &mut ps_del_dec[k];
        ps_dd.seed = (k as i32 + ps_indices.seed as i32) & 3;
        ps_dd.seed_init = ps_dd.seed;
        ps_dd.rd_q10 = 0;
        ps_dd.lf_ar_q14 = ps_nsq.s_lf_ar_q14;
        ps_dd.diff_q14 = ps_nsq.s_diff_shp_q14;

        if ps_common.ltp_mem_length > 0 {
            ps_dd.shape_q14[0] = ps_nsq.s_ltp_shp_q14[ps_common.ltp_mem_length as usize - 1];
        }
        ps_dd.s_lpc_q14[..NSQ_LPC_BUF_LENGTH]
            .copy_from_slice(&ps_nsq.s_lpc_q14[..NSQ_LPC_BUF_LENGTH]);
        ps_dd.s_ar2_q14.copy_from_slice(&ps_nsq.s_ar2_q14);
    }

    let offset_q10 = SILK_QUANT_OFFSETS_Q10[(ps_indices.signal_type >> 1) as usize]
        [ps_indices.quant_offset_type as usize] as i32;
    let mut smpl_buf_idx = 0i32;
    let mut decision_delay = (DECISION_DELAY as i32).min(ps_common.subfr_length);

    if ps_indices.signal_type as i32 == TYPE_VOICED {
        for k in 0..ps_common.nb_subfr as usize {
            let pitch_constraint = pitch_l[k] - LTP_ORDER as i32 / 2 - 1;
            if pitch_constraint > 0 {
                decision_delay = decision_delay.min(pitch_constraint);
            }
        }
    } else if lag > 0 {
        let lag_constraint = lag - LTP_ORDER as i32 / 2 - 1;
        if lag_constraint > 0 {
            decision_delay = decision_delay.min(lag_constraint);
        }
    }

    let lsf_interpolation_flag = if ps_indices.nlsf_interp_coef_q2 == 4 {
        0
    } else {
        1
    };

    ps_nsq.s_ltp_shp_buf_idx = ps_common.ltp_mem_length;
    ps_nsq.s_ltp_buf_idx = ps_common.ltp_mem_length;

    let mut x_ptr = 0;
    let mut pulses_ptr = 0;
    let mut xq_ptr = ps_common.ltp_mem_length as usize;
    let mut subfr_nsq = 0;

    for k in 0..ps_common.nb_subfr as usize {
        let a_q12 =
            &pred_coef_q12[((k >> 1) | (1 - lsf_interpolation_flag as usize)) * MAX_LPC_ORDER..];
        let b_q14 = &ltp_coef_q14[k * LTP_ORDER..];
        let ar_shp_q13 = &ar_q13[k * MAX_SHAPE_LPC_ORDER..];

        let harm_shape_gain = harm_shape_gain_q14[k];
        let mut harm_shape_fir_packed_q14 = silk_rshift(harm_shape_gain, 2);
        harm_shape_fir_packed_q14 |= silk_lshift(silk_rshift(harm_shape_gain, 1), 16);

        ps_nsq.rewhite_flag = 0;
        if ps_indices.signal_type as i32 == TYPE_VOICED {
            lag = pitch_l[k];
            if (k & (3 - silk_lshift(lsf_interpolation_flag, 1) as usize)) == 0 {
                if k == 2 {
                    let mut rd_min_q10 = ps_del_dec[0].rd_q10;
                    let mut winner_ind = 0;
                    for i in 1..n_states {
                        if ps_del_dec[i].rd_q10 < rd_min_q10 {
                            rd_min_q10 = ps_del_dec[i].rd_q10;
                            winner_ind = i;
                        }
                    }
                    for i in 0..n_states {
                        if i != winner_ind {
                            ps_del_dec[i].rd_q10 =
                                ps_del_dec[i].rd_q10.saturating_add(i32::MAX >> 4);
                        }
                    }

                    let ps_dd = &ps_del_dec[winner_ind];
                    let mut last_smple_idx =
                        (smpl_buf_idx + decision_delay) % DECISION_DELAY as i32;
                    for i in 0..decision_delay {
                        last_smple_idx =
                            (last_smple_idx - 1 + DECISION_DELAY as i32) % DECISION_DELAY as i32;
                        let pulse_idx = (pulses_ptr as i32 + i - decision_delay) as isize;
                        let xq_idx = (xq_ptr as i32 + i - decision_delay) as isize;
                        let shp_idx = (ps_nsq.s_ltp_shp_buf_idx + i - decision_delay) as isize;
                        if pulse_idx >= 0 && xq_idx >= 0 && shp_idx >= 0 {
                            pulses[pulse_idx as usize] =
                                silk_rshift_round(ps_dd.q_q10[last_smple_idx as usize], 10) as i8;
                            ps_nsq.xq[xq_idx as usize] = silk_sat16(silk_rshift_round(
                                silk_smulww(ps_dd.xq_q14[last_smple_idx as usize], gains_q16[1]),
                                14,
                            )) as i16;
                            ps_nsq.s_ltp_shp_q14[shp_idx as usize] =
                                ps_dd.shape_q14[last_smple_idx as usize];
                        }
                    }
                    subfr_nsq = 0;
                }

                let start_idx_calc = ps_common.ltp_mem_length
                    - lag
                    - ps_common.predict_lpc_order
                    - LTP_ORDER as i32 / 2;
                if start_idx_calc < 0 {
                    continue;
                }
                let start_idx = start_idx_calc as usize;
                let xq_start = start_idx + k * ps_common.subfr_length as usize;
                let filter_len = ps_common.ltp_mem_length as usize - start_idx;

                if start_idx + filter_len > s_ltp.len() || xq_start + filter_len > ps_nsq.xq.len() {
                    continue;
                }
                silk_lpc_analysis_filter(
                    &mut s_ltp[start_idx..],
                    &ps_nsq.xq[xq_start..],
                    a_q12,
                    filter_len,
                    ps_common.predict_lpc_order as usize,
                    0,
                );
                ps_nsq.s_ltp_buf_idx = ps_common.ltp_mem_length;
                ps_nsq.rewhite_flag = 1;
            }
        }

        silk_nsq_del_dec_scale_states(
            ps_common,
            ps_nsq,
            &mut ps_del_dec,
            &x16[x_ptr..],
            &mut x_sc_q10,
            &s_ltp,
            &mut s_ltp_q15,
            k,
            ps_common.n_states_delayed_decision,
            ltp_scale_q14,
            gains_q16,
            pitch_l,
            ps_indices.signal_type as i32,
            decision_delay,
        );

        silk_noise_shape_quantizer_del_dec(
            ps_nsq,
            &mut ps_del_dec,
            ps_indices.signal_type as i32,
            &x_sc_q10,
            pulses,
            pulses_ptr as i32,
            xq_ptr as i32,
            &mut s_ltp_q15,
            &mut delayed_ga_q10,
            a_q12,
            b_q14,
            ar_shp_q13,
            lag,
            harm_shape_fir_packed_q14,
            tilt_q14[k],
            lf_shp_q14[k],
            gains_q16[k],
            lambda_q10,
            offset_q10,
            ps_common.subfr_length,
            subfr_nsq,
            ps_common.shaping_lpc_order,
            ps_common.predict_lpc_order,
            ps_common.warping_q16,
            ps_common.n_states_delayed_decision,
            &mut smpl_buf_idx,
            decision_delay,
            ps_common.frame_counter,
        );

        x_ptr += ps_common.subfr_length as usize;
        pulses_ptr += ps_common.subfr_length as usize;
        xq_ptr += ps_common.subfr_length as usize;
        subfr_nsq += 1;
    }

    let mut rd_min_q10 = ps_del_dec[0].rd_q10;
    let mut winner_ind = 0;
    for k in 1..n_states {
        if ps_del_dec[k].rd_q10 < rd_min_q10 {
            rd_min_q10 = ps_del_dec[k].rd_q10;
            winner_ind = k;
        }
    }

    let ps_dd = &ps_del_dec[winner_ind];
    ps_nsq.s_lf_ar_q14 = ps_dd.lf_ar_q14;
    ps_nsq.s_diff_shp_q14 = ps_dd.diff_q14;
    ps_nsq.lag_prev = pitch_l[ps_common.nb_subfr as usize - 1];

    let gain_q10_final = silk_rshift(gains_q16[ps_common.nb_subfr as usize - 1], 6);
    let mut last_smple_idx = smpl_buf_idx + decision_delay;
    for i in 0..decision_delay {
        last_smple_idx = (last_smple_idx - 1 + DECISION_DELAY as i32) % DECISION_DELAY as i32;
        let pulse_idx = (pulses_ptr as i32 + i - decision_delay) as isize;
        let xq_idx = (xq_ptr as i32 + i - decision_delay) as isize;
        if pulse_idx >= 0 && (pulse_idx as usize) < pulses.len() {
            pulses[pulse_idx as usize] =
                silk_rshift_round(ps_dd.q_q10[last_smple_idx as usize], 10) as i8;
        }
        if xq_idx >= 0 && (xq_idx as usize) < ps_nsq.xq.len() {
            ps_nsq.xq[xq_idx as usize] = silk_sat16(silk_rshift_round(
                silk_smulww(ps_dd.xq_q14[last_smple_idx as usize], gain_q10_final),
                8,
            )) as i16;
        }
        let shp_idx = (ps_nsq.s_ltp_shp_buf_idx - decision_delay + i) as isize;
        if shp_idx >= 0 && (shp_idx as usize) < ps_nsq.s_ltp_shp_q14.len() {
            ps_nsq.s_ltp_shp_q14[shp_idx as usize] = ps_dd.shape_q14[last_smple_idx as usize];
        }
    }

    let subfr_len = ps_common.subfr_length as usize;
    ps_nsq.s_lpc_q14[..NSQ_LPC_BUF_LENGTH]
        .copy_from_slice(&ps_dd.s_lpc_q14[subfr_len..subfr_len + NSQ_LPC_BUF_LENGTH]);
    ps_nsq.s_ar2_q14.copy_from_slice(&ps_dd.s_ar2_q14);

    let ltp_mem_len = ps_common.ltp_mem_length as usize;
    let frame_len = ps_common.frame_length as usize;
    ps_nsq.xq.copy_within(frame_len..frame_len + ltp_mem_len, 0);
    ps_nsq
        .s_ltp_shp_q14
        .copy_within(frame_len..frame_len + ltp_mem_len, 0);

    ps_del_dec[winner_ind].seed_init
}
