use crate::silk::define::{
    TRANSITION_FRAMES, TRANSITION_INT_NUM, TRANSITION_INT_STEPS, TRANSITION_NA, TRANSITION_NB,
};
use crate::silk::macros::{silk_limit, silk_lshift, silk_rshift, silk_smlawb};
use crate::silk::sigproc_fix::silk_biquad_alt_stride1;
use crate::silk::structs::SilkLPState;
use crate::silk::tables::{SILK_TRANSITION_LP_A_Q28, SILK_TRANSITION_LP_B_Q28};

fn silk_lp_interpolate_filter_taps(
    b_q28: &mut [i32; TRANSITION_NB],
    a_q28: &mut [i32; TRANSITION_NA],
    ind: usize,
    fac_q16: i32,
) {
    if ind < TRANSITION_INT_NUM - 1 {
        if fac_q16 > 0 {
            if fac_q16 < 32768 {
                for nb in 0..TRANSITION_NB {
                    b_q28[nb] = silk_smlawb(
                        SILK_TRANSITION_LP_B_Q28[ind][nb],
                        SILK_TRANSITION_LP_B_Q28[ind + 1][nb] - SILK_TRANSITION_LP_B_Q28[ind][nb],
                        fac_q16,
                    );
                }
                for na in 0..TRANSITION_NA {
                    a_q28[na] = silk_smlawb(
                        SILK_TRANSITION_LP_A_Q28[ind][na],
                        SILK_TRANSITION_LP_A_Q28[ind + 1][na] - SILK_TRANSITION_LP_A_Q28[ind][na],
                        fac_q16,
                    );
                }
            } else {
                for nb in 0..TRANSITION_NB {
                    b_q28[nb] = silk_smlawb(
                        SILK_TRANSITION_LP_B_Q28[ind + 1][nb],
                        SILK_TRANSITION_LP_B_Q28[ind + 1][nb] - SILK_TRANSITION_LP_B_Q28[ind][nb],
                        fac_q16 - (1 << 16),
                    );
                }
                for na in 0..TRANSITION_NA {
                    a_q28[na] = silk_smlawb(
                        SILK_TRANSITION_LP_A_Q28[ind + 1][na],
                        SILK_TRANSITION_LP_A_Q28[ind + 1][na] - SILK_TRANSITION_LP_A_Q28[ind][na],
                        fac_q16 - (1 << 16),
                    );
                }
            }
        } else {
            *b_q28 = SILK_TRANSITION_LP_B_Q28[ind];
            *a_q28 = SILK_TRANSITION_LP_A_Q28[ind];
        }
    } else {
        *b_q28 = SILK_TRANSITION_LP_B_Q28[TRANSITION_INT_NUM - 1];
        *a_q28 = SILK_TRANSITION_LP_A_Q28[TRANSITION_INT_NUM - 1];
    }
}

pub fn silk_lp_variable_cutoff(ps_lp: &mut SilkLPState, frame: &mut [i16], frame_length: usize) {
    let mut b_q28 = [0i32; TRANSITION_NB];
    let mut a_q28 = [0i32; TRANSITION_NA];
    let mut fac_q16: i32;
    let ind: usize;

    if ps_lp.mode != 0 {
        if TRANSITION_INT_STEPS == 64 {
            fac_q16 = silk_lshift(TRANSITION_FRAMES - ps_lp.transition_frame_no, 16 - 6);
        } else {
            fac_q16 = (silk_lshift(TRANSITION_FRAMES - ps_lp.transition_frame_no, 16))
                / TRANSITION_FRAMES;
        }

        ind = silk_rshift(fac_q16, 16) as usize;
        fac_q16 -= silk_lshift(ind as i32, 16);

        silk_lp_interpolate_filter_taps(&mut b_q28, &mut a_q28, ind, fac_q16);

        ps_lp.transition_frame_no =
            silk_limit(ps_lp.transition_frame_no + ps_lp.mode, 0, TRANSITION_FRAMES);

        silk_biquad_alt_stride1(frame, &b_q28, &a_q28, &mut ps_lp.in_lp_state, frame_length);
    }
}
