//! Minimal ECS demo via the engine crate.
//!
//! Run with: `cargo run -p latticeon --example ecs_minimal`

use std::marker::PhantomData;

use latticeon::ecs::prelude::*;

#[derive(Debug, Clone, Component)]
struct Position(i32, i32);

#[derive(Debug, Clone, Component)]
struct Velocity(i32, i32);

#[derive(Debug, Clone, Component)]
struct Health(i32);

// ---------------------------------------------------------------------------
// Query processor: movement (reads Velocity, writes Position)
// ---------------------------------------------------------------------------

#[derive(Default, Processor)]
struct MovementProcessor {
    #[read]  _vel: PhantomData<Velocity>,
    #[write] _pos: PhantomData<Position>,
}

impl MovementProcessorRun for MovementProcessor {
    fn run(&self, view: &mut MovementProcessorView<'_>, _commands: &Commands) {
        println!(
            "  move entity {:?}: ({},{}) += ({},{})",
            view.entity, view.pos.0, view.pos.1, view.vel.0, view.vel.1,
        );
        view.pos.0 += view.vel.0;
        view.pos.1 += view.vel.1;
    }
}

// ---------------------------------------------------------------------------
// Query processor: heal (writes Health, reads Position for filtering)
// Explicitly runs after MovementProcessor so heals see updated positions.
// ---------------------------------------------------------------------------

#[derive(Default, Processor)]
#[after(MovementProcessor)]
struct HealProcessor {
    #[read]  _pos: PhantomData<Position>,
    #[write] _hp: PhantomData<Health>,
}

impl HealProcessorRun for HealProcessor {
    fn run(&self, view: &mut HealProcessorView<'_>, _commands: &Commands) {
        view.hp.0 += 1;
        println!(
            "  heal entity {:?} at ({},{}): hp={}",
            view.entity, view.pos.0, view.pos.1, view.hp.0,
        );
    }
}

// ---------------------------------------------------------------------------
// Plain processor: spawn-via-commands (deferred spawn demonstrated)
// ---------------------------------------------------------------------------

#[derive(Default, Processor)]
struct LateSpawnProcessor;

impl LateSpawnProcessorRun for LateSpawnProcessor {
    fn run(&self, commands: &Commands) {
        commands.spawn((Position(99, 99), Velocity(0, 0)));
        println!("  queued deferred spawn");
    }
}

fn main() {
    let mut universe = Universe::new();

    // Spawn some entities
    universe.spawn((Position(0, 0), Velocity(1, 0)));
    universe.spawn((Position(10, 5), Velocity(-1, 1)));
    universe.spawn((Position(20, 30), Velocity(0, -1), Health(100)));

    // Build a schedule with two stages
    let mut schedule = Schedule::new();
    schedule.add_stage("PostUpdate");

    // HealProcessor declares #[after(MovementProcessor)] so movement always runs first,
    // even though their component writes are disjoint.
    schedule.add_processor_to_stage("Update", MovementProcessor::default());
    schedule.add_processor_to_stage("Update", HealProcessor::default());

    // LateSpawnProcessor: commands are applied after Update stage
    schedule.add_processor_to_stage("Update", LateSpawnProcessor::default());

    println!("=== Tick 1 ===");
    schedule.run(&mut universe);
    println!("Entity count after tick 1: {}\n", universe.entity_count());

    println!("=== Tick 2 ===");
    schedule.run(&mut universe);
    println!("Entity count after tick 2: {}", universe.entity_count());
}
