use std::alloc::System;
use std::hint::black_box;
use std::time::Instant;

use libremetaverse_types::{Matrix4, Quaternion, Vector3};
use stats_alloc::{INSTRUMENTED_SYSTEM, Region, StatsAlloc};

#[global_allocator]
static ALLOCATOR: &StatsAlloc<System> = &INSTRUMENTED_SYSTEM;

fn main() {
    const ITERATIONS: usize = 250_000;

    let rotation = Quaternion::create_from_eulers_with_single_single_single(0.25, 0.5, 0.75)
        .expect("valid benchmark rotation");
    let matrix = Matrix4::mul_with_matrix4_matrix4(
        Matrix4::create_scale(Vector3 {
            x: 2.0,
            y: 3.0,
            z: 4.0,
        })
        .expect("valid benchmark scale"),
        Matrix4::create_translation(Vector3 {
            x: 10.0,
            y: 20.0,
            z: 30.0,
        })
        .expect("valid benchmark translation"),
    );

    // Warm up runtime and formatting paths before entering the measured region.
    black_box(Matrix4::transform(matrix, rotation).expect("matrix transform"));
    black_box(
        Quaternion::slerp(Quaternion::identity(), rotation, 0.5).expect("quaternion interpolation"),
    );

    let allocation_region = Region::new(ALLOCATOR);
    let started = Instant::now();
    let mut transformed = matrix;
    let mut interpolated = Quaternion::identity();
    for index in 0..ITERATIONS {
        let amount = f32::from(u8::try_from(index & 255).expect("masked benchmark index")) / 255.0;
        transformed = Matrix4::transform(black_box(transformed), black_box(rotation))
            .expect("matrix transform");
        interpolated = Quaternion::slerp(
            black_box(interpolated),
            black_box(rotation),
            black_box(amount),
        )
        .expect("quaternion interpolation");
    }
    let elapsed = started.elapsed();
    let allocation_stats = allocation_region.change();

    black_box((transformed, interpolated));
    println!(
        "geometry hot paths: {ITERATIONS} iterations in {elapsed:?}, {} allocations, {} reallocations",
        allocation_stats.allocations, allocation_stats.reallocations
    );
    assert_eq!(
        (allocation_stats.allocations, allocation_stats.reallocations),
        (0, 0),
        "matrix transforms and quaternion interpolation must remain allocation-free"
    );
}
