use bevy::prelude::*;

#[macro_export]
macro_rules! global_observer {
    ($app:expr, $($system:tt)*) => {{
        $app.world_mut()
            .add_observer($($system)*)
            .insert(global_observer!(@name $($system)*))
    }};

    (@name $system:ident ::< $($param:ident),+ $(,)? >) => {{
        let mut name = String::new();
        name.push_str(stringify!($system));
        name.push_str("::<");
        $(
            name.push_str(std::any::type_name::<$param>());
        )+
        name.push_str(">");
        Name::new(name)
    }};

    (@name $system:expr) => {
        Name::new(stringify!($system))
    };
}

pub use global_observer;

pub fn plugin(app: &mut App) {
    app.add_systems(Update, global_observers);
}

fn global_observers(
    mut cmds: Commands,
    query: Query<Entity, (With<Observer>, Without<Children>, Added<Observer>)>,
    mut root: Local<Option<Entity>>,
) {
    if root.is_none() {
        let new_root = cmds.spawn(Name::new("Observers")).id();
        *root = Some(new_root);
    }

    let Some(root) = *root else {
        return;
    };

    for o in query.iter() {
        cmds.entity(o).try_insert(ChildOf(root));
    }
}
