use std::any::{Any, TypeId};
use std::cell::RefCell;
use std::collections::{HashMap, VecDeque};
use std::rc::Rc;
use guinea_core::guard::Verdict;
use guinea_router::router::{SegmentEntry, placement_hash};
use crate::Iced;
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub(crate) struct Placement {
pub cursor: usize,
pub segment: TypeId,
}
pub(crate) struct Held {
pub node: Box<dyn Any>,
pub params: Box<dyn Any>,
pub verdict: Rc<RefCell<Verdict>>,
}
const MAX_KEPT: usize = 10;
#[derive(Default)]
pub struct Nodes {
live: HashMap<Placement, Held>,
kept: HashMap<(u64, usize), Held>,
order: VecDeque<(u64, usize)>,
chain: Option<&'static [SegmentEntry<Iced>]>,
}
impl Nodes {
pub(crate) fn get<P: 'static>(&self, cursor: usize) -> Option<&P> {
let placement = Placement {
cursor,
segment: TypeId::of::<P>(),
};
self.live.get(&placement)?.node.downcast_ref()
}
pub(crate) fn get_mut<P: 'static>(
&mut self,
cursor: usize,
) -> Option<(&mut P, &Rc<RefCell<Verdict>>)> {
let placement = Placement {
cursor,
segment: TypeId::of::<P>(),
};
let held = self.live.get_mut(&placement)?;
let node = held.node.downcast_mut()?;
Some((node, &held.verdict))
}
pub(crate) fn sync(&mut self, chain: &'static [SegmentEntry<Iced>]) {
let fresh = take_staged();
let unchanged = self
.chain
.is_some_and(|current| std::ptr::eq(current, chain));
if fresh.is_empty() && unchanged {
return;
}
let leaving = self.chain.take();
let staged: Vec<Placement> = fresh.iter().map(|(placement, _, _)| *placement).collect();
for (placement, held) in std::mem::take(&mut self.live) {
let survives = !staged.contains(&placement)
&& chain
.get(placement.cursor)
.is_some_and(|entry| (entry.type_id)() == placement.segment);
if survives {
self.live.insert(placement, held);
continue;
}
if let Some(previous) = leaving
&& previous
.get(placement.cursor)
.is_some_and(|entry| entry.cache_state)
{
self.keep(
(placement_hash(previous, placement.cursor), placement.cursor),
held,
);
}
}
for (placement, held, cache_state) in fresh {
let held = match cache_state {
true => self.restored(chain, placement, held),
false => held,
};
self.live.insert(placement, held);
}
self.chain = Some(chain);
}
fn restored(
&mut self,
chain: &'static [SegmentEntry<Iced>],
placement: Placement,
fresh: Held,
) -> Held {
let key = (placement_hash(chain, placement.cursor), placement.cursor);
let Some(kept) = self.kept.remove(&key) else {
return fresh;
};
self.order.retain(|held| *held != key);
let same = (chain[placement.cursor].same_params)(&*kept.params, &*fresh.params);
if same { kept } else { fresh }
}
fn keep(&mut self, key: (u64, usize), held: Held) {
if self.kept.insert(key, held).is_none() {
self.order.push_back(key);
}
while self.order.len() > MAX_KEPT {
if let Some(oldest) = self.order.pop_front() {
self.kept.remove(&oldest);
}
}
}
}
thread_local! {
static STAGED: RefCell<Vec<(Placement, Held, bool)>> = const { RefCell::new(Vec::new()) };
}
pub(crate) fn stage(placement: Placement, held: Held, cache_state: bool) {
STAGED.with(|staged| staged.borrow_mut().push((placement, held, cache_state)));
}
fn take_staged() -> Vec<(Placement, Held, bool)> {
STAGED.with(|staged| std::mem::take(&mut *staged.borrow_mut()))
}