use std::{collections::BTreeSet, sync::Arc};
use gled_model::asset::{AssetTrait, output_device::routing::OutputRouting};
use gled_protocol::assets::{AssetIdVisitor, AssetVisitor};
use tracing::debug;
use super::{ClientId, ClientInput, Engine, StorageCommand, session};
use crate::{
input::Input,
pipeline::output_sender::HOVERED_OUTPUT_ROUTING,
storage::{
action::StorageAction,
asset::Asset,
asset_id::AssetId,
collections::Collections,
store::{AssetStore, delete_asset},
},
};
impl Engine {
pub(super) fn receive_client_inputs(&mut self) -> bool {
loop {
match self.inputs.try_recv() {
Ok(Some(session::EngineInput::Connect { id, state, events })) => {
debug!(id, "client connected");
self.sessions.insert(
id,
session::Session {
state,
events,
applied_sequence: 0,
keys: BTreeSet::new(),
},
);
}
Ok(Some(session::EngineInput::Client { id, input })) => {
self.apply_client_input(id, input)
}
Ok(Some(session::EngineInput::Disconnect(id))) => self.disconnect(id),
Ok(None) => return true,
Err(_) => return false,
}
}
}
fn apply_client_input(&mut self, id: ClientId, input: ClientInput) {
match input {
ClientInput::Action { sequence, action } => {
if let Some(session) = self.sessions.get_mut(&id) {
session.applied_sequence = session.applied_sequence.max(sequence);
}
self.apply_action(action);
}
ClientInput::Keys(keys) => {
debug!(id, ?keys, "keys held in the interface");
if let Some(session) = self.sessions.get_mut(&id) {
session.keys = keys;
}
self.update_keys();
}
ClientInput::SetBeatsPerMinute(bpm) => self.timing.set_beats_per_minute(bpm),
ClientInput::SaveAsset(asset) => asset.accept(&mut SaveInto(&mut self.collections)),
ClientInput::DeleteAsset(id) => id.accept(&mut DeleteFrom(&mut self.collections)),
ClientInput::Storage(command) => match command {
StorageCommand::Commit { message } => StorageAction::Commit { message }.enqueue(),
StorageCommand::Pull => StorageAction::Pull.enqueue(),
StorageCommand::Push => StorageAction::Push.enqueue(),
StorageCommand::SwitchBranch(branch) => {
StorageAction::SwitchBranch(branch).enqueue()
}
StorageCommand::Reclone => StorageAction::Nuke.enqueue(),
StorageCommand::SetGitPassphrase(passphrase) => self
.persistent_state
.git_credentials_mut()
.set_passphrase(passphrase),
},
ClientInput::SetServerSettings(settings) => self.apply_server_settings(settings),
ClientInput::WatchDevices(watch) => self.device_scan.watch(id, watch),
ClientInput::WatchMidi(watch) => self.midi.watch(id, watch),
ClientInput::MidiLearning(learning) => self.midi.learning(id, learning),
ClientInput::MidiTest(command) => self.midi.test(command),
ClientInput::WatchSoundTriggers(triggers) => {
self.watched_triggers.insert(id, triggers);
}
ClientInput::HoverOutputRouting(routing) => {
let owned = self
.hovered_routing
.as_ref()
.is_some_and(|(owner, _)| *owner == id);
match routing {
Some(routing) => self.set_hovered_routing(Some((id, routing))),
None if owned => self.set_hovered_routing(None),
None => {}
}
}
}
}
pub(super) fn disconnect(&mut self, id: ClientId) {
if self.sessions.remove(&id).is_some() {
debug!(id, "client disconnected");
self.update_keys();
self.device_scan.disconnect(id);
self.midi.disconnect(id);
self.watched_triggers.remove(&id);
if self
.hovered_routing
.as_ref()
.is_some_and(|(owner, _)| *owner == id)
{
self.set_hovered_routing(None);
}
}
}
pub(super) fn set_hovered_routing(&mut self, hovered: Option<(ClientId, OutputRouting)>) {
*HOVERED_OUTPUT_ROUTING.lock() = hovered.as_ref().map(|(_, routing)| routing.clone());
self.hovered_routing = hovered;
}
fn update_keys(&self) {
let keys = self
.sessions
.values()
.flat_map(|session| session.keys.iter().copied())
.collect();
Input::set_keys(keys);
}
}
struct SaveInto<'a>(&'a mut Collections);
impl AssetVisitor for SaveInto<'_> {
fn asset<T: AssetTrait>(&mut self, asset: Arc<Asset<T>>) {
Arc::unwrap_or_clone(asset).save(self.0);
}
}
struct DeleteFrom<'a>(&'a mut Collections);
impl AssetIdVisitor for DeleteFrom<'_> {
fn id<T: AssetTrait>(&mut self, id: AssetId<T>) {
delete_asset(id, self.0);
}
}