use std::hint::black_box;

use codspeed_criterion_compat::{Criterion, criterion_group, criterion_main};

use sqpack_rs::tmb::{TMB_HEADER_SIZE, TMB_MAGIC, Tmb};

fn build_empty_tmb() -> Vec<u8> {
    let mut buf = Vec::with_capacity(TMB_HEADER_SIZE);
    buf.extend_from_slice(&TMB_MAGIC);
    buf.extend_from_slice(
        &u32::try_from(TMB_HEADER_SIZE)
            .expect("12 fits")
            .to_le_bytes(),
    );
    buf.extend_from_slice(&0u32.to_le_bytes());
    buf
}

fn build_realistic_tmb() -> Vec<u8> {
    let mut buf = Vec::new();
    buf.extend_from_slice(&TMB_MAGIC);
    buf.extend_from_slice(&0u32.to_le_bytes());
    buf.extend_from_slice(&10u32.to_le_bytes());

    buf.extend_from_slice(b"TMDH");
    buf.extend_from_slice(&16u32.to_le_bytes());
    buf.extend_from_slice(&1i16.to_le_bytes());
    buf.extend_from_slice(&0i16.to_le_bytes());
    buf.extend_from_slice(&60i16.to_le_bytes());
    buf.extend_from_slice(&0i16.to_le_bytes());

    buf.extend_from_slice(b"TMPP");
    buf.extend_from_slice(&12u32.to_le_bytes());
    buf.extend_from_slice(&0u32.to_le_bytes());

    buf.extend_from_slice(b"TMAL");
    buf.extend_from_slice(&16u32.to_le_bytes());
    buf.extend_from_slice(&0u32.to_le_bytes());
    buf.extend_from_slice(&1u32.to_le_bytes());

    buf.extend_from_slice(b"TMAC");
    buf.extend_from_slice(&28u32.to_le_bytes());
    buf.extend_from_slice(&2i16.to_le_bytes());
    buf.extend_from_slice(&0i16.to_le_bytes());
    buf.extend_from_slice(&0i32.to_le_bytes());
    buf.extend_from_slice(&0i32.to_le_bytes());
    buf.extend_from_slice(&0u32.to_le_bytes());
    buf.extend_from_slice(&1u32.to_le_bytes());

    buf.extend_from_slice(b"TMTR");
    buf.extend_from_slice(&24u32.to_le_bytes());
    buf.extend_from_slice(&3i16.to_le_bytes());
    buf.extend_from_slice(&0i16.to_le_bytes());
    buf.extend_from_slice(&0u32.to_le_bytes());
    buf.extend_from_slice(&5u32.to_le_bytes());
    buf.extend_from_slice(&0u32.to_le_bytes());

    buf.extend_from_slice(b"C006");
    buf.extend_from_slice(&24u32.to_le_bytes());
    buf.extend_from_slice(&4i16.to_le_bytes());
    buf.extend_from_slice(&10i16.to_le_bytes());
    buf.extend_from_slice(&1i32.to_le_bytes());
    buf.extend_from_slice(&0i32.to_le_bytes());
    buf.extend_from_slice(&0i32.to_le_bytes());

    buf.extend_from_slice(b"C010");
    buf.extend_from_slice(&40u32.to_le_bytes());
    buf.extend_from_slice(&5i16.to_le_bytes());
    buf.extend_from_slice(&15i16.to_le_bytes());
    buf.extend_from_slice(&50i32.to_le_bytes());
    buf.extend_from_slice(&0i32.to_le_bytes());
    buf.extend_from_slice(&0u32.to_le_bytes());
    buf.extend_from_slice(&0f32.to_le_bytes());
    buf.extend_from_slice(&60f32.to_le_bytes());
    buf.extend_from_slice(&0u32.to_le_bytes());
    buf.extend_from_slice(&0i32.to_le_bytes());

    buf.extend_from_slice(b"C014");
    buf.extend_from_slice(&28u32.to_le_bytes());
    buf.extend_from_slice(&6i16.to_le_bytes());
    buf.extend_from_slice(&20i16.to_le_bytes());
    buf.extend_from_slice(&1i32.to_le_bytes());
    buf.extend_from_slice(&0i32.to_le_bytes());
    buf.extend_from_slice(&0i32.to_le_bytes());
    buf.extend_from_slice(&0i32.to_le_bytes());

    buf.extend_from_slice(b"C063");
    buf.extend_from_slice(&32u32.to_le_bytes());
    buf.extend_from_slice(&7i16.to_le_bytes());
    buf.extend_from_slice(&25i16.to_le_bytes());
    buf.extend_from_slice(&1i32.to_le_bytes());
    buf.extend_from_slice(&0i32.to_le_bytes());
    buf.extend_from_slice(&0u32.to_le_bytes());
    buf.extend_from_slice(&0i32.to_le_bytes());
    buf.push(1);
    buf.push(0);
    buf.extend_from_slice(&0i16.to_le_bytes());

    buf.extend_from_slice(b"C131");
    buf.extend_from_slice(&20u32.to_le_bytes());
    buf.extend_from_slice(&8i16.to_le_bytes());
    buf.extend_from_slice(&30i16.to_le_bytes());
    buf.extend_from_slice(&1i32.to_le_bytes());
    buf.extend_from_slice(&0i32.to_le_bytes());

    let total = u32::try_from(buf.len()).expect("fits");
    buf[4..8].copy_from_slice(&total.to_le_bytes());
    buf
}

fn bench_tmb_parse(c: &mut Criterion) {
    let empty = build_empty_tmb();
    let realistic = build_realistic_tmb();

    let mut group = c.benchmark_group("tmb.parse");
    group.bench_function("empty", |b| {
        b.iter(|| {
            let tmb = Tmb::parse(black_box(&empty)).expect("parse");
            black_box(tmb);
        });
    });
    group.bench_function("realistic", |b| {
        b.iter(|| {
            let tmb = Tmb::parse(black_box(&realistic)).expect("parse");
            black_box(tmb);
        });
    });
    group.finish();
}

criterion_group!(benches, bench_tmb_parse);
criterion_main!(benches);
