using System; using System.Collections; using System.Collections.Generic; using EcsRx.Events.Collections; using EcsRx.Extensions; using EcsRx.MicroRx.Extensions; using EcsRx.MicroRx.Subjects; namespace EcsRx.Plugins.Computeds.Collections { public abstract class ComputedCollectionFromData : IComputedCollection, IDisposable { public IList ComputedData { get; } public List Subscriptions { get; } public IObservable> OnAdded => onElementAdded; public IObservable> OnRemoved => onElementChanged; public IObservable> OnUpdated => onElementChanged; public TInput DataSource { get; } public IEnumerable Value => GetData(); public TOutput this[int index] => ComputedData[index]; public int Count => ComputedData.Count; protected readonly Subject> onDataChanged; protected readonly Subject> onElementAdded; protected readonly Subject> onElementChanged; protected readonly Subject> onElementRemoved; private bool _needsUpdate; public ComputedCollectionFromData(TInput dataSource) { DataSource = dataSource; Subscriptions = new List(); ComputedData = new List(); onDataChanged = new Subject>(); onElementAdded = new Subject>(); onElementChanged = new Subject>(); onElementRemoved = new Subject>(); MonitorChanges(); RefreshData(); } public IDisposable Subscribe(IObserver> observer) { return onDataChanged.Subscribe(observer); } public void MonitorChanges() { RefreshWhen().Subscribe(x => RequestUpdate()).AddTo(Subscriptions); } public void RequestUpdate(object _ = null) { _needsUpdate = true; if(onDataChanged.HasObservers || onElementAdded.HasObservers || onElementChanged.HasObservers || onElementRemoved.HasObservers) { RefreshData(); } } public void RefreshData() { Transform(DataSource); _needsUpdate = false; } /// /// The method to indicate when the listings should be updated /// /// /// If there is no checking required outside of adding/removing this can /// return an empty observable, but common usages would be to refresh every update. /// The bool is throw away, but is a workaround for not having a Unit class /// /// An observable trigger that should trigger when the group should refresh public abstract IObservable RefreshWhen(); /// /// The method to populate ComputedData and raise events from the data source /// /// /// Unfortunately this is not as clever as the other computed classes /// and is unable to really work out whats added/removed etc /// so it is up to the consumer to trigger the events and populate /// the ComputedData object /// /// The dataSource to transform public abstract void Transform(TInput dataSource); public IEnumerable GetData() { if(_needsUpdate) { RefreshData(); } return ComputedData; } public IEnumerator GetEnumerator() { return GetData().GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } public void Dispose() { Subscriptions.DisposeAll(); onDataChanged?.Dispose(); onElementAdded?.Dispose(); onElementChanged?.Dispose(); onElementRemoved?.Dispose(); } } }