use criterion::{Criterion, criterion_group, criterion_main};
use ruin_ui::{
    Color, Element, FlexDirection, LayoutCache, TextStyle, TextSystem, UiSize,
    layout_snapshot_with_cache,
};

fn text_style() -> TextStyle {
    TextStyle::new(14.0, Color::rgb(0xFF, 0xFF, 0xFF))
}

fn make_column_tree(n: usize) -> Element {
    let mut root = Element::new().direction(FlexDirection::Column);
    for i in 0..n {
        root = root.child(Element::text(format!("item {i}"), text_style()));
    }
    root
}

/// Static tree — nothing changes between frames.  After one warmup frame, all
/// subsequent frames should be near-zero cost (all cache hits).
fn bench_static_tree(c: &mut Criterion) {
    let mut text_system = TextSystem::new();
    let mut layout_cache = LayoutCache::new();
    let root = make_column_tree(200);
    let size = UiSize::new(400.0, 4000.0);

    // Warm up cache.
    layout_snapshot_with_cache(1, size, &root, &mut text_system, &mut layout_cache);

    c.bench_function("layout_static_200_nodes", |b| {
        let mut version = 2u64;
        b.iter(|| {
            version += 1;
            layout_snapshot_with_cache(version, size, &root, &mut text_system, &mut layout_cache)
        });
    });
}

/// 200 nodes, one text content changes per frame — only one cache miss expected.
fn bench_single_change(c: &mut Criterion) {
    let mut text_system = TextSystem::new();
    let mut layout_cache = LayoutCache::new();
    let size = UiSize::new(400.0, 4000.0);

    // Initial warmup.
    let root = make_column_tree(200);
    layout_snapshot_with_cache(1, size, &root, &mut text_system, &mut layout_cache);

    c.bench_function("layout_single_change_200_nodes", |b| {
        let mut counter = 0usize;
        let mut version = 2u64;
        b.iter(|| {
            counter += 1;
            version += 1;
            let mut tree = make_column_tree(200);
            tree.children[100] = Element::text(format!("changed {counter}"), text_style());
            layout_snapshot_with_cache(version, size, &tree, &mut text_system, &mut layout_cache)
        });
    });
}

/// Scroll box with 500 items, 20 visible.  Simulates a large list.
fn bench_scroll_list(c: &mut Criterion) {
    let mut text_system = TextSystem::new();
    let mut layout_cache = LayoutCache::new();
    let item_height = 32.0;
    let viewport_height = 640.0;
    let n = 500;

    let mut scroll_box = Element::scroll_box(0.0)
        .width(400.0)
        .height(viewport_height);
    for i in 0..n {
        scroll_box = scroll_box.child(
            Element::new()
                .height(item_height)
                .child(Element::text(format!("list item {i}"), text_style())),
        );
    }
    let root = Element::new().child(scroll_box);
    let size = UiSize::new(400.0, viewport_height);

    // Warm up.
    layout_snapshot_with_cache(1, size, &root, &mut text_system, &mut layout_cache);

    c.bench_function("layout_scroll_list_500_items", |b| {
        let mut version = 2u64;
        b.iter(|| {
            version += 1;
            layout_snapshot_with_cache(version, size, &root, &mut text_system, &mut layout_cache)
        });
    });
}

criterion_group!(
    benches,
    bench_static_tree,
    bench_single_change,
    bench_scroll_list
);
criterion_main!(benches);
