use super::{Comparer, IndexCalculator, KeyValueCollection, KeyValueComparer};
use crate::cache::{ChangeAwareCache, ChangeSet, IQuery, SortOptimisations, SortReason, SortedChangeSet};
use crate::list::SortException;
use ferroui_reactive::{Observable, ObservableExt};
use ferroui_system::{IObservable, IObserver};
use std::cell::RefCell;
use std::hash::Hash;
use std::rc::Rc;
pub(crate) fn sort<T: Clone + 'static, K: Eq + Hash + Clone + 'static>(
source: Rc<dyn IObservable<ChangeSet<T, K>>>,
comparer: Option<Comparer<T>>,
sort_optimisations: SortOptimisations,
comparer_changed: Option<Rc<dyn IObservable<Comparer<T>>>>,
resorter: Option<Rc<dyn IObservable<()>>>,
reset_threshold: usize,
) -> Rc<dyn IObservable<SortedChangeSet<T, K>>> {
assert!(comparer.is_some() || comparer_changed.is_some(), "Must specify comparer or comparerChanged");
Observable::create(move |observer: Rc<dyn IObserver<SortedChangeSet<T, K>>>| {
let sorter = Rc::new(RefCell::new(Sorter::new(sort_optimisations, comparer.clone(), reset_threshold)));
if comparer_changed.is_none() && resorter.is_none() {
let data_sorter = sorter.clone();
let data_changed = source.try_select(move |changes: ChangeSet<T, K>| data_sorter.borrow_mut().sort_changes(&changes).map_err(SortException::into_error));
return where_some(data_changed).subscribe(observer);
}
let comparer_sorter = sorter.clone();
let comparer_changed = comparer_changed
.clone()
.unwrap_or_else(Observable::never)
.try_select(move |comparer: Comparer<T>| comparer_sorter.borrow_mut().sort_comparer(comparer).map_err(SortException::into_error));
let reorder_sorter = sorter.clone();
let sort_again = resorter.clone().unwrap_or_else(Observable::never).try_select(move |_: ()| reorder_sorter.borrow_mut().sort_again().map_err(SortException::into_error));
let data_sorter = sorter.clone();
let data_changed = source.try_select(move |changes: ChangeSet<T, K>| data_sorter.borrow_mut().sort_changes(&changes).map_err(SortException::into_error));
where_some(Observable::merge_all(vec![comparer_changed, data_changed, sort_again])).subscribe(observer)
})
}
pub(crate) fn where_some<U: Clone + 'static>(source: Rc<dyn IObservable<Option<U>>>) -> Rc<dyn IObservable<U>> {
source.where_(Option::is_some).select(|value: Option<U>| value.expect("the values that are not Some are filtered out"))
}
struct Sorter<T, K> {
optimisations: SortOptimisations,
reset_threshold: usize,
cache: ChangeAwareCache<T, K>,
calculator: Option<IndexCalculator<T, K>>,
comparer: KeyValueComparer<T, K>,
have_received_data: bool,
initialised: bool,
}
impl<T: Clone + 'static, K: Eq + Hash + Clone + 'static> Sorter<T, K> {
fn new(optimisations: SortOptimisations, comparer: Option<Comparer<T>>, reset_threshold: usize) -> Self {
Self {
optimisations,
reset_threshold,
cache: ChangeAwareCache::new(),
calculator: None,
comparer: KeyValueComparer::new(comparer),
have_received_data: false,
initialised: false,
}
}
fn sort_changes(&mut self, changes: &ChangeSet<T, K>) -> Result<Option<SortedChangeSet<T, K>>, SortException> {
self.do_sort(SortReason::DataChanged, Some(changes))
}
fn sort_comparer(&mut self, comparer: Comparer<T>) -> Result<Option<SortedChangeSet<T, K>>, SortException> {
self.comparer = KeyValueComparer::new(Some(comparer));
self.do_sort(SortReason::ComparerChanged, None)
}
fn sort_again(&mut self) -> Result<Option<SortedChangeSet<T, K>>, SortException> {
self.do_sort(SortReason::Reorder, None)
}
fn do_sort(&mut self, mut sort_reason: SortReason, changes: Option<&ChangeSet<T, K>>) -> Result<Option<SortedChangeSet<T, K>>, SortException> {
let changes = changes.map(|changes| {
crate::cache::ICache::clone_changes(&mut self.cache, changes);
self.have_received_data = true;
self.cache.capture_changes()
});
if !self.have_received_data {
return Ok(None);
}
if !self.initialised {
sort_reason = SortReason::InitialLoad;
self.initialised = true;
} else if let Some(changes) = &changes {
if self.reset_threshold > 0 && changes.count() >= self.reset_threshold {
sort_reason = SortReason::Reset;
}
}
let change_set = match sort_reason {
SortReason::InitialLoad => {
let mut calculator = IndexCalculator::new(self.comparer.clone(), self.optimisations);
let change_set = calculator.load(&self.cache);
self.calculator = Some(calculator);
change_set
}
SortReason::Reset => {
let Self { calculator, cache, .. } = &mut *self;
calculator.as_mut().expect("The calculator has not been initialized").reset(cache);
changes.expect("a reset of the data has its changes")
}
SortReason::DataChanged => {
let changes = changes.expect("Data has been indicated as changed, but changes is null.");
self.calculator_mut().calculate(&changes)?
}
SortReason::ComparerChanged => {
let comparer = self.comparer.clone();
let mut change_set = self.calculator_mut().change_comparer(comparer);
if self.reset_threshold > 0 && self.cache.count() >= self.reset_threshold {
sort_reason = SortReason::Reset;
let Self { calculator, cache, .. } = &mut *self;
calculator.as_mut().expect("The calculator has not been initialized").reset(cache);
} else {
sort_reason = SortReason::Reorder;
change_set = self.calculator_mut().reorder();
}
change_set
}
SortReason::Reorder => self.calculator_mut().reorder(),
};
if sort_reason == SortReason::Reorder && change_set.count() == 0 {
return Ok(None);
}
let calculator = self.calculator.as_ref().expect("The calculator has not been initialized");
let sorted = KeyValueCollection::new(calculator.list().to_vec(), self.comparer.clone(), sort_reason, self.optimisations);
Ok(Some(SortedChangeSet::new(sorted, change_set)))
}
fn calculator_mut(&mut self) -> &mut IndexCalculator<T, K> {
self.calculator.as_mut().expect("The calculator has not been initialized")
}
}