//! `scene_basic` — a runnable tour of the Stage 3 scene graph.
//!
//! Run with:
//! ```sh
//! cargo run -p oxide-examples --bin scene_basic
//! ```
//!
//! This example has no window or GPU dependency. It builds a small entity
//! hierarchy, prints each node's local and resolved world transform, reparents
//! a node, and round-trips the whole scene through RON — so the Stage 3 scene
//! API can be reviewed by eye.

use oxide_engine::prelude::*;
use oxide_engine::scene::DespawnPolicy;

fn main() {
    env_logger::init();
    println!("Oxide scene demo — Stage 3 scene graph\n");

    let mut scene = Scene::new();

    // A little solar-system-ish hierarchy: sun → planet → moon, plus a probe.
    let sun = scene.spawn("sun", Transform::from_translation(Vec3::new(0.0, 0.0, 0.0)));
    let planet = scene.spawn_child(
        sun,
        "planet",
        Transform::from_trs(
            Vec3::new(10.0, 0.0, 0.0),
            Quat::from_rotation_y(90_f32.to_radians()),
            Vec3::ONE,
        ),
    );
    let moon = scene.spawn_child(
        planet,
        "moon",
        Transform::from_translation(Vec3::new(0.0, 0.0, 2.0)),
    );
    let probe = scene.spawn_child(planet, "probe", Transform::from_translation(Vec3::Y));

    println!("== Hierarchy & world transforms ==");
    print_tree(&scene);

    // World transforms compose down the chain: the moon inherits the planet's
    // rotation, so its local +Z offset lands along the world axes accordingly.
    let moon_world = scene.world_transform(moon).unwrap();
    println!(
        "\nmoon local pos : {}",
        scene.local_transform(moon).unwrap().translation
    );
    println!("moon world pos : {}", moon_world.translation);

    // Reparent the probe directly under the sun and observe its world transform
    // change (its local transform is preserved).
    println!("\n== Reparent probe: planet → sun ==");
    scene.set_parent(probe, Some(sun)).unwrap();
    println!(
        "probe world pos: {}",
        scene.world_transform(probe).unwrap().translation
    );

    // Disable a node (later systems will skip disabled subtrees).
    scene.set_enabled(moon, false);
    println!(
        "\nmoon enabled?  : {}",
        scene.is_enabled(moon).unwrap_or(true)
    );

    // Serialize → deserialize round-trip.
    println!("\n== RON serialization ==");
    let ron = scene.to_ron().expect("serialize");
    println!("{ron}");
    let restored = Scene::from_ron(&ron).expect("deserialize");
    println!(
        "restored {} entities, {} roots",
        restored.len(),
        restored.roots().len()
    );

    // Despawn the planet, detaching its children up to its parent.
    println!("\n== Despawn planet (detach children) ==");
    scene.despawn(planet, DespawnPolicy::DetachChildren);
    print_tree(&scene);
}

/// Prints the scene as an indented tree with each node's world position.
fn print_tree(scene: &Scene) {
    let worlds = scene.world_transforms();
    for &root in scene.roots() {
        print_node(scene, root, 0, &worlds);
    }
}

fn print_node(
    scene: &Scene,
    entity: oxide_engine::scene::Entity,
    depth: usize,
    worlds: &std::collections::HashMap<oxide_engine::scene::Entity, Transform>,
) {
    let indent = "  ".repeat(depth);
    let name = scene.name(entity).unwrap_or_default();
    let enabled = scene.is_enabled(entity).unwrap_or(true);
    let world = worlds
        .get(&entity)
        .map(|t| t.translation)
        .unwrap_or(Vec3::ZERO);
    let tag = if enabled { "" } else { " (disabled)" };
    println!("{indent}- {name}{tag}  world={world}");
    for &child in scene.children(entity) {
        print_node(scene, child, depth + 1, worlds);
    }
}
