//! Metal Backend for RingKernel
//!
//! This crate provides Apple Metal GPU support for RingKernel.
//! Supports macOS, iOS, and Apple Silicon.
//!
//! # Features
//!
//! - Event-driven kernel execution (Metal compute shaders)
//! - MSL (Metal Shading Language) support
//! - Apple Silicon optimization
//! - Unified memory architecture support
//!
//! # Limitations
//!
//! - No true persistent kernels (Metal doesn't support cooperative groups)
//! - macOS/iOS only
//!
//! # Example
//!
//! ```ignore
//! use ringkernel_metal::MetalRuntime;
//!
//! #[tokio::main]
//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
//!     let runtime = MetalRuntime::new().await?;
//!     let kernel = runtime.launch("compute", Default::default()).await?;
//!     kernel.activate().await?;
//!     Ok(())
//! }
//! ```

#![warn(missing_docs)]

#[cfg(all(target_os = "macos", feature = "metal"))]
mod device;
#[cfg(all(target_os = "macos", feature = "metal"))]
mod kernel;
#[cfg(all(target_os = "macos", feature = "metal"))]
mod memory;
#[cfg(all(target_os = "macos", feature = "metal"))]
mod runtime;

#[cfg(all(target_os = "macos", feature = "metal"))]
pub use device::MetalDevice;
#[cfg(all(target_os = "macos", feature = "metal"))]
pub use kernel::MetalKernel;
#[cfg(all(target_os = "macos", feature = "metal"))]
pub use memory::MetalBuffer;
#[cfg(all(target_os = "macos", feature = "metal"))]
pub use runtime::MetalRuntime;

// Stub implementation when Metal is not available
#[cfg(not(all(target_os = "macos", feature = "metal")))]
mod stub {
    use async_trait::async_trait;
    use ringkernel_core::error::{Result, RingKernelError};
    use ringkernel_core::runtime::{
        Backend, KernelHandle, KernelId, LaunchOptions, RingKernelRuntime, RuntimeMetrics,
    };

    /// Stub Metal runtime when not on macOS or Metal feature disabled.
    pub struct MetalRuntime;

    impl MetalRuntime {
        /// Create fails when Metal is not available.
        pub async fn new() -> Result<Self> {
            Err(RingKernelError::BackendUnavailable(
                "Metal not available (requires macOS with metal feature)".to_string(),
            ))
        }
    }

    #[async_trait]
    impl RingKernelRuntime for MetalRuntime {
        fn backend(&self) -> Backend {
            Backend::Metal
        }

        fn is_backend_available(&self, _backend: Backend) -> bool {
            false
        }

        async fn launch(&self, _kernel_id: &str, _options: LaunchOptions) -> Result<KernelHandle> {
            Err(RingKernelError::BackendUnavailable("Metal".to_string()))
        }

        fn get_kernel(&self, _kernel_id: &KernelId) -> Option<KernelHandle> {
            None
        }

        fn list_kernels(&self) -> Vec<KernelId> {
            vec![]
        }

        fn metrics(&self) -> RuntimeMetrics {
            RuntimeMetrics::default()
        }

        async fn shutdown(&self) -> Result<()> {
            Ok(())
        }
    }
}

#[cfg(not(all(target_os = "macos", feature = "metal")))]
pub use stub::MetalRuntime;

/// Check if Metal is available at runtime.
pub fn is_metal_available() -> bool {
    #[cfg(all(target_os = "macos", feature = "metal"))]
    {
        metal::Device::system_default().is_some()
    }
    #[cfg(not(all(target_os = "macos", feature = "metal")))]
    {
        false
    }
}

/// MSL (Metal Shading Language) kernel template.
pub const RING_KERNEL_MSL_TEMPLATE: &str = r#"
//
// RingKernel Metal Shading Language Template
// Generated by ringkernel-metal
//

#include <metal_stdlib>
using namespace metal;

// Control block structure (128 bytes)
struct ControlBlock {
    atomic_uint is_active;
    atomic_uint should_terminate;
    atomic_uint has_terminated;
    uint _pad1;

    atomic_ulong messages_processed;
    atomic_ulong messages_in_flight;

    atomic_ulong input_head;
    atomic_ulong input_tail;
    atomic_ulong output_head;
    atomic_ulong output_tail;

    uint input_capacity;
    uint output_capacity;
    uint input_mask;
    uint output_mask;

    // HLC state
    atomic_ulong hlc_physical;
    atomic_ulong hlc_logical;

    atomic_uint last_error;
    atomic_uint error_count;

    uchar _reserved[16];
};

// Message header structure (256 bytes)
struct MessageHeader {
    ulong magic;
    uint version;
    uint flags;
    ulong message_id;
    ulong correlation_id;
    ulong source_kernel;
    ulong dest_kernel;
    ulong message_type;
    uchar priority;
    uchar _reserved1[7];
    ulong payload_size;
    uint checksum;
    uint _reserved2;
    // HLC timestamp (24 bytes)
    ulong ts_physical;
    ulong ts_logical;
    ulong ts_node_id;
    // Deadline
    ulong deadline_physical;
    ulong deadline_logical;
    ulong deadline_node_id;
    uchar _reserved3[104];
};

// Kernel entry point
kernel void ring_kernel_main(
    device ControlBlock* control [[buffer(0)]],
    device uchar* input_queue [[buffer(1)]],
    device uchar* output_queue [[buffer(2)]],
    device uchar* shared_state [[buffer(3)]],
    uint thread_id [[thread_position_in_threadgroup]],
    uint threadgroup_id [[threadgroup_position_in_grid]],
    uint threads_per_group [[threads_per_threadgroup]]
) {
    // Check if kernel should process
    uint is_active = atomic_load_explicit(&control->is_active, memory_order_acquire);
    if (is_active == 0) {
        return;
    }

    // Check termination
    uint should_term = atomic_load_explicit(&control->should_terminate, memory_order_acquire);
    if (should_term != 0) {
        if (thread_id == 0 && threadgroup_id == 0) {
            atomic_store_explicit(&control->has_terminated, 1, memory_order_release);
        }
        return;
    }

    // User kernel code will be inserted here
    // USER_KERNEL_CODE

    // Update message counter
    if (thread_id == 0 && threadgroup_id == 0) {
        atomic_fetch_add_explicit(&control->messages_processed, 1, memory_order_relaxed);
    }
}
"#;
