//! Cache performance measurement

use std::time::Instant;

use super::super::error::ProfilingError;

/// Cache performance result
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct CacheResult {
    pub l1_bandwidth_gbps: f64,
    pub l2_bandwidth_gbps: f64,
    pub l3_bandwidth_gbps: f64,
    pub l1_latency_ns: f64,
    pub l2_latency_ns: f64,
    pub l3_latency_ns: f64,
}

/// Measure cache performance for L1/L2/L3
pub fn measure_cache_performance() -> Result<CacheResult, ProfilingError> {
    super::super::logging::log_info("Measuring cache performance");

    let config = super::super::config::global_config_or_default();

    // Use detected cache sizes
    let (l1_bandwidth, l1_latency) = measure_cache_level(config.l1_cache_size);
    let (l2_bandwidth, l2_latency) = measure_cache_level(config.l2_cache_size);
    let (l3_bandwidth, l3_latency) = measure_cache_level(config.l3_cache_size);

    Ok(CacheResult {
        l1_bandwidth_gbps: l1_bandwidth,
        l2_bandwidth_gbps: l2_bandwidth,
        l3_bandwidth_gbps: l3_bandwidth,
        l1_latency_ns: l1_latency,
        l2_latency_ns: l2_latency,
        l3_latency_ns: l3_latency,
    })
}

fn measure_cache_level(size: usize) -> (f64, f64) {
    let iterations = 1000;
    let mut data: Vec<u64> = vec![0; size / 8];

    // Measure bandwidth
    let start = Instant::now();
    for _ in 0..iterations {
        for i in 0..data.len() {
            data[i] = data[i].wrapping_add(1);
        }
        std::hint::black_box(&data);
    }
    let duration = start.elapsed();
    let total_bytes = (size * iterations) as f64;
    let bandwidth_gbps = total_bytes / duration.as_secs_f64() / 1_000_000_000.0;

    // Measure latency
    let start = Instant::now();
    for _ in 0..iterations {
        for i in (0..data.len()).step_by(8) {
            data[i] = data[i].wrapping_add(1);
            std::hint::black_box(&data[i]);
        }
    }
    let duration = start.elapsed();
    let total_accesses = iterations * (data.len() / 8);
    let latency_ns = (duration.as_nanos() as f64) / (total_accesses as f64);

    (bandwidth_gbps, latency_ns)
}
