use crate::range_coder::RangeCoder;
use crate::silk::define::*;
use crate::silk::nlsf_unpack::silk_nlsf_unpack;
use crate::silk::structs::*;
use crate::silk::tables::*;

pub fn silk_encode_indices(
    ps_enc_c: &mut SilkEncoderState,
    ps_range_enc: &mut RangeCoder,
    frame_index: usize,
    encode_lbrr: bool,
    cond_coding: i32,
) {
    let ps_indices = if encode_lbrr {
        ps_enc_c.s_cmn.indices_lbrr[frame_index]
    } else {
        ps_enc_c.s_cmn.indices
    };
    let ps_indices = &ps_indices;

    let type_offset = 2 * ps_indices.signal_type + ps_indices.quant_offset_type;
    if encode_lbrr || type_offset >= 2 {
        ps_range_enc.encode_icdf((type_offset - 2) as i32, &SILK_TYPE_OFFSET_VAD_ICDF, 8);
    } else {
        ps_range_enc.encode_icdf(type_offset as i32, &SILK_TYPE_OFFSET_NO_VAD_ICDF, 8);
    }

    if cond_coding == CODE_CONDITIONALLY {
        ps_range_enc.encode_icdf(ps_indices.gains_indices[0] as i32, &SILK_DELTA_GAIN_ICDF, 8);
    } else {
        ps_range_enc.encode_icdf(
            (ps_indices.gains_indices[0] >> 3) as i32,
            &SILK_GAIN_ICDF[ps_indices.signal_type as usize],
            8,
        );
        ps_range_enc.encode_icdf(
            (ps_indices.gains_indices[0] & 7) as i32,
            &SILK_UNIFORM8_ICDF,
            8,
        );
    }

    for i in 1..ps_enc_c.s_cmn.nb_subfr as usize {
        ps_range_enc.encode_icdf(ps_indices.gains_indices[i] as i32, &SILK_DELTA_GAIN_ICDF, 8);
    }

    let cb = ps_enc_c.ps_nlsf_cb.expect("NLSF codebook not initialized");
    ps_range_enc.encode_icdf(
        ps_indices.nlsf_indices[0] as i32,
        &cb.cb1_icdf[((ps_indices.signal_type >> 1) as usize * cb.n_vectors as usize)..],
        8,
    );

    let mut ec_ix = [0i16; MAX_LPC_ORDER];
    let mut pred_q8 = [0u8; MAX_LPC_ORDER];
    silk_nlsf_unpack(
        &mut ec_ix,
        &mut pred_q8,
        cb,
        ps_indices.nlsf_indices[0] as usize,
    );

    for i in 0..cb.order as usize {
        if ps_indices.nlsf_indices[i + 1] >= NLSF_QUANT_MAX_AMPLITUDE as i8 {
            ps_range_enc.encode_icdf(
                2 * NLSF_QUANT_MAX_AMPLITUDE,
                &cb.ec_icdf[ec_ix[i] as usize..],
                8,
            );
            ps_range_enc.encode_icdf(
                (ps_indices.nlsf_indices[i + 1] - NLSF_QUANT_MAX_AMPLITUDE as i8) as i32,
                &SILK_NLSF_EXT_ICDF,
                8,
            );
        } else if ps_indices.nlsf_indices[i + 1] <= -(NLSF_QUANT_MAX_AMPLITUDE as i8) {
            ps_range_enc.encode_icdf(0, &cb.ec_icdf[ec_ix[i] as usize..], 8);
            ps_range_enc.encode_icdf(
                (-ps_indices.nlsf_indices[i + 1] - NLSF_QUANT_MAX_AMPLITUDE as i8) as i32,
                &SILK_NLSF_EXT_ICDF,
                8,
            );
        } else {
            ps_range_enc.encode_icdf(
                (ps_indices.nlsf_indices[i + 1] + NLSF_QUANT_MAX_AMPLITUDE as i8) as i32,
                &cb.ec_icdf[ec_ix[i] as usize..],
                8,
            );
        }
    }

    if ps_enc_c.s_cmn.nb_subfr == MAX_NB_SUBFR as i32 {
        ps_range_enc.encode_icdf(
            ps_indices.nlsf_interp_coef_q2 as i32,
            &SILK_NLSF_INTERPOLATION_FACTOR_ICDF,
            8,
        );
    }

    if ps_indices.signal_type == TYPE_VOICED as i8 {
        let mut encode_absolute_lag_index = true;
        if cond_coding == CODE_CONDITIONALLY && ps_enc_c.s_cmn.ec_prev_signal_type == TYPE_VOICED {
            let mut delta_lag_index =
                ps_indices.lag_index as i32 - ps_enc_c.s_cmn.ec_prev_lag_index as i32;
            if !(-8..=11).contains(&delta_lag_index) {
                delta_lag_index = 0;
            } else {
                delta_lag_index += 9;
                encode_absolute_lag_index = false;
            }
            ps_range_enc.encode_icdf(delta_lag_index, &SILK_PITCH_DELTA_ICDF, 8);
        }
        if encode_absolute_lag_index {
            let half_fs = ps_enc_c.s_cmn.fs_khz / 2;

            let max_lag_index = (SILK_PITCH_LAG_ICDF.len() as i32 - 1) * half_fs + (half_fs - 1);
            let lag_index = (ps_indices.lag_index as i32).min(max_lag_index);

            let pitch_high_bits = lag_index / half_fs;
            let pitch_low_bits = lag_index - pitch_high_bits * half_fs;
            ps_range_enc.encode_icdf(pitch_high_bits, &SILK_PITCH_LAG_ICDF, 8);

            let low_bits_icdf = match ps_enc_c.s_cmn.fs_khz {
                8 => &SILK_UNIFORM4_ICDF[..],
                12 => &SILK_UNIFORM6_ICDF[..],
                16 => &SILK_UNIFORM8_ICDF[..],
                _ => &SILK_UNIFORM8_ICDF[..],
            };
            ps_range_enc.encode_icdf(pitch_low_bits, low_bits_icdf, 8);
        }
        ps_enc_c.s_cmn.ec_prev_lag_index = ps_indices.lag_index;

        let contour_icdf = if ps_enc_c.s_cmn.nb_subfr == 2 {
            if ps_enc_c.s_cmn.fs_khz == 8 {
                &SILK_PITCH_CONTOUR_10_MS_NB_ICDF[..]
            } else {
                &SILK_PITCH_CONTOUR_10_MS_ICDF[..]
            }
        } else if ps_enc_c.s_cmn.fs_khz == 8 {
            &SILK_PITCH_CONTOUR_NB_ICDF[..]
        } else {
            &SILK_PITCH_CONTOUR_ICDF[..]
        };
        ps_range_enc.encode_icdf(ps_indices.contour_index as i32, contour_icdf, 8);

        ps_range_enc.encode_icdf(ps_indices.per_index as i32, &SILK_LTP_PER_INDEX_ICDF, 8);

        for k in 0..ps_enc_c.s_cmn.nb_subfr as usize {
            ps_range_enc.encode_icdf(
                ps_indices.ltp_index[k] as i32,
                SILK_LTP_GAIN_ICDF_PTRS[ps_indices.per_index as usize],
                8,
            );
        }

        if cond_coding == CODE_INDEPENDENTLY {
            ps_range_enc.encode_icdf(ps_indices.ltp_scale_index as i32, &SILK_LTPSCALE_ICDF, 8);
        }
    }

    ps_enc_c.s_cmn.ec_prev_signal_type = ps_indices.signal_type as i32;

    ps_range_enc.encode_icdf(ps_indices.seed as i32, &SILK_UNIFORM4_ICDF, 8);
}

pub fn silk_encode_stereo(
    ps_range_enc: &mut RangeCoder,
    side_idx: i8,
    pred_idx: i8,
    only_middle: i8,
) {
    ps_range_enc.encode_icdf(only_middle as i32, &SILK_STEREO_ONLY_CODE_MID_ICDF, 8);

    if only_middle == 0 {
        let i = (side_idx as i32).min(4);
        let j = (pred_idx as i32) >> 2;
        let joint_idx = i * 5 + j;

        ps_range_enc.encode_icdf(joint_idx, &SILK_STEREO_PRED_JOINT_ICDF, 8);
    }
}
