use crate::cache::{ChangeAwareCache, ChangeReason, ChangeSet, ICache};
use crate::internal::{CacheParentSubscription, ParentSubscriptionHandler};
use ferroui_reactive::{Observable, ObservableExt};
use ferroui_system::{IDisposable, IObservable, IObserver};
use std::cell::RefCell;
use std::hash::Hash;
use std::rc::Rc;
pub(crate) type ObservableTransform<S, K, D> = Rc<dyn Fn(&S, &K) -> Rc<dyn IObservable<D>>>;
pub(crate) fn transform_on_observable<S: Clone + 'static, K: Eq + Hash + Clone + 'static, D: Clone + PartialEq + 'static>(
source: Rc<dyn IObservable<ChangeSet<S, K>>>,
transform: ObservableTransform<S, K, D>,
transform_on_refresh: bool,
) -> Rc<dyn IObservable<ChangeSet<D, K>>> {
Observable::create(move |observer: Rc<dyn IObserver<ChangeSet<D, K>>>| {
let handler = Subscription { cache: RefCell::new(ChangeAwareCache::new()), transform: transform.clone(), transform_on_refresh };
let subscription = CacheParentSubscription::new(observer, Box::new(handler));
subscription.create_parent_subscription(source.clone());
let subscription: Rc<dyn IDisposable> = subscription;
subscription
})
}
struct Subscription<S, K, D> {
cache: RefCell<ChangeAwareCache<D, K>>,
transform: ObservableTransform<S, K, D>,
transform_on_refresh: bool,
}
impl<S: Clone + 'static, K: Eq + Hash + Clone + 'static, D: Clone + PartialEq + 'static> Subscription<S, K, D> {
fn add_transform_subscription(&self, owner: &CacheParentSubscription<S, K, D, ChangeSet<D, K>>, item: &S, key: &K) {
let observable = (self.transform)(item, key).distinct_until_changed();
owner.add_child_subscription(owner.make_child_observable(observable), key.clone());
}
}
impl<S: Clone + 'static, K: Eq + Hash + Clone + 'static, D: Clone + PartialEq + 'static> ParentSubscriptionHandler<S, K, D, ChangeSet<D, K>> for Subscription<S, K, D> {
fn parent_on_next(&self, owner: &CacheParentSubscription<S, K, D, ChangeSet<D, K>>, changes: ChangeSet<S, K>) {
for change in &changes {
match change.reason() {
ChangeReason::Add | ChangeReason::Update => self.add_transform_subscription(owner, change.current(), change.key()),
ChangeReason::Remove => {
self.cache.borrow_mut().remove(change.key());
owner.remove_child_subscription(change.key());
}
ChangeReason::Refresh => {
if self.transform_on_refresh {
self.add_transform_subscription(owner, change.current(), change.key());
} else {
self.cache.borrow_mut().refresh(change.key());
}
}
ChangeReason::Moved => {}
}
}
}
fn child_on_next(&self, child: D, parent_key: &K) {
self.cache.borrow_mut().add_or_update(child, parent_key.clone());
}
fn emit_changes(&self, observer: &Rc<dyn IObserver<ChangeSet<D, K>>>) {
let changes = self.cache.borrow_mut().capture_changes();
if changes.count() > 0 {
observer.on_next(changes);
}
}
}