#![allow(dead_code)]

use core::cmp::Ordering;

use super::types::{FixedCeltSig, FixedOpusVal16, FixedOpusVal32};
use libm::trunc;

#[inline]
pub(crate) fn qconst16(value: f64, bits: u32) -> FixedOpusVal16 {
    let scale = (1i64 << bits) as f64;
    trunc(value * scale + 0.5) as FixedOpusVal16
}

#[inline]
pub(crate) fn qconst16_clamped(value: f64, bits: u32) -> FixedOpusVal16 {
    let scale = (1i64 << bits) as f64;
    let raw = trunc(value * scale + 0.5) as i32;
    raw.clamp(-32_767, 32_767) as FixedOpusVal16
}

#[inline]
pub(crate) fn qconst32(value: f64, bits: u32) -> FixedOpusVal32 {
    let scale = (1i64 << bits) as f64;
    trunc(value * scale + 0.5) as FixedOpusVal32
}

#[inline]
pub(crate) fn add32(a: FixedOpusVal32, b: FixedOpusVal32) -> FixedOpusVal32 {
    a.wrapping_add(b)
}

#[inline]
pub(crate) fn sub32(a: FixedOpusVal32, b: FixedOpusVal32) -> FixedOpusVal32 {
    a.wrapping_sub(b)
}

#[inline]
pub(crate) fn add32_ovflw(a: FixedOpusVal32, b: FixedOpusVal32) -> FixedOpusVal32 {
    a.wrapping_add(b)
}

#[inline]
pub(crate) fn sub32_ovflw(a: FixedOpusVal32, b: FixedOpusVal32) -> FixedOpusVal32 {
    a.wrapping_sub(b)
}

#[inline]
pub(crate) fn neg32_ovflw(a: FixedOpusVal32) -> FixedOpusVal32 {
    a.wrapping_neg()
}

#[inline]
pub(crate) fn abs32(a: FixedOpusVal32) -> FixedOpusVal32 {
    if a < 0 { a.wrapping_neg() } else { a }
}

#[inline]
pub(crate) fn extract16(a: FixedOpusVal32) -> FixedOpusVal16 {
    a as FixedOpusVal16
}

#[inline]
pub(crate) fn extend32(a: FixedOpusVal16) -> FixedCeltSig {
    FixedCeltSig::from(a)
}

#[inline]
pub(crate) fn shl32(a: FixedOpusVal32, shift: u32) -> FixedOpusVal32 {
    a.wrapping_shl(shift)
}

#[inline]
pub(crate) fn shr32(a: FixedOpusVal32, shift: u32) -> FixedOpusVal32 {
    a >> shift
}

#[inline]
pub(crate) fn vshr32(a: FixedOpusVal32, shift: i32) -> FixedOpusVal32 {
    match shift.cmp(&0) {
        Ordering::Greater => shr32(a, shift as u32),
        Ordering::Less => shl32(a, (-shift) as u32),
        Ordering::Equal => a,
    }
}

#[inline]
pub(crate) fn pshr32(a: FixedOpusVal32, shift: u32) -> FixedOpusVal32 {
    if shift == 0 {
        return a;
    }
    let bias = 1i32.wrapping_shl(shift - 1);
    shr32(a.wrapping_add(bias), shift)
}

#[inline]
pub(crate) fn pshr32_ovflw(a: FixedOpusVal32, shift: u32) -> FixedOpusVal32 {
    if shift == 0 {
        return a;
    }
    let bias = 1i32.wrapping_shl(shift - 1);
    shr32(a.wrapping_add(bias), shift)
}

#[inline]
pub(crate) fn mult16_16(a: FixedOpusVal16, b: FixedOpusVal16) -> FixedOpusVal32 {
    FixedOpusVal32::from(a) * FixedOpusVal32::from(b)
}

#[inline]
pub(crate) fn mult16_16_q15(a: FixedOpusVal16, b: FixedOpusVal16) -> FixedOpusVal16 {
    (mult16_16(a, b) >> 15) as FixedOpusVal16
}

#[inline]
pub(crate) fn mult16_16_p15(a: FixedOpusVal16, b: FixedOpusVal16) -> FixedOpusVal16 {
    ((mult16_16(a, b) + 16_384) >> 15) as FixedOpusVal16
}

#[inline]
pub(crate) fn mult16_32_q15(a: FixedOpusVal16, b: FixedOpusVal32) -> FixedOpusVal32 {
    ((i64::from(a) * i64::from(b)) >> 15) as FixedOpusVal32
}

#[inline]
pub(crate) fn mult16_32_q16(a: FixedOpusVal16, b: FixedOpusVal32) -> FixedOpusVal32 {
    ((i64::from(a) * i64::from(b)) >> 16) as FixedOpusVal32
}

#[inline]
pub(crate) fn mult32_32_q31(a: FixedOpusVal32, b: FixedOpusVal32) -> FixedOpusVal32 {
    ((i64::from(a) * i64::from(b)) >> 31) as FixedOpusVal32
}

#[inline]
pub(crate) fn mult32_32_q16(a: FixedOpusVal32, b: FixedOpusVal32) -> FixedOpusVal32 {
    ((i64::from(a) * i64::from(b)) >> 16) as FixedOpusVal32
}

#[inline]
pub(crate) fn mult32_32_p31(a: FixedOpusVal32, b: FixedOpusVal32) -> FixedOpusVal32 {
    ((i64::from(a) * i64::from(b) + (1i64 << 30)) >> 31) as FixedOpusVal32
}

#[inline]
pub(crate) fn mult32_32_32(a: FixedOpusVal32, b: FixedOpusVal32) -> FixedOpusVal32 {
    a.wrapping_mul(b)
}

#[inline]
pub(crate) fn div32(a: FixedOpusVal32, b: FixedOpusVal32) -> FixedOpusVal32 {
    debug_assert!(b != 0);
    a / b
}

#[inline]
pub(crate) fn mac16_16(c: FixedOpusVal32, a: FixedOpusVal16, b: FixedOpusVal16) -> FixedOpusVal32 {
    add32(c, mult16_16(a, b))
}
