use bevy::platform::collections::HashSet;
use bevy::prelude::*;
use bevy_gearbox::prelude::*;
use bevy_gearbox::transitions::{Source, Target, TransitionListener};
use crate::editor_state::{EditorState, StateMachinePersistentData};
use crate::components::{NodeType, LeafNode};
use crate::editor_state::SetInitialStateRequested;
#[derive(Component)]
pub struct NodeKindRoot;
#[derive(Component, Copy, Clone)]
pub struct NodeKindFor(pub Entity);
#[derive(Component)]
pub struct NodeKindLeaf;
#[derive(Component)]
pub struct NodeKindParent;
#[derive(Component)]
pub struct NodeKindParallel;
#[derive(Event)]
pub struct AddChildClicked;
#[derive(Event)]
pub struct ChildAdded;
#[derive(Event)]
pub struct AllChildrenRemoved;
#[derive(Event)]
pub struct MakeParallelClicked;
#[derive(Event)]
pub struct MakeParentClicked;
#[derive(Event)]
pub struct MakeLeafClicked;
pub fn sync_node_kind_machines(
editor_state: Res<EditorState>,
mut commands: Commands,
mut state_machines: Query<(&StateMachinePersistentData, &mut crate::editor_state::StateMachineTransientData), With<StateMachineRoot>>,
) {
let Some(root) = editor_state.selected_machine else { return; };
let Ok((persistent, mut transient)) = state_machines.get_mut(root) else { return; };
for (&state_entity, _node) in persistent.nodes.iter() {
if transient.node_kind_roots.contains_key(&state_entity) { continue; }
let leaf = commands.spawn((Name::new("NodeKind::Leaf"), NodeKindLeaf)).id();
let parent = commands.spawn((Name::new("NodeKind::Parent"), NodeKindParent)).id();
let parallel = commands.spawn((Name::new("NodeKind::Parallel"), NodeKindParallel)).id();
let root_entity = commands
.spawn((
Name::new("NodeKind"),
NodeKindRoot,
NodeKindFor(state_entity),
InitialState(leaf),
CurrentState(HashSet::new()),
StateMachineRoot,
))
.id();
commands.entity(leaf).insert((NodeKindFor(state_entity), bevy_gearbox::StateChildOf(root_entity)));
commands.entity(parent).insert((NodeKindFor(state_entity), bevy_gearbox::StateChildOf(root_entity)));
commands.entity(parallel).insert((NodeKindFor(state_entity), bevy_gearbox::StateChildOf(root_entity)));
commands.spawn((Source(leaf), Target(parent), TransitionListener::<AddChildClicked>::default(), NodeKindFor(state_entity)));
commands.spawn((Source(leaf), Target(parent), TransitionListener::<ChildAdded>::default(), NodeKindFor(state_entity)));
commands.spawn((Source(parent), Target(parallel), TransitionListener::<MakeParallelClicked>::default(), NodeKindFor(state_entity)));
commands.spawn((Source(leaf), Target(parallel), TransitionListener::<MakeParallelClicked>::default(), NodeKindFor(state_entity)));
commands.spawn((Source(leaf), Target(parent), TransitionListener::<MakeParentClicked>::default(), NodeKindFor(state_entity)));
commands.spawn((Source(parallel), Target(parent), TransitionListener::<MakeParentClicked>::default(), NodeKindFor(state_entity)));
commands.spawn((Source(parent), Target(leaf), TransitionListener::<AllChildrenRemoved>::default(), NodeKindFor(state_entity)));
commands.spawn((Source(parallel), Target(leaf), TransitionListener::<AllChildrenRemoved>::default(), NodeKindFor(state_entity)));
commands.spawn((Source(parent), Target(leaf), TransitionListener::<MakeLeafClicked>::default(), NodeKindFor(state_entity)));
commands.spawn((Source(parallel), Target(leaf), TransitionListener::<MakeLeafClicked>::default(), NodeKindFor(state_entity)));
transient.node_kind_roots.insert(state_entity, root_entity);
}
}
pub fn on_enter_nodekind_state_parallel(
trigger: Trigger<EnterState>,
nk_for_query: Query<&NodeKindFor, With<NodeKindParallel>>,
mut commands: Commands,
editor_state: Res<EditorState>,
) {
let nk_state = trigger.target();
let Ok(NodeKindFor(target_state_entity)) = nk_for_query.get(nk_state) else { return; };
let state = *target_state_entity;
commands.entity(state).insert(bevy_gearbox::Parallel);
commands.entity(state).remove::<bevy_gearbox::InitialState>();
let Some(root) = editor_state.selected_machine else { return; };
commands.queue(move |world: &mut World| {
let has_child = world
.get::<bevy_gearbox::StateChildren>(state)
.map(|c| c.into_iter().next().is_some())
.unwrap_or(false);
if has_child { return; }
let child = world.spawn((bevy_gearbox::StateChildOf(state), Name::new("New State"))).id();
if let Some(mut persistent) = world.get_mut::<StateMachinePersistentData>(root) {
if let Some(parent_node) = persistent.nodes.get(&state) {
let parent_pos = match parent_node {
NodeType::Leaf(leaf) => leaf.entity_node.position,
NodeType::Parent(parent) => parent.entity_node.position,
};
let pos = parent_pos + egui::Vec2::new(50.0, 50.0);
persistent.nodes.insert(child, NodeType::Leaf(LeafNode::new(pos)));
}
}
});
}
pub fn on_enter_nodekind_state_parent(
trigger: Trigger<EnterState>,
nk_for_query: Query<&NodeKindFor, With<NodeKindParent>>,
mut commands: Commands,
editor_state: Res<EditorState>,
) {
let nk_state = trigger.target();
let Ok(NodeKindFor(target_state_entity)) = nk_for_query.get(nk_state) else { return; };
let state = *target_state_entity;
commands.entity(state).remove::<bevy_gearbox::Parallel>();
let Some(root) = editor_state.selected_machine else { return; };
commands.queue(move |world: &mut World| {
let first_child: Option<Entity> = world
.get::<bevy_gearbox::StateChildren>(state)
.and_then(|children| children.into_iter().next().copied())
.or_else(|| {
let child = world.spawn((bevy_gearbox::StateChildOf(state), Name::new("New State"))).id();
let Some(mut persistent) = world.get_mut::<StateMachinePersistentData>(root) else { return None; };
let Some(parent_node) = persistent.nodes.get(&state) else { return None; };
let parent_pos = match parent_node {
NodeType::Leaf(leaf) => leaf.entity_node.position,
NodeType::Parent(parent) => parent.entity_node.position,
};
let pos = parent_pos + egui::Vec2::new(50.0, 50.0);
persistent.nodes.insert(child, NodeType::Leaf(LeafNode::new(pos)));
Some(child)
});
let Some(init) = first_child else { return; };
world.trigger(SetInitialStateRequested { child_entity: init });
});
}
pub fn on_enter_nodekind_state_leaf(
trigger: Trigger<EnterState>,
nk_for_query: Query<&NodeKindFor, With<NodeKindLeaf>>,
mut commands: Commands,
) {
let nk_state = trigger.target();
let Ok(NodeKindFor(target_state_entity)) = nk_for_query.get(nk_state) else { return; };
let state = *target_state_entity;
commands.entity(state).remove::<bevy_gearbox::Parallel>();
commands.entity(state).remove::<bevy_gearbox::InitialState>();
commands.entity(state).remove::<bevy_gearbox::StateChildren>();
}
pub fn on_enter_nodekind_state_parent_via_make_parent(
trigger: Trigger<EnterState>,
nk_for_query: Query<&NodeKindFor, With<NodeKindParent>>,
mut commands: Commands,
editor_state: Res<EditorState>,
) {
let nk_state = trigger.target();
let Ok(NodeKindFor(target_state_entity)) = nk_for_query.get(nk_state) else { return; };
let state = *target_state_entity;
let Some(root) = editor_state.selected_machine else { return; };
commands.queue(move |world: &mut World| {
let mut first_child: Option<Entity> = world
.get::<bevy_gearbox::StateChildren>(state)
.and_then(|children| children.into_iter().next().copied());
if first_child.is_none() {
let child = world.spawn((bevy_gearbox::StateChildOf(state), Name::new("New State"))).id();
first_child = Some(child);
let Some(mut persistent) = world.get_mut::<StateMachinePersistentData>(root) else { return; };
let Some(parent_node) = persistent.nodes.get(&state) else { return; };
let parent_pos = match parent_node {
NodeType::Leaf(leaf) => leaf.entity_node.position,
NodeType::Parent(parent) => parent.entity_node.position,
};
let pos = parent_pos + egui::Vec2::new(50.0, 50.0);
persistent.nodes.insert(child, NodeType::Leaf(LeafNode::new(pos)));
}
let Some(init) = first_child else { return; };
world.trigger(SetInitialStateRequested { child_entity: init });
});
}
pub fn on_remove_state_children(
trigger: Trigger<OnRemove, bevy_gearbox::StateChildren>,
editor_state: Res<EditorState>,
mut q: Query<&mut crate::editor_state::StateMachineTransientData, With<StateMachineRoot>>,
mut commands: Commands,
) {
let parent = trigger.target();
let Some(root) = editor_state.selected_machine else { return; };
let Ok(transient) = q.get_mut(root) else { return; };
let Some(&nk_root) = transient.node_kind_roots.get(&parent) else { return; };
commands.trigger_targets(AllChildrenRemoved, nk_root);
}