#![cfg(not(feature = "fixed_point"))]

use mousiki::c_style_api::opus_encoder::{
    OpusEncoderCtlRequest, opus_encode, opus_encoder_create, opus_encoder_ctl,
};
use mousiki::c_style_api::packet::{Mode, opus_packet_get_mode};

const OPUS_APPLICATION_VOIP: i32 = 2048;
const OPUS_APPLICATION_AUDIO: i32 = 2049;

const MODE_HYBRID: i32 = 1001;

const OPUS_BANDWIDTH_FULLBAND: i32 = 1105;

const OPUS_SIGNAL_VOICE: i32 = 3001;
const OPUS_SIGNAL_MUSIC: i32 = 3002;

const SAMPLE_RATE: i32 = 48_000;
const CHANNELS: usize = 2;
const FRAME_SIZE: usize = 960;
const MAX_PACKET: usize = 1500;

include!(concat!(
    env!("CARGO_MANIFEST_DIR"),
    "/tests/fixtures/hybrid_encode_vectors.rs"
));

fn clamp_i16(value: i32) -> i16 {
    value.clamp(i16::MIN as i32, i16::MAX as i32) as i16
}

fn fill_test_pcm(buf: &mut [i16], channels: usize, seed: i32) {
    let frames = buf.len() / channels;
    for frame in 0..frames {
        for ch in 0..channels {
            let idx = frame * channels + ch;
            let base = 2000 + seed;
            let wave = ((frame as i32 * 37 + ch as i32 * 13 + seed) % 400) - 200;
            let value = base + wave * 10;
            buf[idx] = clamp_i16(value);
        }
    }
}

#[test]
fn hybrid_encode_hp_filter_and_delay_vectors_match_reference() {
    let mut enc =
        opus_encoder_create(SAMPLE_RATE, CHANNELS as i32, OPUS_APPLICATION_VOIP).expect("encoder");
    opus_encoder_ctl(&mut enc, OpusEncoderCtlRequest::SetForceMode(MODE_HYBRID))
        .expect("force hybrid");
    opus_encoder_ctl(
        &mut enc,
        OpusEncoderCtlRequest::SetBandwidth(OPUS_BANDWIDTH_FULLBAND),
    )
    .expect("set bandwidth");
    opus_encoder_ctl(&mut enc, OpusEncoderCtlRequest::SetBitrate(20_000)).expect("set bitrate");
    opus_encoder_ctl(&mut enc, OpusEncoderCtlRequest::SetVbr(false)).expect("disable vbr");
    opus_encoder_ctl(&mut enc, OpusEncoderCtlRequest::SetDtx(false)).expect("disable dtx");
    opus_encoder_ctl(
        &mut enc,
        OpusEncoderCtlRequest::SetSignal(OPUS_SIGNAL_VOICE),
    )
    .expect("set signal");

    let mut pcm = vec![0i16; FRAME_SIZE * CHANNELS * 2];
    fill_test_pcm(&mut pcm, CHANNELS, 0);
    let mut packet = vec![0u8; MAX_PACKET];

    let len0 = opus_encode(
        &mut enc,
        &pcm[..FRAME_SIZE * CHANNELS],
        FRAME_SIZE,
        &mut packet,
    )
    .expect("encode frame 0");
    assert_eq!(&packet[..len0], &HP_DELAY_PACKET0[..]);
    assert_eq!(opus_packet_get_mode(&packet[..len0]), Ok(Mode::HYBRID));
    let mut range0 = 0u32;
    opus_encoder_ctl(&mut enc, OpusEncoderCtlRequest::GetFinalRange(&mut range0))
        .expect("get final range 0");
    assert_eq!(range0, HP_DELAY_RANGE0);

    let len1 = opus_encode(
        &mut enc,
        &pcm[FRAME_SIZE * CHANNELS..],
        FRAME_SIZE,
        &mut packet,
    )
    .expect("encode frame 1");
    assert_eq!(&packet[..len1], &HP_DELAY_PACKET1[..]);
    assert_eq!(opus_packet_get_mode(&packet[..len1]), Ok(Mode::HYBRID));
    let mut range1 = 0u32;
    opus_encoder_ctl(&mut enc, OpusEncoderCtlRequest::GetFinalRange(&mut range1))
        .expect("get final range 1");
    assert_eq!(range1, HP_DELAY_RANGE1);
}

#[test]
fn hybrid_encode_stereo_width_vectors_match_reference() {
    let mut enc =
        opus_encoder_create(SAMPLE_RATE, CHANNELS as i32, OPUS_APPLICATION_AUDIO).expect("encoder");
    opus_encoder_ctl(&mut enc, OpusEncoderCtlRequest::SetForceMode(MODE_HYBRID))
        .expect("force hybrid");
    opus_encoder_ctl(
        &mut enc,
        OpusEncoderCtlRequest::SetBandwidth(OPUS_BANDWIDTH_FULLBAND),
    )
    .expect("set bandwidth");
    opus_encoder_ctl(&mut enc, OpusEncoderCtlRequest::SetBitrate(12_000)).expect("set bitrate");
    opus_encoder_ctl(&mut enc, OpusEncoderCtlRequest::SetVbr(false)).expect("disable vbr");
    opus_encoder_ctl(&mut enc, OpusEncoderCtlRequest::SetDtx(false)).expect("disable dtx");
    opus_encoder_ctl(
        &mut enc,
        OpusEncoderCtlRequest::SetSignal(OPUS_SIGNAL_MUSIC),
    )
    .expect("set signal");

    let mut pcm = vec![0i16; FRAME_SIZE * CHANNELS];
    fill_test_pcm(&mut pcm, CHANNELS, 42);
    let mut packet = vec![0u8; MAX_PACKET];

    let len = opus_encode(&mut enc, &pcm, FRAME_SIZE, &mut packet).expect("encode frame");
    assert_eq!(&packet[..len], &STEREO_WIDTH_PACKET[..]);
    assert_eq!(opus_packet_get_mode(&packet[..len]), Ok(Mode::HYBRID));
    let mut range = 0u32;
    opus_encoder_ctl(&mut enc, OpusEncoderCtlRequest::GetFinalRange(&mut range))
        .expect("get final range");
    assert_eq!(range, STEREO_WIDTH_RANGE);
}
