use nmp_core::substrate::{EventId, KernelEvent};
use crate::block::TimelineBlock;
use crate::resolver::ParentResolver;
use super::{GroupDelta, Grouper};
impl<R: ParentResolver> Grouper<R> {
#[must_use]
pub fn on_insert(&mut self, event: &KernelEvent) -> Option<GroupDelta> {
if self.by_id.contains_key(&event.id) {
return None;
}
if self
.superseded_by
.get(&event.id)
.is_some_and(|set| !set.is_empty())
{
self.by_id.insert(event.id.clone(), event.clone());
return None;
}
self.by_id.insert(event.id.clone(), event.clone());
if let Some(target) = self.resolver.supersedes(event) {
self.superseded_by
.entry(target.clone())
.or_default()
.insert(event.id.clone());
self.unplace_standalone(&target);
}
let waiting = self.orphans.remove(&event.id).unwrap_or_default();
let mut delta = self.place_event(event);
let mut replay_queue: Vec<EventId> = waiting.into_iter().collect();
while let Some(child_id) = replay_queue.pop() {
if self.seen.contains(&child_id) {
continue;
}
let Some(child) = self.by_id.get(&child_id).cloned() else {
continue;
};
self.place_event(&child);
if let Some(more) = self.orphans.remove(&child_id) {
replay_queue.extend(more);
}
}
self.sort_blocks_newest_first();
self.collapse_adjacent();
self.sort_blocks_newest_first();
if matches!(
delta,
Some(GroupDelta::BlockInserted(_) | GroupDelta::BlockReplaced(_))
) {
delta = delta.and_then(|d| self.reindex_delta(d, &event.id));
}
delta
}
#[must_use]
pub fn on_remove(&mut self, id: &EventId) -> Option<GroupDelta> {
self.by_id.remove(id);
self.pending_ancestor_ids.remove(id);
self.orphaned.remove(id);
self.orphans.retain(|_, set| {
set.remove(id);
!set.is_empty()
});
self.superseded_by.remove(id);
let mut restore_candidates: Vec<EventId> = Vec::new();
self.superseded_by.retain(|target, set| {
set.remove(id);
if set.is_empty() {
restore_candidates.push(target.clone());
false
} else {
true
}
});
let block_delta = self.remove_id_from_blocks(id);
let mut restored_any = false;
for target in restore_candidates {
if self.seen.contains(&target) {
continue;
}
let Some(event) = self.by_id.get(&target).cloned() else {
continue;
};
let _ = self.place_event(&event);
restored_any = true;
}
if block_delta.is_some() || restored_any {
self.collapse_adjacent();
self.sort_blocks_newest_first();
}
block_delta
}
fn remove_id_from_blocks(&mut self, id: &EventId) -> Option<GroupDelta> {
if !self.seen.remove(id) {
return None;
}
let mut removed_idx: Option<usize> = None;
let mut block_replaced_idx: Option<usize> = None;
for (idx, block) in self.blocks.iter_mut().enumerate() {
match block {
TimelineBlock::Standalone { id: eid, .. } if eid == id => {
removed_idx = Some(idx);
break;
}
TimelineBlock::Module {
events,
has_gap,
root,
} => {
if events.iter().any(|e| e == id) {
events.retain(|e| e != id);
*has_gap = true;
if events.is_empty() {
removed_idx = Some(idx);
} else if events.len() == 1 {
let only = events.remove(0);
*block = TimelineBlock::Standalone {
id: only,
root: root.take(),
};
block_replaced_idx = Some(idx);
} else {
block_replaced_idx = Some(idx);
}
break;
}
}
TimelineBlock::Standalone { .. } => {}
}
}
if let Some(idx) = removed_idx {
self.blocks.remove(idx);
Some(GroupDelta::BlockRemoved(idx))
} else {
block_replaced_idx.map(GroupDelta::BlockReplaced)
}
}
fn unplace_standalone(&mut self, id: &EventId) {
let standalone_idx = self.blocks.iter().position(|block| match block {
TimelineBlock::Standalone { id: eid, .. } => eid == id,
TimelineBlock::Module { .. } => false,
});
if let Some(idx) = standalone_idx {
self.blocks.remove(idx);
self.seen.remove(id);
}
}
#[must_use]
pub fn on_replace(&mut self, old_id: &EventId, new_event: &KernelEvent) -> Option<GroupDelta> {
let _ = self.on_remove(old_id); self.on_insert(new_event)
}
}