use std::collections::{BTreeMap, BTreeSet};
use sva_formula::NodeId;
use super::{SumSlot, Typing};
use crate::arguments::Arguments;
use crate::instantiate::Instances;
use crate::time::Grid;
#[derive(Debug, PartialEq)]
pub(super) enum Entry {
Node(NodeId, (String, Grid)),
Origin(u32, (String, Grid)),
Path(String, Option<NodeId>),
Copy((String, Grid), Option<NodeId>),
Pending(NodeId),
Noted(String, Option<Arguments>),
}
#[derive(Debug, Default, PartialEq)]
pub(super) struct Draft {
pub(super) hidden: BTreeSet<String>,
pub(super) by_path: BTreeMap<String, NodeId>,
pub(super) copies: BTreeMap<(String, Grid), NodeId>,
pub(super) arguments: BTreeMap<String, Arguments>,
pub(super) sum: Option<Option<(NodeId, Vec<SumSlot>)>>,
pub(super) journal: Vec<Entry>,
}
#[derive(Debug, Default, PartialEq)]
pub(super) struct Units {
file: String,
grids: Vec<(Grid, Unit)>,
}
#[derive(Debug, Default, PartialEq)]
struct Unit {
nodes: Vec<NodeId>,
origins: Vec<u32>,
}
pub(crate) struct Mark {
journal: usize,
lowered: usize,
}
impl Typing {
pub(crate) fn hide(&mut self, paths: impl IntoIterator<Item = String>) {
self.draft.hidden.extend(paths);
}
pub(crate) fn checkpoint(&self) -> Mark {
Mark {
journal: self.draft.journal.len(),
lowered: self.lowered.len(),
}
}
pub(crate) fn rollback(&mut self, mark: Mark) {
while self.draft.journal.len() > mark.journal {
match self.draft.journal.pop().expect("an entry past the mark") {
Entry::Node(id, _) => {
self.nodes[id.0 as usize] = None;
self.free.push(id.0);
self.pending.remove(&id);
}
Entry::Origin(token, _) => self.free_origin(token),
Entry::Path(path, old) => restore(&mut self.draft.by_path, path, old),
Entry::Copy(key, old) => restore(&mut self.draft.copies, key, old),
Entry::Pending(id) => {
self.pending.remove(&id);
}
Entry::Noted(path, old) => restore(&mut self.draft.arguments, path, old),
}
}
self.lowered.truncate(mark.lowered);
self.numbers.0.get_mut().clear();
}
pub(crate) fn abort(&mut self) -> Vec<NodeId> {
let made = self.draft.journal.iter().filter_map(|entry| match entry {
Entry::Node(id, _) => Some(*id),
_ => None,
});
let freed = made.collect();
self.rollback(Mark {
journal: 0,
lowered: 0,
});
self.draft = Draft::default();
freed
}
pub(crate) fn commit(&mut self, inst: &Instances) -> Vec<NodeId> {
let draft = std::mem::take(&mut self.draft);
let mut touched = BTreeSet::new();
let mut freed = Vec::new();
for path in &draft.hidden {
if let Some(units) = self.units.remove(path) {
for (_, unit) in units.grids {
for id in unit.nodes {
self.nodes[id.0 as usize] = None;
self.free.push(id.0);
freed.push(id);
}
for token in unit.origins {
self.free_origin(token);
}
}
touched.insert(units.file);
}
self.by_path.remove(path);
self.copies.remove(path);
self.arguments.remove(path);
}
for entry in draft.journal {
let (unit, (path, grid)) = match entry {
Entry::Node(id, at) => ((Some(id), None), at),
Entry::Origin(token, at) => ((None, Some(token)), at),
_ => continue,
};
let units = self.units.entry(path.clone()).or_insert_with(|| Units {
file: inst.origin(&path).unwrap_or(&path).to_string(),
grids: Vec::new(),
});
touched.insert(units.file.clone());
let held = match units.grids.iter().position(|(g, _)| *g == grid) {
Some(at) => &mut units.grids[at].1,
None => {
units.grids.push((grid, Unit::default()));
&mut units.grids.last_mut().expect("just pushed").1
}
};
match unit {
(Some(id), _) => held.nodes.push(id),
(_, Some(token)) => held.origins.push(token),
_ => {}
}
}
self.by_path.extend(draft.by_path);
for ((path, grid), id) in draft.copies {
self.copies.entry(path).or_default().insert(grid, id);
}
self.arguments.extend(draft.arguments);
if let Some(sum) = draft.sum {
self.sum = sum;
}
for file in touched {
self.name_file(inst, &file);
}
self.numbers.0.get_mut().clear();
freed
}
fn name_file(&mut self, inst: &Instances, file: &str) {
if self.aliases.remove(file) {
self.by_path.remove(file);
}
let held: Vec<NodeId> = inst
.instances_of(file)
.filter_map(|p| self.by_path.get(&p).copied())
.collect();
if let ([only], None) = (held.as_slice(), self.by_path.get(file)) {
self.by_path.insert(file.to_string(), *only);
self.aliases.insert(file.to_string());
}
match held.is_empty() {
true => self.files.remove(file),
false => self.files.insert(file.to_string(), held),
};
}
fn free_origin(&mut self, token: u32) {
let held = self.origins[token as usize].take();
let (site, _) = held.expect("an origin marked once");
self.free_origins.push(token);
let held = self.sites[site as usize]
.as_mut()
.expect("a site an origin marks");
held.1 -= 1;
if held.1 == 0 {
let (name, _) = self.sites[site as usize].take().expect("a site");
self.site_ids.remove(&name);
self.free_sites.push(site);
}
}
}
fn restore<K: Ord, V>(map: &mut BTreeMap<K, V>, key: K, old: Option<V>) {
match old {
Some(old) => {
map.insert(key, old);
}
None => {
map.remove(&key);
}
}
}