use criterion::{
    BenchmarkGroup, BenchmarkId, Criterion, measurement::Measurement,
};
use h3o::CellIndex;
use std::{hint::black_box, os::raw::c_int};

const HEXAGON: u64 = 0x8b1fb46622defff;
const PENTAGON: u64 = 0x8b0800000000fff;

pub fn bench(c: &mut Criterion) {
    let mut group = c.benchmark_group("gridDisk");

    for k in [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 30, 40, 50, 100] {
        bench_h3o(&mut group, "h3o/Hexagon", HEXAGON, k);
        bench_h3(&mut group, "h3/Hexagon", HEXAGON, k);

        bench_h3o(&mut group, "h3o/Pentagon", PENTAGON, k);
        bench_h3(&mut group, "h3/Pentagon", PENTAGON, k);
    }

    group.finish();
}

// -----------------------------------------------------------------------------

fn bench_h3o<T>(
    group: &mut BenchmarkGroup<T>,
    name: &'static str,
    index: u64,
    k: u32,
) where
    T: Measurement,
{
    let index = CellIndex::try_from(index).expect("hex index");
    group.bench_with_input(BenchmarkId::new(name, k), &index, |b, &index| {
        b.iter_with_large_drop(|| {
            black_box(index).grid_disk::<Vec<_>>(black_box(k))
        })
    });
}

fn bench_h3<T>(
    group: &mut BenchmarkGroup<T>,
    name: &'static str,
    index: u64,
    k: u32,
) where
    T: Measurement,
{
    let size =
        usize::try_from(h3o::max_grid_disk_size(k)).expect("grid too large");

    group.bench_with_input(BenchmarkId::new(name, k), &index, |b, &index| {
        b.iter_with_large_drop(|| unsafe {
            let mut cells = vec![0u64; size];

            h3ron_h3_sys::gridDisk(
                black_box(index),
                black_box(k as c_int),
                cells.as_mut_ptr(),
            )
        })
    });
}
