//! Processor abstraction: typed units of work that run over the Universe.
//!
//! Each processor declares which component types it reads and writes.
//! The scheduler uses these declarations for dependency analysis,
//! topological ordering, and parallel execution.
//!
//! The `#[derive(Processor)]` macro generates per-processor view structs and
//! run traits that enforce component access at compile time. See the macro
//! documentation in `latticeon-macros` for details.

use crate::ecs::commands::Commands;
use crate::ecs::component::{Component, ComponentId, ComponentIdRegistry};
use crate::ecs::universe::Universe;

// ---------------------------------------------------------------------------
// Access marker traits (used by the derive macro for dependency declarations)
// ---------------------------------------------------------------------------

/// Marker: processor `Self` declares read access to component `T`.
pub trait AllowRead<T: Component> {}

/// Marker: processor `Self` declares write access to component `T`.
/// Write implies read -- the derive macro also generates `AllowRead<T>`.
pub trait AllowWrite<T: Component> {}

// ---------------------------------------------------------------------------
// Processor trait (type-erased, used by scheduler / dyn dispatch)
// ---------------------------------------------------------------------------

/// A processor is a unit of ECS logic that declares its component dependencies
/// and executes over the universe.
///
/// Typically you implement this via `#[derive(Processor)]` which auto-generates
/// `reads`, `writes`, access marker traits, a per-processor view struct, and a
/// per-processor run trait. The generated `Processor::run` iterates matching
/// entities (for query processors) or runs once (for plain processors).
pub trait Processor: Send + Sync + 'static {
    /// Component IDs this processor reads (immutable access).
    fn reads(&self, registry: &ComponentIdRegistry) -> Vec<ComponentId>;

    /// Component IDs this processor writes (mutable access).
    fn writes(&self, registry: &ComponentIdRegistry) -> Vec<ComponentId>;

    /// Execute the processor logic. Called by the scheduler with an untyped context.
    fn run(&mut self, ctx: &mut ProcessorContext);

    /// Short type name of this processor, used for dependency matching.
    fn processor_name(&self) -> &'static str { "" }

    /// Processor names that must execute **before** this one (i.e. this runs after them).
    fn execute_after(&self) -> Vec<&'static str> { vec![] }

    /// Processor names that must execute **after** this one (i.e. this runs before them).
    fn execute_before(&self) -> Vec<&'static str> { vec![] }

    /// Optional tag for this processor (e.g. for categorization or debugging).
    /// Does not affect execution order or barriers; use groups for that.
    fn processor_tag(&self) -> Option<&'static str> { None }
}

// ---------------------------------------------------------------------------
// ProcessorContext (type-erased, for dyn Processor / scheduler)
// ---------------------------------------------------------------------------

/// Untyped runtime context used by the scheduler and `dyn Processor`.
pub struct ProcessorContext<'a> {
    universe: &'a mut Universe,
    commands: &'a Commands,
}

impl<'a> ProcessorContext<'a> {
    pub fn new(universe: &'a mut Universe, commands: &'a Commands) -> Self {
        Self { universe, commands }
    }

    /// Access the deferred command buffer (always allowed).
    pub fn commands(&self) -> &Commands {
        self.commands
    }

    /// Direct universe access (untyped -- only for scheduler internals / macro-generated code).
    pub fn universe(&self) -> &Universe {
        self.universe
    }

    /// Direct mutable universe access (untyped -- only for scheduler internals / macro-generated code).
    pub fn universe_mut(&mut self) -> &mut Universe {
        self.universe
    }
}

// ---------------------------------------------------------------------------
// Convenience: run processors sequentially (for simple use-cases)
// ---------------------------------------------------------------------------

/// Run a slice of processors sequentially. For parallel execution, use [`Schedule`](crate::ecs::schedule::Schedule).
pub fn run_processors(processors: &mut [Box<dyn Processor>], universe: &mut Universe) {
    let commands = Commands::new();
    for proc in processors.iter_mut() {
        let mut ctx = ProcessorContext::new(universe, &commands);
        proc.run(&mut ctx);
    }
    commands.apply(universe);
}
