//! Synchronization primitives for transfer tracking.

use std::sync::atomic::{AtomicU64, AtomicBool, Ordering};
use std::sync::Arc;

/// Status of a transfer fence.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FenceStatus {
    /// Transfer is still pending.
    Pending,
    /// Transfer is complete.
    Complete,
    /// Transfer failed.
    Failed,
}

/// A fence for tracking GPU transfer completion.
#[derive(Clone)]
pub struct TransferFence {
    id: u64,
    complete: Arc<AtomicBool>,
    failed: Arc<AtomicBool>,
}

impl TransferFence {
    /// Create a new pending fence.
    pub(crate) fn new(id: u64) -> Self {
        Self {
            id,
            complete: Arc::new(AtomicBool::new(false)),
            failed: Arc::new(AtomicBool::new(false)),
        }
    }
    
    /// Get the fence ID.
    pub fn id(&self) -> u64 {
        self.id
    }
    
    /// Check the fence status.
    pub fn status(&self) -> FenceStatus {
        if self.failed.load(Ordering::Acquire) {
            FenceStatus::Failed
        } else if self.complete.load(Ordering::Acquire) {
            FenceStatus::Complete
        } else {
            FenceStatus::Pending
        }
    }
    
    /// Check if the transfer is complete (not failed).
    pub fn is_complete(&self) -> bool {
        self.complete.load(Ordering::Acquire) && !self.failed.load(Ordering::Acquire)
    }
    
    /// Check if the transfer is still pending.
    pub fn is_pending(&self) -> bool {
        !self.complete.load(Ordering::Acquire) && !self.failed.load(Ordering::Acquire)
    }
    
    /// Mark as complete.
    pub(crate) fn mark_complete(&self) {
        self.complete.store(true, Ordering::Release);
    }
    
    /// Mark as failed.
    pub(crate) fn mark_failed(&self) {
        self.failed.store(true, Ordering::Release);
        self.complete.store(true, Ordering::Release);
    }
}

impl std::fmt::Debug for TransferFence {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("TransferFence")
            .field("id", &self.id)
            .field("status", &self.status())
            .finish()
    }
}

/// Generator for unique fence IDs.
pub(crate) struct FenceIdGenerator {
    next_id: AtomicU64,
}

impl FenceIdGenerator {
    pub fn new() -> Self {
        Self {
            next_id: AtomicU64::new(1),
        }
    }
    
    pub fn next(&self) -> u64 {
        self.next_id.fetch_add(1, Ordering::Relaxed)
    }
}

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

#[cfg(test)]
mod tests {
    use super::*;
    
    #[test]
    fn test_fence_lifecycle() {
        let fence = TransferFence::new(1);
        assert!(fence.is_pending());
        assert_eq!(fence.status(), FenceStatus::Pending);
        
        fence.mark_complete();
        assert!(fence.is_complete());
        assert_eq!(fence.status(), FenceStatus::Complete);
    }
    
    #[test]
    fn test_fence_failure() {
        let fence = TransferFence::new(2);
        fence.mark_failed();
        assert_eq!(fence.status(), FenceStatus::Failed);
        assert!(!fence.is_complete());
    }
}
