//! Decompose integrator's per-cycle latency into:
//!   acquire | write_file | exec_builder.output() | drop
//! to find the supermachine overhead vs rustc CPU time.

use std::time::{Duration, Instant};
use supermachine::Image;

const SOURCE: &[u8] = b"fn main() { println!(\"hello\"); }";
const CMD: &[&str] = &["sh", "-c", "rustc -O /tmp/main.rs -o /tmp/m && /tmp/m"];

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let snap = format!(
        "{}/.local/supermachine-snapshots/rust_1_slim",
        std::env::var("HOME")?
    );
    let image = Image::from_snapshot(&snap)?;
    let pool_size: usize = std::env::var("POOL")
        .ok()
        .and_then(|s| s.parse().ok())
        .unwrap_or(1);
    let pool = image
        .pool()
        .min(pool_size)
        .max(pool_size)
        .idle_timeout(Duration::MAX)
        .build()?;
    println!("pool: min={pool_size} max={pool_size}");

    let n: usize = std::env::var("N")
        .ok()
        .and_then(|s| s.parse().ok())
        .unwrap_or(10);
    let mut sums = [0u128; 4]; // acquire / write_file / exec / drop

    println!("=== {n} cycles, breakdown (µs) ===");
    println!("  cycle |    acq |   wfile |    exec |   drop |  total");
    for i in 0..n {
        let t0 = Instant::now();
        let vm = pool.acquire()?;
        let t1 = Instant::now();
        vm.write_file("/tmp/main.rs", SOURCE)?;
        let t2 = Instant::now();
        let _ = vm
            .exec_builder()
            .argv(CMD.iter().copied())
            .timeout(Duration::from_secs(60))
            .output()?;
        let t3 = Instant::now();
        drop(vm);
        let t4 = Instant::now();
        let acq = (t1 - t0).as_micros();
        let wf = (t2 - t1).as_micros();
        let ex = (t3 - t2).as_micros();
        let dr = (t4 - t3).as_micros();
        let total = (t4 - t0).as_micros();
        sums[0] += acq;
        sums[1] += wf;
        sums[2] += ex;
        sums[3] += dr;
        if i < 3 || i == n - 1 {
            println!("  {i:5} | {acq:>6} | {wf:>7} | {ex:>7} | {dr:>6} | {total:>6}");
        } else if i == 3 {
            println!("    ... ");
        }
    }
    let avg = |s: u128| s / n as u128;
    let total: u128 = sums.iter().sum::<u128>() / n as u128;
    println!(
        "\n  avg     | {:>6} | {:>7} | {:>7} | {:>6} | {:>6}",
        avg(sums[0]),
        avg(sums[1]),
        avg(sums[2]),
        avg(sums[3]),
        total
    );
    println!(
        "\n  rustc share: {:.1}%",
        (sums[2] as f64 / sums.iter().sum::<u128>() as f64) * 100.0
    );
    println!(
        "  supermachine overhead per cycle: {} µs",
        avg(sums[0]) + avg(sums[1]) + avg(sums[3])
    );
    Ok(())
}
