// Copyright 2025 the Vello Authors
// SPDX-License-Identifier: Apache-2.0 OR MIT

use crate::data::get_data_items;
use criterion::Criterion;
use vello_common::fearless_simd::Level;
use vello_common::flatten::Line;
use vello_common::kurbo::{Affine, Rect, Shape};
use vello_common::peniko::Fill;
use vello_common::strip_generator::{StripGenerator, StripStorage};
use vello_common::tile::Tiles;

pub fn shift_lines_50_percent(lines: &[Line]) -> Vec<Line> {
    if lines.is_empty() {
        return vec![];
    }

    let mut min_x = f32::MAX;
    let mut max_x = f32::MIN;
    for line in lines {
        min_x = min_x.min(line.p0.x).min(line.p1.x);
        max_x = max_x.max(line.p0.x).max(line.p1.x);
    }

    let shift_amount = (min_x + max_x) / 2.0;

    let mut shifted = lines.to_vec();
    for line in &mut shifted {
        line.p0.x -= shift_amount;
        line.p1.x -= shift_amount;
    }
    shifted
}

pub fn render_strips(c: &mut Criterion) {
    let mut g = c.benchmark_group("render_strips");
    g.sample_size(50);

    macro_rules! strip_single {
        ($item:expr, $level:expr, $suffix:expr) => {
            let lines = $item.lines();
            let tiles = $item.sorted_tiles();

            g.bench_function(format!("{}_{}", $item.name.clone(), $suffix), |b| {
                let mut strip_buf = vec![];
                let mut alpha_buf = vec![];

                b.iter(|| {
                    strip_buf.clear();
                    alpha_buf.clear();

                    vello_common::strip::render(
                        $level,
                        &tiles,
                        &mut strip_buf,
                        &mut alpha_buf,
                        Fill::NonZero,
                        None,
                        &lines,
                    );
                    std::hint::black_box((&strip_buf, &alpha_buf));
                })
            });
        };
    }

    for item in get_data_items() {
        // Commenting this out by default since SIMD is what we care about most.
        // strip_single!(item, Level::baseline(), "fallback");
        let simd_level = Level::new();
        if !simd_level.is_fallback() {
            strip_single!(item, simd_level, "simd");
        }
    }
    g.finish();
}

pub fn render_strips_cull(c: &mut Criterion) {
    let mut g_cull = c.benchmark_group("render_strips_culled50");
    g_cull.sample_size(50);

    for item in get_data_items() {
        let simd_level = Level::new();
        if simd_level.is_fallback() {
            continue;
        }

        let shifted_lines = shift_lines_50_percent(&item.lines());

        let mut tiler = Tiles::new(simd_level, item.width, item.height);
        tiler.make_tiles_analytic_aa(simd_level, &shifted_lines, item.width, item.height);
        tiler.sort_tiles();

        g_cull.bench_function(item.name.clone().to_string(), |b| {
            let mut strip_buf = vec![];
            let mut alpha_buf = vec![];

            b.iter(|| {
                strip_buf.clear();
                alpha_buf.clear();

                vello_common::strip::render(
                    simd_level,
                    &tiler,
                    &mut strip_buf,
                    &mut alpha_buf,
                    Fill::NonZero,
                    None,
                    &shifted_lines,
                );
                std::hint::black_box((&strip_buf, &alpha_buf));
            });
        });
    }
    g_cull.finish();
}

pub fn render_rect(c: &mut Criterion) {
    let mut g = c.benchmark_group("render_rect");
    g.sample_size(50);

    let rect = Rect::new(10.0, 10.0, 24.0, 24.0);
    let width = 100;
    let height = 100;
    let level = Level::new();

    // Benchmark: generate_filled_path (path-based approach)
    g.bench_function("14x14_via_path", |b| {
        let mut generator = StripGenerator::new(width, height, level);
        let mut storage = StripStorage::default();

        b.iter(|| {
            storage.clear();
            generator.generate_filled_path(
                rect.to_path(0.1),
                Fill::NonZero,
                Affine::IDENTITY,
                None,
                &mut storage,
                None,
            );
            generator.reset(width, height);
            std::hint::black_box(&storage);
        });
    });

    // Benchmark: generate_rect_strips_with_clip (optimized rect approach)
    g.bench_function("14x14_via_rect", |b| {
        let mut generator = StripGenerator::new(width, height, level);
        let mut storage = StripStorage::default();

        b.iter(|| {
            storage.clear();
            generator.generate_filled_rect_fast(&rect, &mut storage, None);
            generator.reset(width, height);
            std::hint::black_box(&storage);
        });
    });

    g.finish();
}
