//! # Leopard
//!
//! A high-performance parallelized vector container library with deferred execution.
//!
//! Leopard provides [`LVec`], a parallel vector container that records operations and
//! executes them in a single bulk parallel pass. This design minimizes thread pool
//! creation overhead by batching all operations together.
//!
//! ## Key Features
//!
//! - **Deferred Execution**: Operations are recorded between [`LQueue::start`] and
//!   [`LQueue::end`], then executed in one parallel batch
//! - **Type-Agnostic Queue**: A single [`LQueue`] can manage `LVec<T>` of different types
//! - **SIMD-Style Masking**: Boolean masks with blend, select, and masked operations
//! - **Operator Overloading**: Natural syntax with `+`, `-`, `*`, `/` operators
//! - **Dependency Graph**: Operations are automatically ordered based on data dependencies
//!
//! ## Quick Start
//!
//! ```rust
//! use leopard::{LQueue, LVec, LMask};
//!
//! let q = LQueue::new();
//! let x: LVec<f64> = q.lvec_with_capacity(1000);
//! let y: LVec<f64> = q.lvec_with_capacity(1000);
//!
//! q.start();
//! let x = x.fill_with(|i| i as f64);
//! let y = y.fill_with(|i| (i * 2) as f64);
//! let z = &x * &y + &x;
//! q.end();
//!
//! let result = z.materialize().unwrap();
//! ```

use std::any::Any;
use std::cell::RefCell;
use std::ops::{Add, Sub, Mul, Div, Index, BitAnd, BitOr, BitXor, Not};
use std::rc::Rc;
use std::sync::Arc;
use rayon::prelude::*;

/// Default reserved capacity for LVec when using [`LQueue::lvec`].
const DEFAULT_CAPACITY: usize = 128;

// ============================================================================
// Type-erased Operation Trait (Internal)
// ============================================================================

/// Internal trait for type-erased operations that can be executed.
trait ErasedOperation: Send + Sync {
    /// Execute the operation and return the result as a type-erased Arc.
    fn execute(&self, results: &[Option<Arc<dyn Any + Send + Sync>>]) -> Arc<dyn Any + Send + Sync>;
    
    /// Get the unique result ID for this operation.
    fn result_id(&self) -> usize;
    
    /// Get the list of result IDs this operation depends on.
    fn dependencies(&self) -> Vec<usize>;
}

// ============================================================================
// Operation Types (Internal)
// ============================================================================

#[derive(Clone, Copy, Debug)]
enum BinaryOpType {
    Add,
    Sub,
    Mul,
    Div,
}

/// Source of an operand - either direct data or a pending result from another operation.
#[derive(Clone)]
enum OperandSource<T: Clone + Send + Sync> {
    /// Direct data that is already available.
    Direct(Arc<Vec<T>>),
    /// Pending result from another operation, identified by result ID.
    Pending(usize),
}

/// Binary operation between two LVecs (add, sub, mul, div).
struct BinaryOp<T: Clone + Send + Sync + 'static> {
    op_type: BinaryOpType,
    left: OperandSource<T>,
    right: OperandSource<T>,
    result_id: usize,
}

impl<T> ErasedOperation for BinaryOp<T>
where
    T: Clone + Send + Sync + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Div<Output = T> + 'static,
{
    fn execute(&self, results: &[Option<Arc<dyn Any + Send + Sync>>]) -> Arc<dyn Any + Send + Sync> {
        let left_data = get_data::<T>(&self.left, results);
        let right_data = get_data::<T>(&self.right, results);
        let len = left_data.len().min(right_data.len());
        let op_type = self.op_type;

        let result: Vec<T> = (0..len)
            .into_par_iter()
            .map(|i| {
                let l = left_data[i].clone();
                let r = right_data[i].clone();
                match op_type {
                    BinaryOpType::Add => l + r,
                    BinaryOpType::Sub => l - r,
                    BinaryOpType::Mul => l * r,
                    BinaryOpType::Div => l / r,
                }
            })
            .collect();

        Arc::new(result)
    }

    fn result_id(&self) -> usize {
        self.result_id
    }

    fn dependencies(&self) -> Vec<usize> {
        let mut deps = Vec::new();
        if let OperandSource::Pending(id) = &self.left {
            deps.push(*id);
        }
        if let OperandSource::Pending(id) = &self.right {
            deps.push(*id);
        }
        deps
    }
}

/// Map operation that transforms each element using a closure.
struct MapOp<T: Clone + Send + Sync + 'static> {
    source: OperandSource<T>,
    func: Arc<dyn Fn(usize, &T) -> T + Send + Sync>,
    len: usize,
    result_id: usize,
}

impl<T: Clone + Send + Sync + 'static> ErasedOperation for MapOp<T> {
    fn execute(&self, results: &[Option<Arc<dyn Any + Send + Sync>>]) -> Arc<dyn Any + Send + Sync> {
        let data = get_data::<T>(&self.source, results);
        let len = self.len.min(data.len());
        let func = &self.func;

        let result: Vec<T> = (0..len)
            .into_par_iter()
            .map(|i| func(i, &data[i]))
            .collect();

        Arc::new(result)
    }

    fn result_id(&self) -> usize {
        self.result_id
    }

    fn dependencies(&self) -> Vec<usize> {
        if let OperandSource::Pending(id) = &self.source {
            vec![*id]
        } else {
            vec![]
        }
    }
}

/// Conditional map operation that applies different transformations based on a condition.
struct MapWhereOp<T: Clone + Send + Sync + 'static> {
    source: OperandSource<T>,
    condition: Arc<dyn Fn(usize, &T) -> bool + Send + Sync>,
    if_true: Arc<dyn Fn(usize, &T) -> T + Send + Sync>,
    if_false: Arc<dyn Fn(usize, &T) -> T + Send + Sync>,
    len: usize,
    result_id: usize,
}

impl<T: Clone + Send + Sync + 'static> ErasedOperation for MapWhereOp<T> {
    fn execute(&self, results: &[Option<Arc<dyn Any + Send + Sync>>]) -> Arc<dyn Any + Send + Sync> {
        let data = get_data::<T>(&self.source, results);
        let len = self.len.min(data.len());

        let result: Vec<T> = (0..len)
            .into_par_iter()
            .map(|i| {
                if (self.condition)(i, &data[i]) {
                    (self.if_true)(i, &data[i])
                } else {
                    (self.if_false)(i, &data[i])
                }
            })
            .collect();

        Arc::new(result)
    }

    fn result_id(&self) -> usize {
        self.result_id
    }

    fn dependencies(&self) -> Vec<usize> {
        if let OperandSource::Pending(id) = &self.source {
            vec![*id]
        } else {
            vec![]
        }
    }
}

/// Blend operation that selects elements from two vectors based on a mask.
struct BlendOp<T: Clone + Send + Sync + 'static> {
    if_false: OperandSource<T>,
    if_true: OperandSource<T>,
    mask: Arc<Vec<bool>>,
    len: usize,
    result_id: usize,
}

impl<T: Clone + Send + Sync + 'static> ErasedOperation for BlendOp<T> {
    fn execute(&self, results: &[Option<Arc<dyn Any + Send + Sync>>]) -> Arc<dyn Any + Send + Sync> {
        let false_data = get_data::<T>(&self.if_false, results);
        let true_data = get_data::<T>(&self.if_true, results);
        let mask = &self.mask;
        let len = self.len;

        let result: Vec<T> = (0..len)
            .into_par_iter()
            .map(|i| {
                if mask[i] {
                    true_data[i].clone()
                } else {
                    false_data[i].clone()
                }
            })
            .collect();

        Arc::new(result)
    }

    fn result_id(&self) -> usize {
        self.result_id
    }

    fn dependencies(&self) -> Vec<usize> {
        let mut deps = Vec::new();
        if let OperandSource::Pending(id) = &self.if_false {
            deps.push(*id);
        }
        if let OperandSource::Pending(id) = &self.if_true {
            deps.push(*id);
        }
        deps
    }
}

/// Masked apply operation that applies a function only where mask is true.
struct MaskedApplyOp<T: Clone + Send + Sync + 'static> {
    source: OperandSource<T>,
    mask: Arc<Vec<bool>>,
    func: Arc<dyn Fn(usize, &T) -> T + Send + Sync>,
    len: usize,
    result_id: usize,
}

impl<T: Clone + Send + Sync + 'static> ErasedOperation for MaskedApplyOp<T> {
    fn execute(&self, results: &[Option<Arc<dyn Any + Send + Sync>>]) -> Arc<dyn Any + Send + Sync> {
        let data = get_data::<T>(&self.source, results);
        let mask = &self.mask;
        let len = self.len;
        let func = &self.func;

        let result: Vec<T> = (0..len)
            .into_par_iter()
            .map(|i| {
                if mask[i] {
                    func(i, &data[i])
                } else {
                    data[i].clone()
                }
            })
            .collect();

        Arc::new(result)
    }

    fn result_id(&self) -> usize {
        self.result_id
    }

    fn dependencies(&self) -> Vec<usize> {
        if let OperandSource::Pending(id) = &self.source {
            vec![*id]
        } else {
            vec![]
        }
    }
}

/// Fill operation that initializes a vector using a closure.
struct FillOp<T: Clone + Send + Sync + 'static> {
    func: Arc<dyn Fn(usize) -> T + Send + Sync>,
    len: usize,
    result_id: usize,
}

impl<T: Clone + Send + Sync + 'static> ErasedOperation for FillOp<T> {
    fn execute(&self, _results: &[Option<Arc<dyn Any + Send + Sync>>]) -> Arc<dyn Any + Send + Sync> {
        let len = self.len;
        let func = &self.func;

        let result: Vec<T> = (0..len)
            .into_par_iter()
            .map(|i| func(i))
            .collect();

        Arc::new(result)
    }

    fn result_id(&self) -> usize {
        self.result_id
    }

    fn dependencies(&self) -> Vec<usize> {
        vec![]
    }
}

/// Helper to extract data from an operand source.
fn get_data<T: Clone + Send + Sync + 'static>(
    source: &OperandSource<T>,
    results: &[Option<Arc<dyn Any + Send + Sync>>],
) -> Arc<Vec<T>> {
    match source {
        OperandSource::Direct(data) => Arc::clone(data),
        OperandSource::Pending(id) => {
            let any_ref = results[*id].as_ref().unwrap();
            any_ref.clone().downcast::<Vec<T>>().unwrap()
        }
    }
}

// ============================================================================
// LMask - Boolean mask for SIMD-style branchless operations
// ============================================================================

/// A boolean mask for parallel conditional operations.
///
/// `LMask` provides SIMD-style masking capabilities for [`LVec`] operations.
/// Masks can be combined using logical operators (`&`, `|`, `^`, `!`) and
/// used with methods like [`LVec::blend`], [`LVec::masked_apply`], and
/// [`LVec::masked_fill`].
///
/// # Examples
///
/// ```rust
/// use leopard::LMask;
///
/// // Create a uniform mask
/// let all_true = LMask::new(10, true);
///
/// // Create a pattern mask
/// let evens = LMask::from_fn(10, |i| i % 2 == 0);
///
/// // Combine masks
/// let combined = &all_true & &evens;
/// let inverted = !&evens;
/// ```
#[derive(Clone)]
pub struct LMask {
    data: Arc<Vec<bool>>,
    len: usize,
}

impl LMask {
    /// Creates a new mask with all elements set to the same value.
    ///
    /// # Arguments
    ///
    /// * `len` - The length of the mask
    /// * `value` - The boolean value for all elements
    ///
    /// # Examples
    ///
    /// ```rust
    /// use leopard::LMask;
    ///
    /// let all_true = LMask::new(100, true);
    /// let all_false = LMask::new(100, false);
    /// ```
    pub fn new(len: usize, value: bool) -> Self {
        LMask {
            data: Arc::new(vec![value; len]),
            len,
        }
    }

    /// Creates a mask from a closure that takes an index and returns a boolean.
    ///
    /// # Arguments
    ///
    /// * `len` - The length of the mask
    /// * `f` - A closure that takes an index `usize` and returns `bool`
    ///
    /// # Examples
    ///
    /// ```rust
    /// use leopard::LMask;
    ///
    /// // Even indices
    /// let evens = LMask::from_fn(10, |i| i % 2 == 0);
    ///
    /// // First half
    /// let first_half = LMask::from_fn(100, |i| i < 50);
    /// ```
    pub fn from_fn<F>(len: usize, f: F) -> Self
    where
        F: Fn(usize) -> bool,
    {
        let data: Vec<bool> = (0..len).map(f).collect();
        LMask {
            data: Arc::new(data),
            len,
        }
    }

    /// Returns the length of the mask.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use leopard::LMask;
    ///
    /// let mask = LMask::new(100, true);
    /// assert_eq!(mask.len(), 100);
    /// ```
    #[inline]
    pub fn len(&self) -> usize {
        self.len
    }

    /// Returns `true` if the mask has no elements.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use leopard::LMask;
    ///
    /// let empty = LMask::new(0, true);
    /// assert!(empty.is_empty());
    /// ```
    #[inline]
    pub fn is_empty(&self) -> bool {
        self.len == 0
    }

    /// Returns the mask data as a slice.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use leopard::LMask;
    ///
    /// let mask = LMask::from_fn(5, |i| i > 2);
    /// assert_eq!(mask.as_slice(), &[false, false, false, true, true]);
    /// ```
    #[inline]
    pub fn as_slice(&self) -> &[bool] {
        &self.data[..self.len]
    }
}

impl Index<usize> for LMask {
    type Output = bool;
    
    /// Accesses the mask value at the given index.
    ///
    /// # Panics
    ///
    /// Panics if `index >= self.len()`.
    #[inline]
    fn index(&self, index: usize) -> &Self::Output {
        &self.data[index]
    }
}

impl BitAnd for &LMask {
    type Output = LMask;
    
    /// Performs element-wise logical AND between two masks.
    fn bitand(self, other: Self) -> Self::Output {
        let len = self.len.min(other.len);
        let data: Vec<bool> = (0..len).map(|i| self.data[i] && other.data[i]).collect();
        LMask { data: Arc::new(data), len }
    }
}

impl BitAnd for LMask {
    type Output = LMask;
    fn bitand(self, other: Self) -> Self::Output { (&self).bitand(&other) }
}

impl BitOr for &LMask {
    type Output = LMask;
    
    /// Performs element-wise logical OR between two masks.
    fn bitor(self, other: Self) -> Self::Output {
        let len = self.len.min(other.len);
        let data: Vec<bool> = (0..len).map(|i| self.data[i] || other.data[i]).collect();
        LMask { data: Arc::new(data), len }
    }
}

impl BitOr for LMask {
    type Output = LMask;
    fn bitor(self, other: Self) -> Self::Output { (&self).bitor(&other) }
}

impl BitXor for &LMask {
    type Output = LMask;
    
    /// Performs element-wise logical XOR between two masks.
    fn bitxor(self, other: Self) -> Self::Output {
        let len = self.len.min(other.len);
        let data: Vec<bool> = (0..len).map(|i| self.data[i] ^ other.data[i]).collect();
        LMask { data: Arc::new(data), len }
    }
}

impl BitXor for LMask {
    type Output = LMask;
    fn bitxor(self, other: Self) -> Self::Output { (&self).bitxor(&other) }
}

impl Not for &LMask {
    type Output = LMask;
    
    /// Performs element-wise logical NOT on the mask.
    fn not(self) -> Self::Output {
        let data: Vec<bool> = self.data.iter().map(|&b| !b).collect();
        LMask { data: Arc::new(data), len: self.len }
    }
}

impl Not for LMask {
    type Output = LMask;
    fn not(self) -> Self::Output { (&self).not() }
}

impl std::fmt::Debug for LMask {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "LMask({:?})", self.as_slice())
    }
}

// ============================================================================
// LQueue - The operation queue
// ============================================================================

/// Internal state of the operation queue.
struct LQueueInner {
    operations: Vec<Box<dyn ErasedOperation>>,
    results: Vec<Option<Arc<dyn Any + Send + Sync>>>,
    recording: bool,
    next_result_id: usize,
}

/// The operation queue that records and executes parallel operations.
///
/// `LQueue` is the central coordinator for deferred parallel execution. It manages
/// the recording of operations and their bulk execution, minimizing thread pool
/// overhead by batching all operations together.
///
/// # Workflow
///
/// 1. Create vectors with [`LQueue::lvec`] or [`LQueue::lvec_with_capacity`]
/// 2. Call [`LQueue::start`] to begin recording
/// 3. Perform operations on [`LVec`] instances (all operations are recorded, not executed)
/// 4. Call [`LQueue::end`] to execute all recorded operations in parallel
/// 5. Use [`LVec::materialize`] to retrieve results
///
/// # Type Agnostic
///
/// A single `LQueue` can manage vectors of different types simultaneously:
///
/// ```rust
/// use leopard::{LQueue, LVec};
///
/// let q = LQueue::new();
/// let integers: LVec<i32> = q.lvec_with_capacity(100);
/// let floats: LVec<f64> = q.lvec_with_capacity(100);
/// // Both can be used in the same recording session
/// ```
///
/// # Examples
///
/// ```rust
/// use leopard::{LQueue, LVec};
///
/// let q = LQueue::new();
/// let x: LVec<f64> = q.lvec_with_capacity(1000);
/// let y: LVec<f64> = q.lvec_with_capacity(1000);
///
/// q.start();
/// let x = x.fill_with(|i| i as f64);
/// let y = y.fill_with(|i| (i * 2) as f64);
/// let z = &x + &y;
/// q.end();
///
/// let result = z.materialize().unwrap();
/// println!("Sum: {:?}", &result[0..5]);
/// ```
#[derive(Clone)]
pub struct LQueue {
    inner: Rc<RefCell<LQueueInner>>,
}

impl LQueue {
    /// Creates a new operation queue.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use leopard::LQueue;
    ///
    /// let q = LQueue::new();
    /// ```
    pub fn new() -> Self {
        LQueue {
            inner: Rc::new(RefCell::new(LQueueInner {
                operations: Vec::new(),
                results: Vec::new(),
                recording: false,
                next_result_id: 0,
            })),
        }
    }

    /// Creates a new [`LVec`] with the default capacity (128 elements).
    ///
    /// # Type Parameters
    ///
    /// * `T` - The element type, must implement `Clone + Send + Sync + Default`
    ///
    /// # Examples
    ///
    /// ```rust
    /// use leopard::{LQueue, LVec};
    ///
    /// let q = LQueue::new();
    /// let vec: LVec<f64> = q.lvec();
    /// assert_eq!(vec.capacity(), 128);
    /// ```
    pub fn lvec<T>(&self) -> LVec<T>
    where
        T: Clone + Send + Sync + Default + 'static,
    {
        self.lvec_with_capacity(DEFAULT_CAPACITY)
    }

    /// Creates a new [`LVec`] with the specified capacity.
    ///
    /// # Arguments
    ///
    /// * `capacity` - The number of elements the vector can hold
    ///
    /// # Type Parameters
    ///
    /// * `T` - The element type, must implement `Clone + Send + Sync + Default`
    ///
    /// # Examples
    ///
    /// ```rust
    /// use leopard::{LQueue, LVec};
    ///
    /// let q = LQueue::new();
    /// let vec: LVec<f64> = q.lvec_with_capacity(10000);
    /// assert_eq!(vec.capacity(), 10000);
    /// ```
    pub fn lvec_with_capacity<T>(&self, capacity: usize) -> LVec<T>
    where
        T: Clone + Send + Sync + Default + 'static,
    {
        LVec {
            data: Arc::new(vec![T::default(); capacity]),
            len: capacity,
            capacity,
            queue: Rc::clone(&self.inner),
            pending_result_id: None,
        }
    }

    /// Starts recording operations.
    ///
    /// After calling `start()`, all operations on [`LVec`] instances created from
    /// this queue will be recorded rather than executed. The operations will be
    /// executed when [`LQueue::end`] is called.
    ///
    /// # Panics
    ///
    /// Operations on [`LVec`] will panic if called outside of a `start()`/`end()` block.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use leopard::{LQueue, LVec};
    ///
    /// let q = LQueue::new();
    /// let x: LVec<f64> = q.lvec_with_capacity(100);
    ///
    /// q.start();  // Begin recording
    /// let x = x.fill(42.0);
    /// // ... more operations ...
    /// q.end();    // Execute all recorded operations
    /// ```
    pub fn start(&self) {
        let mut inner = self.inner.borrow_mut();
        inner.recording = true;
        inner.operations.clear();
        inner.results.clear();
        inner.next_result_id = 0;
    }

    /// Stops recording and executes all recorded operations in parallel.
    ///
    /// This method executes all operations that were recorded since the last
    /// [`LQueue::start`] call. Operations are executed in dependency order,
    /// with independent operations running in parallel.
    ///
    /// After `end()` is called, results can be retrieved using [`LVec::materialize`].
    ///
    /// # Performance
    ///
    /// All parallel execution happens in this single call, minimizing thread pool
    /// creation overhead compared to executing operations individually.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use leopard::{LQueue, LVec};
    ///
    /// let q = LQueue::new();
    /// let x: LVec<f64> = q.lvec_with_capacity(1000);
    ///
    /// q.start();
    /// let x = x.fill_with(|i| i as f64);
    /// let y = x.map(|_, v| v * 2.0);
    /// q.end();  // Both fill and map execute here
    ///
    /// let result = y.materialize().unwrap();
    /// ```
    pub fn end(&self) {
        let mut inner = self.inner.borrow_mut();
        inner.recording = false;
        Self::execute_all(&mut inner);
    }

    /// Executes all recorded operations respecting dependencies.
    fn execute_all(inner: &mut LQueueInner) {
        if inner.operations.is_empty() {
            return;
        }

        // Resize results vector
        inner.results.resize_with(inner.next_result_id, || None);

        // Build dependency levels for proper execution order
        let mut levels: Vec<Vec<usize>> = Vec::new();
        let mut op_levels: Vec<usize> = vec![0; inner.operations.len()];

        let mut result_to_op: std::collections::HashMap<usize, usize> = std::collections::HashMap::new();
        for (i, op) in inner.operations.iter().enumerate() {
            result_to_op.insert(op.result_id(), i);
        }

        // Compute the level for each operation based on dependencies
        for (i, op) in inner.operations.iter().enumerate() {
            let mut level = 0;
            for dep_id in op.dependencies() {
                if let Some(&dep_op_idx) = result_to_op.get(&dep_id) {
                    level = level.max(op_levels[dep_op_idx] + 1);
                }
            }
            op_levels[i] = level;
            while levels.len() <= level {
                levels.push(Vec::new());
            }
            levels[level].push(i);
        }

        // Execute each level (operations within a level have no dependencies on each other)
        for level_ops in levels {
            for &op_idx in &level_ops {
                let result = inner.operations[op_idx].execute(&inner.results);
                let result_id = inner.operations[op_idx].result_id();
                inner.results[result_id] = Some(result);
            }
        }
    }

    /// Returns `true` if the queue is currently recording operations.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use leopard::LQueue;
    ///
    /// let q = LQueue::new();
    /// assert!(!q.is_recording());
    ///
    /// q.start();
    /// assert!(q.is_recording());
    ///
    /// q.end();
    /// assert!(!q.is_recording());
    /// ```
    pub fn is_recording(&self) -> bool {
        self.inner.borrow().recording
    }
}

impl Default for LQueue {
    fn default() -> Self {
        Self::new()
    }
}

// ============================================================================
// LVec - The parallelized vector container
// ============================================================================

/// A parallelized vector container with deferred execution.
///
/// `LVec` is the main data structure in Leopard. It represents a vector that
/// supports parallel operations through deferred execution. Operations on `LVec`
/// are recorded when called between [`LQueue::start`] and [`LQueue::end`], and
/// executed in bulk when `end()` is called.
///
/// # Creating LVec
///
/// `LVec` instances must be created through an [`LQueue`]:
///
/// ```rust
/// use leopard::{LQueue, LVec};
///
/// let q = LQueue::new();
/// let vec: LVec<f64> = q.lvec_with_capacity(1000);
/// ```
///
/// # Operations
///
/// All operations must be called between `q.start()` and `q.end()`:
///
/// - **Initialization**: [`fill`](LVec::fill), [`fill_with`](LVec::fill_with)
/// - **Transformation**: [`map`](LVec::map), [`map_where`](LVec::map_where)
/// - **Arithmetic**: `+`, `-`, `*`, `/` operators
/// - **Masking**: [`blend`](LVec::blend), [`masked_apply`](LVec::masked_apply), [`masked_fill`](LVec::masked_fill)
///
/// # Retrieving Results
///
/// After `q.end()`, use [`materialize`](LVec::materialize) to get the computed data:
///
/// ```rust
/// use leopard::{LQueue, LVec};
///
/// let q = LQueue::new();
/// let x: LVec<f64> = q.lvec_with_capacity(10);
///
/// q.start();
/// let x = x.fill_with(|i| i as f64);
/// q.end();
///
/// let data = x.materialize().unwrap();
/// println!("{:?}", &data[..]);
/// ```
///
/// # Pending State
///
/// After an operation is recorded, the resulting `LVec` is in a "pending" state.
/// The actual data is not available until after `q.end()` is called and
/// [`materialize`](LVec::materialize) is used to retrieve it.
#[derive(Clone)]
pub struct LVec<T: Clone + Send + Sync + 'static> {
    data: Arc<Vec<T>>,
    len: usize,
    capacity: usize,
    queue: Rc<RefCell<LQueueInner>>,
    pending_result_id: Option<usize>,
}

impl<T: Clone + Send + Sync + 'static> LVec<T> {
    /// Returns the length of the vector.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use leopard::{LQueue, LVec};
    ///
    /// let q = LQueue::new();
    /// let vec: LVec<f64> = q.lvec_with_capacity(100);
    /// assert_eq!(vec.len(), 100);
    /// ```
    #[inline]
    pub fn len(&self) -> usize {
        self.len
    }

    /// Returns `true` if the vector has no elements.
    #[inline]
    pub fn is_empty(&self) -> bool {
        self.len == 0
    }

    /// Returns the capacity of the vector.
    #[inline]
    pub fn capacity(&self) -> usize {
        self.capacity
    }

    /// Returns `true` if this vector has a pending operation.
    ///
    /// A pending vector's data is not yet available. Call [`materialize`](LVec::materialize)
    /// after [`LQueue::end`] to retrieve the computed data.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use leopard::{LQueue, LVec};
    ///
    /// let q = LQueue::new();
    /// let x: LVec<f64> = q.lvec_with_capacity(10);
    /// assert!(!x.is_pending());
    ///
    /// q.start();
    /// let y = x.fill(1.0);
    /// assert!(y.is_pending());
    /// q.end();
    /// ```
    #[inline]
    pub fn is_pending(&self) -> bool {
        self.pending_result_id.is_some()
    }

    /// Retrieves the computed data after [`LQueue::end`] has been called.
    ///
    /// For pending vectors (those created by operations), this returns the
    /// computed result. For non-pending vectors, this returns the original data.
    ///
    /// # Returns
    ///
    /// - `Some(Arc<Vec<T>>)` if the data is available
    /// - `None` if the vector is pending and `q.end()` hasn't been called
    ///
    /// # Examples
    ///
    /// ```rust
    /// use leopard::{LQueue, LVec};
    ///
    /// let q = LQueue::new();
    /// let x: LVec<f64> = q.lvec_with_capacity(5);
    ///
    /// q.start();
    /// let x = x.fill_with(|i| i as f64 * 2.0);
    /// q.end();
    ///
    /// let data = x.materialize().unwrap();
    /// assert_eq!(&data[..], &[0.0, 2.0, 4.0, 6.0, 8.0]);
    /// ```
    pub fn materialize(&self) -> Option<Arc<Vec<T>>> {
        if let Some(result_id) = self.pending_result_id {
            let inner = self.queue.borrow();
            inner.results.get(result_id).and_then(|r| {
                r.as_ref().and_then(|arc| arc.clone().downcast::<Vec<T>>().ok())
            })
        } else {
            Some(Arc::clone(&self.data))
        }
    }

    /// Returns the data as a slice.
    ///
    /// # Warning
    ///
    /// For pending vectors, this returns an empty or default-initialized slice,
    /// not the computed result. Use [`materialize`](LVec::materialize) after
    /// [`LQueue::end`] to get the actual computed data.
    #[inline]
    pub fn as_slice(&self) -> &[T] {
        &self.data[..self.len]
    }

    // ========================================================================
    // Internal helpers
    // ========================================================================

    fn get_source(&self) -> OperandSource<T> {
        if let Some(id) = self.pending_result_id {
            OperandSource::Pending(id)
        } else {
            OperandSource::Direct(Arc::clone(&self.data))
        }
    }

    fn create_pending(&self, result_id: usize, len: usize) -> Self {
        LVec {
            data: Arc::new(Vec::new()),
            len,
            capacity: len,
            queue: Rc::clone(&self.queue),
            pending_result_id: Some(result_id),
        }
    }

    // ========================================================================
    // Recording Operations
    // ========================================================================

    /// Fills the vector using a closure that takes an index.
    ///
    /// This is a recorded operation that must be called between [`LQueue::start`]
    /// and [`LQueue::end`].
    ///
    /// # Arguments
    ///
    /// * `f` - A closure that takes an index `usize` and returns the value `T`
    ///
    /// # Panics
    ///
    /// Panics if called outside of a `start()`/`end()` block.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use leopard::{LQueue, LVec};
    ///
    /// let q = LQueue::new();
    /// let x: LVec<f64> = q.lvec_with_capacity(5);
    ///
    /// q.start();
    /// let x = x.fill_with(|i| i as f64 * 10.0);
    /// q.end();
    ///
    /// let data = x.materialize().unwrap();
    /// assert_eq!(&data[..], &[0.0, 10.0, 20.0, 30.0, 40.0]);
    /// ```
    pub fn fill_with<F>(&self, f: F) -> Self
    where
        F: Fn(usize) -> T + Send + Sync + 'static,
    {
        let mut inner = self.queue.borrow_mut();
        if !inner.recording {
            panic!("fill_with must be called between q.start() and q.end()");
        }

        let result_id = inner.next_result_id;
        inner.next_result_id += 1;

        inner.operations.push(Box::new(FillOp {
            func: Arc::new(f),
            len: self.len,
            result_id,
        }));

        drop(inner);
        self.create_pending(result_id, self.len)
    }

    /// Fills the vector with a constant value.
    ///
    /// This is a recorded operation that must be called between [`LQueue::start`]
    /// and [`LQueue::end`].
    ///
    /// # Arguments
    ///
    /// * `value` - The value to fill the vector with
    ///
    /// # Panics
    ///
    /// Panics if called outside of a `start()`/`end()` block.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use leopard::{LQueue, LVec};
    ///
    /// let q = LQueue::new();
    /// let x: LVec<i32> = q.lvec_with_capacity(5);
    ///
    /// q.start();
    /// let x = x.fill(42);
    /// q.end();
    ///
    /// let data = x.materialize().unwrap();
    /// assert_eq!(&data[..], &[42, 42, 42, 42, 42]);
    /// ```
    pub fn fill(&self, value: T) -> Self {
        self.fill_with(move |_| value.clone())
    }

    /// Transforms each element using a closure.
    ///
    /// This is a recorded operation that must be called between [`LQueue::start`]
    /// and [`LQueue::end`].
    ///
    /// # Arguments
    ///
    /// * `f` - A closure that takes an index and a reference to the element,
    ///         and returns the transformed value
    ///
    /// # Panics
    ///
    /// Panics if called outside of a `start()`/`end()` block.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use leopard::{LQueue, LVec};
    ///
    /// let q = LQueue::new();
    /// let x: LVec<f64> = q.lvec_with_capacity(5);
    ///
    /// q.start();
    /// let x = x.fill_with(|i| i as f64);
    /// let y = x.map(|_, v| v * v);  // Square each element
    /// q.end();
    ///
    /// let data = y.materialize().unwrap();
    /// assert_eq!(&data[..], &[0.0, 1.0, 4.0, 9.0, 16.0]);
    /// ```
    pub fn map<F>(&self, f: F) -> Self
    where
        F: Fn(usize, &T) -> T + Send + Sync + 'static,
    {
        let mut inner = self.queue.borrow_mut();
        if !inner.recording {
            panic!("map must be called between q.start() and q.end()");
        }

        let result_id = inner.next_result_id;
        inner.next_result_id += 1;

        inner.operations.push(Box::new(MapOp {
            source: self.get_source(),
            func: Arc::new(f),
            len: self.len,
            result_id,
        }));

        drop(inner);
        self.create_pending(result_id, self.len)
    }

    /// Applies different transformations based on a condition.
    ///
    /// For each element, if the condition returns `true`, `if_true` is applied;
    /// otherwise, `if_false` is applied. This is SIMD-style branchless conditional
    /// execution.
    ///
    /// This is a recorded operation that must be called between [`LQueue::start`]
    /// and [`LQueue::end`].
    ///
    /// # Arguments
    ///
    /// * `condition` - A closure that returns `true` or `false` for each element
    /// * `if_true` - Transformation to apply when condition is `true`
    /// * `if_false` - Transformation to apply when condition is `false`
    ///
    /// # Panics
    ///
    /// Panics if called outside of a `start()`/`end()` block.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use leopard::{LQueue, LVec};
    ///
    /// let q = LQueue::new();
    /// let x: LVec<i32> = q.lvec_with_capacity(6);
    ///
    /// q.start();
    /// let x = x.fill_with(|i| i as i32 + 1);
    /// let y = x.map_where(
    ///     |_, v| *v % 2 == 0,  // Is even?
    ///     |_, v| v * 10,       // If even: multiply by 10
    ///     |_, v| v + 1000,     // If odd: add 1000
    /// );
    /// q.end();
    ///
    /// let data = y.materialize().unwrap();
    /// // [1, 2, 3, 4, 5, 6] -> [1001, 20, 1003, 40, 1005, 60]
    /// ```
    pub fn map_where<C, TF, FF>(&self, condition: C, if_true: TF, if_false: FF) -> Self
    where
        C: Fn(usize, &T) -> bool + Send + Sync + 'static,
        TF: Fn(usize, &T) -> T + Send + Sync + 'static,
        FF: Fn(usize, &T) -> T + Send + Sync + 'static,
    {
        let mut inner = self.queue.borrow_mut();
        if !inner.recording {
            panic!("map_where must be called between q.start() and q.end()");
        }

        let result_id = inner.next_result_id;
        inner.next_result_id += 1;

        inner.operations.push(Box::new(MapWhereOp {
            source: self.get_source(),
            condition: Arc::new(condition),
            if_true: Arc::new(if_true),
            if_false: Arc::new(if_false),
            len: self.len,
            result_id,
        }));

        drop(inner);
        self.create_pending(result_id, self.len)
    }

    /// Creates a mask from a predicate applied to each element.
    ///
    /// Unlike other operations, this executes immediately (not recorded) because
    /// masks are needed to define subsequent masking operations.
    ///
    /// # Arguments
    ///
    /// * `predicate` - A closure that returns `true` or `false` for each element
    ///
    /// # Warning
    ///
    /// This only works on non-pending vectors. For pending vectors, use
    /// [`LMask::from_fn`] with the appropriate pattern.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use leopard::{LQueue, LVec};
    ///
    /// let q = LQueue::new();
    /// let x: LVec<i32> = q.lvec_with_capacity(10);
    ///
    /// // Note: mask() works on the initial (non-pending) vector
    /// let mask = x.mask(|i, _| i >= 5);
    /// ```
    pub fn mask<F>(&self, predicate: F) -> LMask
    where
        F: Fn(usize, &T) -> bool,
    {
        let data: Vec<bool> = (0..self.len)
            .map(|i| predicate(i, &self.data[i]))
            .collect();
        
        LMask {
            data: Arc::new(data),
            len: self.len,
        }
    }

    /// Blends two vectors using a mask (SIMD-style select).
    ///
    /// For each element, if the mask is `true`, the value from `other` is used;
    /// otherwise, the value from `self` is used.
    ///
    /// This is a recorded operation that must be called between [`LQueue::start`]
    /// and [`LQueue::end`].
    ///
    /// # Arguments
    ///
    /// * `other` - The vector to blend with
    /// * `mask` - The mask determining which vector to select from
    ///
    /// # Panics
    ///
    /// Panics if called outside of a `start()`/`end()` block.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use leopard::{LQueue, LVec, LMask};
    ///
    /// let q = LQueue::new();
    /// let a: LVec<i32> = q.lvec_with_capacity(5);
    /// let b: LVec<i32> = q.lvec_with_capacity(5);
    /// let mask = LMask::from_fn(5, |i| i >= 3);
    ///
    /// q.start();
    /// let a = a.fill(1);
    /// let b = b.fill(100);
    /// let c = a.blend(&b, &mask);
    /// q.end();
    ///
    /// let data = c.materialize().unwrap();
    /// // mask: [false, false, false, true, true]
    /// // result: [1, 1, 1, 100, 100]
    /// ```
    pub fn blend(&self, other: &Self, mask: &LMask) -> Self {
        let mut inner = self.queue.borrow_mut();
        if !inner.recording {
            panic!("blend must be called between q.start() and q.end()");
        }

        let result_id = inner.next_result_id;
        inner.next_result_id += 1;
        let len = self.len.min(other.len).min(mask.len());

        inner.operations.push(Box::new(BlendOp {
            if_false: self.get_source(),
            if_true: other.get_source(),
            mask: Arc::clone(&mask.data),
            len,
            result_id,
        }));

        drop(inner);
        self.create_pending(result_id, len)
    }

    /// Selects between two vectors based on a mask.
    ///
    /// This is equivalent to `if_false.blend(if_true, mask)`.
    ///
    /// # Arguments
    ///
    /// * `mask` - The mask determining which vector to select from
    /// * `if_true` - Values to use where mask is `true`
    /// * `if_false` - Values to use where mask is `false`
    ///
    /// # Panics
    ///
    /// Panics if called outside of a `start()`/`end()` block.
    pub fn select(mask: &LMask, if_true: &Self, if_false: &Self) -> Self {
        if_false.blend(if_true, mask)
    }

    /// Applies a function only where the mask is `true`.
    ///
    /// Elements where the mask is `false` retain their original values.
    ///
    /// This is a recorded operation that must be called between [`LQueue::start`]
    /// and [`LQueue::end`].
    ///
    /// # Arguments
    ///
    /// * `mask` - The mask determining where to apply the function
    /// * `f` - The function to apply
    ///
    /// # Panics
    ///
    /// Panics if called outside of a `start()`/`end()` block.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use leopard::{LQueue, LVec, LMask};
    ///
    /// let q = LQueue::new();
    /// let x: LVec<i32> = q.lvec_with_capacity(5);
    /// let mask = LMask::from_fn(5, |i| i >= 3);
    ///
    /// q.start();
    /// let x = x.fill_with(|i| i as i32);
    /// let y = x.masked_apply(&mask, |_, v| v * 100);
    /// q.end();
    ///
    /// let data = y.materialize().unwrap();
    /// // [0, 1, 2, 3, 4] with mask [F, F, F, T, T]
    /// // result: [0, 1, 2, 300, 400]
    /// ```
    pub fn masked_apply<F>(&self, mask: &LMask, f: F) -> Self
    where
        F: Fn(usize, &T) -> T + Send + Sync + 'static,
    {
        let mut inner = self.queue.borrow_mut();
        if !inner.recording {
            panic!("masked_apply must be called between q.start() and q.end()");
        }

        let result_id = inner.next_result_id;
        inner.next_result_id += 1;
        let len = self.len.min(mask.len());

        inner.operations.push(Box::new(MaskedApplyOp {
            source: self.get_source(),
            mask: Arc::clone(&mask.data),
            func: Arc::new(f),
            len,
            result_id,
        }));

        drop(inner);
        self.create_pending(result_id, len)
    }

    /// Fills with a constant value only where the mask is `true`.
    ///
    /// Elements where the mask is `false` retain their original values.
    ///
    /// This is a recorded operation that must be called between [`LQueue::start`]
    /// and [`LQueue::end`].
    ///
    /// # Arguments
    ///
    /// * `mask` - The mask determining where to fill
    /// * `value` - The value to fill with
    ///
    /// # Panics
    ///
    /// Panics if called outside of a `start()`/`end()` block.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use leopard::{LQueue, LVec, LMask};
    ///
    /// let q = LQueue::new();
    /// let x: LVec<i32> = q.lvec_with_capacity(5);
    /// let mask = LMask::from_fn(5, |i| i >= 3);
    ///
    /// q.start();
    /// let x = x.fill_with(|i| i as i32);
    /// let y = x.masked_fill(&mask, 999);
    /// q.end();
    ///
    /// let data = y.materialize().unwrap();
    /// // [0, 1, 2, 3, 4] with mask [F, F, F, T, T]
    /// // result: [0, 1, 2, 999, 999]
    /// ```
    pub fn masked_fill(&self, mask: &LMask, value: T) -> Self
    where
        T: 'static,
    {
        self.masked_apply(mask, move |_, _| value.clone())
    }
}

impl<T: Clone + Send + Sync + Default + 'static> Default for LVec<T> {
    fn default() -> Self {
        panic!("LVec must be created via LQueue::lvec() or LQueue::lvec_with_capacity()")
    }
}

impl<T: Clone + Send + Sync + 'static> Index<usize> for LVec<T> {
    type Output = T;
    
    /// Accesses the element at the given index.
    ///
    /// # Warning
    ///
    /// For pending vectors, this accesses the original (uncomputed) data,
    /// not the result of the pending operation. Use [`materialize`](LVec::materialize)
    /// after [`LQueue::end`] to get the computed data.
    ///
    /// # Panics
    ///
    /// Panics if `index >= self.len()`.
    #[inline]
    fn index(&self, index: usize) -> &Self::Output {
        &self.data[index]
    }
}

// ============================================================================
// Operator Overloading (all recorded)
// ============================================================================

fn record_binary_op<T>(left: &LVec<T>, right: &LVec<T>, op_type: BinaryOpType) -> LVec<T>
where
    T: Clone + Send + Sync + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Div<Output = T> + 'static,
{
    let mut inner = left.queue.borrow_mut();
    if !inner.recording {
        panic!("Binary operations must be called between q.start() and q.end()");
    }

    let result_id = inner.next_result_id;
    inner.next_result_id += 1;
    let len = left.len.min(right.len);

    inner.operations.push(Box::new(BinaryOp {
        op_type,
        left: left.get_source(),
        right: right.get_source(),
        result_id,
    }));

    drop(inner);
    left.create_pending(result_id, len)
}

impl<T> Add for LVec<T>
where
    T: Clone + Send + Sync + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Div<Output = T> + 'static,
{
    type Output = Self;
    
    /// Adds two vectors element-wise (recorded operation).
    fn add(self, other: Self) -> Self::Output {
        record_binary_op(&self, &other, BinaryOpType::Add)
    }
}

impl<T> Add for &LVec<T>
where
    T: Clone + Send + Sync + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Div<Output = T> + 'static,
{
    type Output = LVec<T>;
    
    /// Adds two vectors element-wise (recorded operation).
    fn add(self, other: Self) -> Self::Output {
        record_binary_op(self, other, BinaryOpType::Add)
    }
}

impl<T> Sub for LVec<T>
where
    T: Clone + Send + Sync + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Div<Output = T> + 'static,
{
    type Output = Self;
    
    /// Subtracts two vectors element-wise (recorded operation).
    fn sub(self, other: Self) -> Self::Output {
        record_binary_op(&self, &other, BinaryOpType::Sub)
    }
}

impl<T> Sub for &LVec<T>
where
    T: Clone + Send + Sync + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Div<Output = T> + 'static,
{
    type Output = LVec<T>;
    
    /// Subtracts two vectors element-wise (recorded operation).
    fn sub(self, other: Self) -> Self::Output {
        record_binary_op(self, other, BinaryOpType::Sub)
    }
}

impl<T> Mul for LVec<T>
where
    T: Clone + Send + Sync + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Div<Output = T> + 'static,
{
    type Output = Self;
    
    /// Multiplies two vectors element-wise (recorded operation).
    fn mul(self, other: Self) -> Self::Output {
        record_binary_op(&self, &other, BinaryOpType::Mul)
    }
}

impl<T> Mul for &LVec<T>
where
    T: Clone + Send + Sync + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Div<Output = T> + 'static,
{
    type Output = LVec<T>;
    
    /// Multiplies two vectors element-wise (recorded operation).
    fn mul(self, other: Self) -> Self::Output {
        record_binary_op(self, other, BinaryOpType::Mul)
    }
}

impl<T> Div for LVec<T>
where
    T: Clone + Send + Sync + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Div<Output = T> + 'static,
{
    type Output = Self;
    
    /// Divides two vectors element-wise (recorded operation).
    fn div(self, other: Self) -> Self::Output {
        record_binary_op(&self, &other, BinaryOpType::Div)
    }
}

impl<T> Div for &LVec<T>
where
    T: Clone + Send + Sync + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Div<Output = T> + 'static,
{
    type Output = LVec<T>;
    
    /// Divides two vectors element-wise (recorded operation).
    fn div(self, other: Self) -> Self::Output {
        record_binary_op(self, other, BinaryOpType::Div)
    }
}

impl<T: Clone + Send + Sync + std::fmt::Debug + 'static> std::fmt::Debug for LVec<T> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        if self.is_pending() {
            write!(f, "LVec<pending>")
        } else {
            write!(f, "LVec({:?})", self.as_slice())
        }
    }
}
