//
// Syd: rock-solid application kernel
// benches/sandbox/wildmatch.rs: Wildmatch microbenchmarks
//
// Copyright (c) 2025 Ali Polatel <alip@chesswob.org>
//
// SPDX-License-Identifier: GPL-3.0

use std::{hint::black_box, io::BufRead, time::Duration};

use brunch::{benches, Bench};
use syd::wildmatch::wildmatch;

fn parse_line(line: &[u8]) -> Option<(bool, Vec<u8>, Vec<u8>)> {
    let mut parts = vec![];
    let mut i = 0;
    while i < line.len() {
        while i < line.len() && matches!(line[i], b' ' | b'\t') {
            i += 1;
        }
        if i >= line.len() {
            break;
        }
        if matches!(line[i], b'\'' | b'"' | b'`') {
            let quote = line[i];
            i += 1;
            let start = i;
            while i < line.len() && line[i] != quote {
                i += 1;
            }
            parts.push(&line[start..i]);
            if i < line.len() {
                i += 1;
            }
        } else {
            let start = i;
            while i < line.len() && !matches!(line[i], b' ' | b'\t') {
                i += 1;
            }
            parts.push(&line[start..i]);
        }
    }
    if parts.len() >= 4 {
        let expected = parts[0] == b"1";
        Some((expected, parts[2].to_vec(), parts[3].to_vec()))
    } else {
        None
    }
}

fn fnmatch(pat: &[u8], input: &[u8]) -> bool {
    use nix::NixPath;
    pat.with_nix_path(|pat_cstr| {
        input.with_nix_path(|input_cstr| {
            let flags = libc::FNM_PATHNAME | libc::FNM_NOESCAPE | libc::FNM_PERIOD;
            // SAFETY: FFI call to fnmatch(3)
            unsafe { libc::fnmatch(pat_cstr.as_ptr(), input_cstr.as_ptr(), flags) == 0 }
        })
    })
    .map(|res| res.unwrap())
    .unwrap()
}

fn main() {
    let data = include_bytes!("../../src/wildtest.txt.xz");
    let decoder = xz2::read::XzDecoder::new(&data[..]);
    let reader = std::io::BufReader::new(decoder);

    let mut tests = Vec::new();
    for line in reader.lines() {
        let line = line.expect("valid line");
        let line_bytes = line.as_bytes();
        if line_bytes.starts_with(b"#") || line.trim().is_empty() {
            continue;
        }
        if let Some((expected, text, pattern)) = parse_line(line_bytes) {
            tests.push((expected, text, pattern));
        }
    }

    let tests = std::sync::Arc::new(tests);
    println!("Loaded {} tests for benchmarking.", tests.len());

    benches!(
        inline:
        Bench::new("wildmatch")
            .with_samples(tests.len().try_into().unwrap())
            .with_timeout(Duration::from_secs(30))
            .run_seeded(tests.clone(), |tests| {
                for (_, text, pattern) in tests.iter() {
                    black_box(wildmatch(pattern, text));
                }
            }),
        Bench::new("fnmatch")
            .with_samples(tests.len().try_into().unwrap())
            .with_timeout(Duration::from_secs(30))
            .run_seeded(tests, |tests| {
                for (_, text, pattern) in tests.iter() {
                    black_box(fnmatch(pattern, text));
                }
            })
    );
}
