use std::hint::black_box;

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

use sqpack_rs::sklb::Sklb;

const SKLB_MAGIC: [u8; 4] = *b"blks";
const VERSION1: u16 = 0x3030;
const VERSION2_V1: u16 = 0x3131;
const VERSION2_V2: u16 = 0x3133;
const V1_HEADER_BYTES: usize = 28;
const V2_HEADER_BYTES: usize = 36;
const PAYLOAD_BYTES: usize = 4096;

fn build_v1_4k_sklb() -> Vec<u8> {
    let havok_offset = u16::try_from(V1_HEADER_BYTES).expect("28 fits u16");
    let layer_offset: u16 = 0x2E;

    let mut buf = Vec::with_capacity(V1_HEADER_BYTES + PAYLOAD_BYTES);
    buf.extend_from_slice(&SKLB_MAGIC);
    buf.extend_from_slice(&VERSION1.to_le_bytes());
    buf.extend_from_slice(&VERSION2_V1.to_le_bytes());
    buf.extend_from_slice(&layer_offset.to_le_bytes());
    buf.extend_from_slice(&havok_offset.to_le_bytes());
    for _ in 0..4 {
        buf.extend_from_slice(&0u32.to_le_bytes());
    }
    assert_eq!(buf.len(), V1_HEADER_BYTES);

    buf.extend_from_slice(&0xCAB0_0D1Eu32.to_le_bytes());
    buf.resize(V1_HEADER_BYTES + PAYLOAD_BYTES, 0);

    buf
}

fn build_v2_4k_sklb() -> Vec<u8> {
    let havok_offset = u32::try_from(V2_HEADER_BYTES).expect("36 fits u32");
    let layer_offset: u32 = 0x40;

    let mut buf = Vec::with_capacity(V2_HEADER_BYTES + PAYLOAD_BYTES);
    buf.extend_from_slice(&SKLB_MAGIC);
    buf.extend_from_slice(&VERSION1.to_le_bytes());
    buf.extend_from_slice(&VERSION2_V2.to_le_bytes());
    buf.extend_from_slice(&layer_offset.to_le_bytes());
    buf.extend_from_slice(&havok_offset.to_le_bytes());
    for _ in 0..5 {
        buf.extend_from_slice(&0u32.to_le_bytes());
    }
    assert_eq!(buf.len(), V2_HEADER_BYTES);

    buf.extend_from_slice(&0xCAB0_0D1Eu32.to_le_bytes());
    buf.resize(V2_HEADER_BYTES + PAYLOAD_BYTES, 0);

    buf
}

fn bench_sklb_parse(c: &mut Criterion) {
    let sklb_v1 = build_v1_4k_sklb();
    let sklb_v2 = build_v2_4k_sklb();

    let mut group = c.benchmark_group("sklb.parse");

    group.bench_function("v1_4k_payload", |b| {
        b.iter(|| {
            let sklb = Sklb::parse(black_box(&sklb_v1)).expect("parse");
            black_box(sklb);
        });
    });

    group.bench_function("v2_4k_payload", |b| {
        b.iter(|| {
            let sklb = Sklb::parse(black_box(&sklb_v2)).expect("parse");
            black_box(sklb);
        });
    });

    group.finish();
}

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