use leopard_vec::{LQueue, LVec, LMask};

fn main() {
    // Create a new queue (type-agnostic!)
    let q = LQueue::new();

    // Create LVecs using the lvec() method (uses default capacity)
    // Can use lvec_with_capacity() for specific sizes
    let x: LVec<u32> = q.lvec();
    let y: LVec<u32> = q.lvec();
    let a: LVec<f64> = q.lvec_with_capacity(500);
    let b: LVec<f64> = q.lvec_with_capacity(500);

    // ========================================================================
    // ALL Operations are recorded between q.start() and q.end()
    // This minimizes thread creation latency by doing everything in one bulk
    // parallel execution call.
    // ========================================================================

    println!("=== Recording and Executing All Operations in One Parallel Batch ===\n");

    q.start();

    // === Initialization (recorded) ===
    let x = x.fill_with(|i| (i + 1) as u32);
    let y = y.fill_with(|i| ((i + 1) * 2) as u32);
    let a = a.fill_with(|i| (i + 1) as f64 * 0.5);
    let b = b.fill_with(|i| (i + 1) as f64 * 0.25);

    // === Arithmetic operations (recorded) ===
    let z = &x * &y + &x * &y;  // z = 2 * x * y
    let w = &z / &x;             // w = z / x

    // === f64 computations (recorded) ===
    let c = &a * &b;
    let d = &c + &a;

    // === Map operations (recorded) ===
    let mapped = a.map(|i, val| val * (i as f64 + 1.0));

    // === Conditional map (recorded) ===
    let branched = x.map_where(
        |_, val| *val % 2 == 0,   // condition: is even?
        |_, val| val * 10,        // if true: multiply by 10
        |_, val| val + 1000,      // if false: add 1000
    );

    // Execute ALL operations in one parallel execution pass!
    q.end();

    // ========================================================================
    // Retrieve results after q.end()
    // ========================================================================

    println!("=== Results from First Batch ===");

    if let Some(x_result) = x.materialize() {
        println!("x[0..5] = {:?}", &x_result[0..5]);
    }

    if let Some(y_result) = y.materialize() {
        println!("y[0..5] = {:?}", &y_result[0..5]);
    }

    if let Some(z_result) = z.materialize() {
        println!("z[0..5] = 2*x*y = {:?}", &z_result[0..5]);
    }

    if let Some(w_result) = w.materialize() {
        println!("w[0..5] = z/x = {:?}", &w_result[0..5]);
    }

    if let Some(c_result) = c.materialize() {
        println!("c[0..5] = a*b = {:?}", &c_result[0..5]);
    }

    if let Some(d_result) = d.materialize() {
        println!("d[0..5] = c+a = {:?}", &d_result[0..5]);
    }

    if let Some(mapped_result) = mapped.materialize() {
        println!("mapped[0..5] = {:?}", &mapped_result[0..5]);
    }

    if let Some(branched_result) = branched.materialize() {
        println!("branched[0..6] = {:?}", &branched_result[0..6]);
        // Expected: [1001, 20, 1003, 40, 1005, 60]
    }

    // ========================================================================
    // Demo: Blend with Mask
    // ========================================================================
    
    println!("\n=== Blend Demo with Mask ===");
    
    let v1: LVec<u32> = q.lvec_with_capacity(10);
    let v2: LVec<u32> = q.lvec_with_capacity(10);
    
    // Create a mask from closure: true for indices >= 5
    let mask = LMask::from_fn(10, |i| i >= 5);
    println!("mask: {:?}", mask.as_slice());
    
    q.start();
    
    let v1 = v1.fill_with(|i| i as u32);           // [0, 1, 2, ..., 9]
    let v2 = v2.fill_with(|i| (i * 100) as u32);   // [0, 100, 200, ..., 900]
    
    // Blend: use v1 where mask is false, v2 where mask is true
    let blended = v1.blend(&v2, &mask);
    
    // Masked apply: double values only where mask is true
    let masked_doubled = v1.masked_apply(&mask, |_, val| val * 2);
    
    // Masked fill: set to 999 where mask is true
    let masked_filled = v1.masked_fill(&mask, 999);
    
    q.end();
    
    println!("v1: {:?}", v1.materialize().unwrap());
    println!("v2: {:?}", v2.materialize().unwrap());
    println!("blended (v1 where i<5, v2 where i>=5): {:?}", blended.materialize().unwrap());
    println!("masked_doubled (v1*2 where i>=5): {:?}", masked_doubled.materialize().unwrap());
    println!("masked_filled (999 where i>=5): {:?}", masked_filled.materialize().unwrap());

    // ========================================================================
    // Demo: Mask Operations
    // ========================================================================
    
    println!("\n=== Mask Operations Demo ===");
    
    let mask_a = LMask::from_fn(8, |i| i < 4);      // [true, true, true, true, false, false, false, false]
    let mask_b = LMask::from_fn(8, |i| i % 2 == 0); // [true, false, true, false, true, false, true, false]
    
    println!("mask_a (i < 4):      {:?}", mask_a.as_slice());
    println!("mask_b (i %% 2 == 0): {:?}", mask_b.as_slice());
    println!("mask_a & mask_b:     {:?}", (&mask_a & &mask_b).as_slice());
    println!("mask_a | mask_b:     {:?}", (&mask_a | &mask_b).as_slice());
    println!("mask_a ^ mask_b:     {:?}", (&mask_a ^ &mask_b).as_slice());
    println!("!mask_a:             {:?}", (!&mask_a).as_slice());

    // ========================================================================
    // Demo: Chained Operations
    // ========================================================================
    
    println!("\n=== Chained Operations Demo ===");
    
    let arr: LVec<f64> = q.lvec_with_capacity(8);
    
    q.start();
    
    // Initialize
    let arr = arr.fill_with(|i| i as f64 + 1.0);  // [1, 2, 3, 4, 5, 6, 7, 8]
    
    // Chain: square -> conditional -> add constant
    let squared = arr.map(|_, v| v * v);          // [1, 4, 9, 16, 25, 36, 49, 64]
    let conditional = squared.map_where(
        |_, v| *v > 20.0,
        |_, v| v / 2.0,    // if > 20: halve
        |_, v| v * 2.0,    // if <= 20: double
    );  // [2, 8, 18, 32, 12.5, 18, 24.5, 32]
    
    q.end();
    
    println!("arr: {:?}", arr.materialize().unwrap());
    println!("squared: {:?}", squared.materialize().unwrap());
    println!("conditional: {:?}", conditional.materialize().unwrap());

    // ========================================================================
    // Demo: is_recording check
    // ========================================================================
    
    println!("\n=== Queue State Demo ===");
    println!("q.is_recording() before start: {}", q.is_recording());
    q.start();
    println!("q.is_recording() after start:  {}", q.is_recording());
    q.end();
    println!("q.is_recording() after end:    {}", q.is_recording());

    println!("\n=== Key Insights ===");
    println!("✓ ALL operations (fill, map, add, mul, div, map_where, blend, etc.) are RECORDED");
    println!("✓ No parallel execution happens until q.end() is called");
    println!("✓ q.end() executes ALL recorded operations in ONE parallel batch");
    println!("✓ This minimizes thread pool creation/destruction overhead!");
    println!("✓ Operations are executed in dependency order (DAG)");
    println!("✓ Same queue supports multiple types (u32, f64, etc.)");
    println!("✓ Masks can be created with LMask::from_fn() for complex patterns");
}
