use std::collections::HashMap;
use gled_model::asset::{
project::Project,
scene::{grid::GridLocation, instance::SceneInstance},
svg::Svg,
};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum ProjectUpdate {
Unchanged,
Replaced(Option<Box<Project>>),
Patched(ProjectPatch),
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ProjectPatch {
pub scenes: Vec<(GridLocation, Option<SceneInstance>)>,
pub svg: Option<Option<Svg>>,
pub rest: Option<Box<Project>>,
}
impl ProjectUpdate {
pub fn between(old: Option<&Project>, new: Option<&Project>) -> Self {
match (old, new) {
(None, None) => Self::Unchanged,
(_, None) => Self::Replaced(None),
(None, Some(new)) => Self::Replaced(Some(Box::new(new.clone()))),
(Some(old), Some(new)) if old == new => Self::Unchanged,
(Some(old), Some(new)) => Self::Patched(ProjectPatch::between(old, new)),
}
}
pub fn apply_to(self, project: &mut Option<Project>) {
match self {
Self::Unchanged => {}
Self::Replaced(new) => *project = new.map(|new| *new),
Self::Patched(patch) => {
if let Some(project) = project {
patch.apply_to(project);
}
}
}
}
}
impl ProjectPatch {
fn between(old: &Project, new: &Project) -> Self {
let mut scenes: Vec<_> = new
.scenes_instances_grid
.iter()
.filter(|(location, scene)| old.scenes_instances_grid.get(location) != Some(scene))
.map(|(location, scene)| (*location, Some(scene.clone())))
.collect();
scenes.extend(
old.scenes_instances_grid
.keys()
.filter(|location| !new.scenes_instances_grid.contains_key(location))
.map(|location| (*location, None)),
);
let svg = (old.svg != new.svg).then(|| new.svg.clone());
let new_rest = rest(new);
let rest = (rest(old) != new_rest).then(|| Box::new(new_rest));
Self { scenes, svg, rest }
}
fn apply_to(self, project: &mut Project) {
if let Some(rest) = self.rest {
let scenes = std::mem::take(&mut project.scenes_instances_grid);
let svg = project.svg.take();
*project = *rest;
project.scenes_instances_grid = scenes;
project.svg = svg;
}
if let Some(svg) = self.svg {
project.svg = svg;
}
for (location, scene) in self.scenes {
match scene {
Some(scene) => {
project.scenes_instances_grid.insert(location, scene);
}
None => {
project.scenes_instances_grid.remove(&location);
}
}
}
}
}
fn rest(project: &Project) -> Project {
let Project {
palette,
groups,
grid_width,
grid_height,
grid_highlight,
scenes_instances_grid: _,
svg: _,
channel_overwrites,
output_routings,
artnet_config,
tap_input_events,
blackout_input_events,
blackout_hold_input_events,
half_input_events,
double_input_events,
midi_active_mappings,
main_dimmer,
audio_input_device,
artnet_control_config,
osc_config,
} = project;
Project {
palette: palette.clone(),
groups: groups.clone(),
grid_width: *grid_width,
grid_height: *grid_height,
grid_highlight: *grid_highlight,
channel_overwrites: channel_overwrites.clone(),
output_routings: output_routings.clone(),
artnet_config: artnet_config.clone(),
tap_input_events: tap_input_events.clone(),
blackout_input_events: blackout_input_events.clone(),
blackout_hold_input_events: blackout_hold_input_events.clone(),
half_input_events: half_input_events.clone(),
double_input_events: double_input_events.clone(),
midi_active_mappings: midi_active_mappings.clone(),
main_dimmer: *main_dimmer,
audio_input_device: audio_input_device.clone(),
artnet_control_config: *artnet_control_config,
osc_config: *osc_config,
scenes_instances_grid: HashMap::new(),
svg: None,
}
}