// This file is part of the uutils coreutils package.
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.

use divan::{Bencher, black_box};
use uu_cksum::uumain;
use uucore::benchmark::{get_bench_args, setup_test_file, text_data};

// Macro to generate benchmarks for each algorithm
macro_rules! bench_algorithm {
    ($algo_name:ident, $algo_str:expr) => {
        #[divan::bench]
        fn $algo_name(bencher: Bencher) {
            let data = text_data::generate_by_size(100, 80);
            let file_path = setup_test_file(&data);

            bencher
                .with_inputs(|| {
                    get_bench_args(&[&"--algorithm", &$algo_str, &file_path]).into_iter()
                })
                .bench_values(|args| black_box(uumain(args)));
        }
    };
    ($algo_name:ident, $algo_str:expr, $length:expr) => {
        #[divan::bench]
        fn $algo_name(bencher: Bencher) {
            let data = text_data::generate_by_size(100, 80);
            let file_path = setup_test_file(&data);

            bencher
                .with_inputs(|| {
                    get_bench_args(&[
                        &"--algorithm",
                        &$algo_str,
                        &"--length",
                        &$length,
                        &file_path,
                    ])
                    .into_iter()
                })
                .bench_values(|args| black_box(uumain(args)));
        }
    };
}

// Special macro for SHAKE algorithms that require length parameter
// Since SHAKE algorithms have fundamental --length parameter conflicts in cksum,
// we implement them using direct digest calculation for meaningful benchmarks
macro_rules! bench_shake_algorithm {
    ($algo_name:ident, $algo_str:expr, Shake128) => {
        #[divan::bench]
        fn $algo_name(bencher: Bencher) {
            use uucore::sum::{Digest, Shake128};

            let data = text_data::generate_by_size(100, 80);

            bencher.bench(|| {
                let mut shake = Shake128::with_output_bits(256);
                shake.hash_update(&data);

                // SHAKE algorithms can output any length, use 256 bits (32 bytes) for meaningful comparison
                let mut output = [0u8; 32];
                shake.hash_finalize(&mut output);

                black_box(output);
            });
        }
    };
    ($algo_name:ident, $algo_str:expr, Shake256) => {
        #[divan::bench]
        fn $algo_name(bencher: Bencher) {
            use uucore::sum::{Digest, Shake256};

            let data = text_data::generate_by_size(100, 80);

            bencher.bench(|| {
                let mut shake = Shake256::with_output_bits(512);
                shake.hash_update(&data);

                // SHAKE algorithms can output any length, use 256 bits (32 bytes) for meaningful comparison
                let mut output = [0u8; 32];
                shake.hash_finalize(&mut output);

                black_box(output);
            });
        }
    };
}

// Generate benchmarks for all supported algorithms
bench_algorithm!(cksum_sysv, "sysv");
bench_algorithm!(cksum_bsd, "bsd");
bench_algorithm!(cksum_crc, "crc");
bench_algorithm!(cksum_crc32b, "crc32b");
bench_algorithm!(cksum_md5, "md5");
bench_algorithm!(cksum_sha1, "sha1");
bench_algorithm!(cksum_sha2, "sha2", "256");
bench_algorithm!(cksum_sha3, "sha3", "256");
bench_algorithm!(cksum_blake2b, "blake2b");
bench_algorithm!(cksum_sm3, "sm3");
bench_algorithm!(cksum_sha224, "sha224");
bench_algorithm!(cksum_sha256, "sha256");
bench_algorithm!(cksum_sha384, "sha384");
bench_algorithm!(cksum_sha512, "sha512");
bench_algorithm!(cksum_blake3, "blake3");
bench_shake_algorithm!(cksum_shake128, "shake128", Shake128);
bench_shake_algorithm!(cksum_shake256, "shake256", Shake256);

/// Benchmark cksum with default CRC algorithm
#[divan::bench]
fn cksum_default(bencher: Bencher) {
    let data = text_data::generate_by_size(100, 80);
    let file_path = setup_test_file(&data);

    bencher
        .with_inputs(|| get_bench_args(&[&file_path]).into_iter())
        .bench_values(|args| black_box(uumain(args)));
}

/// Benchmark cksum with raw output format
#[divan::bench]
fn cksum_raw_output(bencher: Bencher) {
    let data = text_data::generate_by_size(100, 80);
    let file_path = setup_test_file(&data);

    bencher
        .with_inputs(|| get_bench_args(&[&"--raw", &file_path]).into_iter())
        .bench_values(|args| black_box(uumain(args)));
}

/// Benchmark cksum processing multiple files
#[divan::bench]
fn cksum_multiple_files(bencher: Bencher) {
    let data1 = text_data::generate_by_size(50, 80);
    let data2 = text_data::generate_by_size(50, 80);
    let data3 = text_data::generate_by_size(50, 80);

    let file1 = setup_test_file(&data1);
    let file2 = setup_test_file(&data2);
    let file3 = setup_test_file(&data3);

    bencher
        .with_inputs(|| get_bench_args(&[&file1, &file2, &file3]).into_iter())
        .bench_values(|args| black_box(uumain(args)));
}

fn main() {
    divan::main();
}
