use bevy::prelude::*;
use crate::types::EntityId;
use super::model::store::EditorStore;
use super::model::types::{ConnectionState, IndexFilter};
use bevy_gearbox_protocol::client::{ClientCommand, NetCommand};
use crate::editor::workspace::Workspace;
use crate::editor::docs::Docs;
use rfd::FileDialog;
#[derive(Debug, Clone)]
pub struct EndpointConfig { pub endpoint: String }
pub fn connect(store: &mut EditorStore, endpoint: EndpointConfig) {
store.connection = ConnectionState::Connecting;
store.last_endpoint = Some(endpoint.endpoint.clone());
}
pub fn disconnect(store: &mut EditorStore) {
store.connection = ConnectionState::Disconnected;
store.clear_session();
}
pub fn reconnect(store: &mut EditorStore) {
let endpoint = store.last_endpoint.clone();
store.clear_session();
store.connection = ConnectionState::Disconnected;
if let Some(ep) = endpoint {
store.connection = ConnectionState::Connecting;
store.session_id = store.session_id.wrapping_add(1);
store.connection = ConnectionState::Connected { session_id: store.session_id, endpoint: ep };
}
}
pub fn refresh_index(store: &mut EditorStore, _filter: IndexFilter) {
store.index.is_loading = true;
store.index.is_loading = false;
}
#[allow(dead_code)]
pub fn open_machine(_store: &mut EditorStore, _entity: EntityId) {
}
#[derive(Debug, Clone, Event)]
pub struct ConnectRequested { pub endpoint: String }
#[derive(Debug, Clone, Event)]
pub struct DisconnectRequested;
#[derive(Debug, Clone, Event)]
pub struct ReconnectRequested;
#[derive(Debug, Clone, Event)]
pub struct RefreshIndexRequested { pub query: String }
#[derive(Debug, Clone, Event)]
pub struct OpenRequested { pub entity: EntityId }
pub fn on_connect_requested(evt: On<ConnectRequested>, mut store: ResMut<EditorStore>, mut proto_cmd: MessageWriter<ClientCommand>, _proto_net: MessageWriter<NetCommand>) {
connect(&mut store, EndpointConfig { endpoint: evt.endpoint.clone() });
proto_cmd.write(ClientCommand::SetUrl { url: evt.endpoint.clone() });
proto_cmd.write(ClientCommand::RefreshMachines);
}
pub fn on_disconnect_requested(_evt: On<DisconnectRequested>, mut store: ResMut<EditorStore>, mut proto_net: MessageWriter<NetCommand>) {
proto_net.write(NetCommand::StopDiscovery);
disconnect(&mut store);
}
pub fn on_reconnect_requested(_evt: On<ReconnectRequested>, mut store: ResMut<EditorStore>, mut proto_cmd: MessageWriter<ClientCommand>, mut proto_net: MessageWriter<NetCommand>) {
reconnect(&mut store);
if let Some(ep) = store.last_endpoint.clone() {
proto_cmd.write(ClientCommand::SetUrl { url: ep });
}
proto_net.write(NetCommand::StartDiscovery);
proto_cmd.write(ClientCommand::RefreshMachines);
}
pub fn on_refresh_index_requested(evt: On<RefreshIndexRequested>, mut store: ResMut<EditorStore>, mut proto_cmd: MessageWriter<ClientCommand>) {
refresh_index(&mut store, IndexFilter { query: evt.query.clone() });
proto_cmd.write(ClientCommand::RefreshMachines);
}
pub fn on_open_requested(
evt: On<OpenRequested>,
_store: ResMut<EditorStore>,
workspace: ResMut<Workspace>,
mut docs: ResMut<Docs>,
_commands: Commands,
mut proto_net: MessageWriter<NetCommand>,
mut proto_cmd: MessageWriter<ClientCommand>,
) {
let doc = docs.map.entry(evt.entity).or_default();
doc.transform.pan = workspace.board_transform.pan;
doc.transform.zoom = workspace.board_transform.zoom;
proto_net.write(NetCommand::StartMachine { id: evt.entity.0 });
proto_cmd.write(ClientCommand::FetchGraph { id: evt.entity.0 });
}
#[derive(Debug, Clone, Event)]
pub struct UnsubscribeRequested { pub entity: EntityId }
pub fn on_unsubscribe_requested(evt: On<UnsubscribeRequested>, mut proto_net: MessageWriter<NetCommand>) {
proto_net.write(NetCommand::StopComponents { id: evt.entity.0 });
proto_net.write(NetCommand::StopMachine { id: evt.entity.0 });
}
#[derive(Debug, Clone, Event)]
pub struct CloseRequested { pub entity: EntityId }
pub fn close_doc_and_unsubscribe(
entity: EntityId,
workspace: &mut Workspace,
docs: &mut Docs,
proto_net: &mut MessageWriter<NetCommand>,
)
{
if let Some(doc) = docs.map.get(&entity) {
if let Some(g) = &doc.graph {
for nid in g.nodes.keys() { proto_net.write(NetCommand::StopComponents { id: nid.0 }); }
for eid in g.edges.keys() { proto_net.write(NetCommand::StopComponents { id: eid.0 }); }
}
}
proto_net.write(NetCommand::StopComponents { id: entity.0 });
proto_net.write(NetCommand::StopMachine { id: entity.0 });
let _ = docs.map.remove(&entity);
if let Some((d, _)) = workspace.global_selection {
if d == entity { workspace.global_selection = None; }
}
}
pub fn on_close_requested(
evt: On<CloseRequested>,
mut workspace: ResMut<Workspace>,
mut docs: ResMut<Docs>,
mut proto_net: MessageWriter<NetCommand>,
) {
close_doc_and_unsubscribe(evt.entity, &mut workspace, &mut docs, &mut proto_net);
}
#[derive(Debug, Clone, Event)]
pub struct SaveAsRequested { pub doc: EntityId, pub target: EntityId }
pub fn on_save_as_requested(
save_as_requested: On<SaveAsRequested>,
_workspace: Res<Workspace>,
docs: Res<Docs>,
client: Res<bevy_gearbox_protocol::client::Client>,
rt: Res<bevy_gearbox_protocol::client::TokioRuntime>,
) {
let picked = FileDialog::new()
.add_filter("State Machine Sidecar", &["sm.ron"])
.set_title("Save State Machine")
.set_directory("assets")
.set_file_name("statemachine")
.save_file();
if let Some(path) = picked {
let fname = path.file_name().and_then(|s| s.to_str()).unwrap_or("");
let base = if fname.ends_with(".sm.ron") { &fname[..fname.len()-7] } else { fname };
let id_text = base.to_string();
let scn_path = format!("{}.scn.ron", id_text);
let sm_path = format!("{}.sm.ron", id_text);
let sidecar_text: Option<String> = docs
.map
.get(&save_as_requested.doc)
.map(|doc| {
let root = save_as_requested.target;
let sc = crate::persistence::extract_sidecar_for_subtree(doc, &root);
let pretty = ron::ser::PrettyConfig::new();
ron::ser::to_string_pretty(&sc, pretty).ok()
})
.flatten();
let entity_bits = save_as_requested.target.0;
let client_cloned = client.clone();
rt.0.spawn(async move {
if client_cloned.set_state_machine_id(entity_bits, &id_text).await.is_err() { return; }
if client_cloned.save_as(entity_bits, &scn_path).await.is_err() { return; }
if let Some(txt) = sidecar_text {
let _ = client_cloned.save_sidecar(&sm_path, &txt).await;
}
});
}
}
#[derive(Debug, Clone, Event)]
pub struct SaveSubstatesRequested { pub target: EntityId }
pub fn on_save_substates_requested(
req: On<SaveSubstatesRequested>,
client: Res<bevy_gearbox_protocol::client::Client>,
rt: Res<bevy_gearbox_protocol::client::TokioRuntime>,
) {
let id = req.target.0;
let client_cloned = client.clone();
rt.0.spawn(async move {
let _ = client_cloned.save_substates(id).await;
});
}
#[derive(Debug, Clone, Event)]
pub struct SetEdgeDelayRequested { pub target: EntityId, pub seconds: f32 }
#[derive(Debug, Clone, Event)]
pub struct ClearEdgeDelayRequested { pub target: EntityId }
#[derive(Debug, Clone, Event)]
pub struct SetEdgeKindRequested { pub target: EntityId, pub internal: bool }