using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Reflection; using InABox.Core; using System.Diagnostics.CodeAnalysis; namespace comal.timesheets { public abstract class ListModel : Model, IListModel, IEnumerable where TParent : ListModel where TEntity : Entity, IRemotable, IPersistent, new() where TItem : Shell, new() { protected ListModel(IModelHost host, Func> filter, bool transient = false) : base(host, filter, transient) { Reset(); } protected ListModel(IModelHost host, Func> filter, [NotNull] String filename) : base(host, filter, filename) { Reset(); } protected override void Initialize() { _allitems = null; _items.Clear(); } public virtual SortOrder Sort => LookupFactory.DefineSort(); private IEnumerable _allitems; protected IList _items = new List(); public IList Items { get => _items; set => SetProperty(ref _items, value); } public void Search(Func predicate) { Items = predicate != null ? new List(_allitems.Where(predicate)) : new List(_allitems); } protected virtual Expression> ImageColumn => null; public override void BeforeLoad(MultiQuery query) { } public override void AfterLoad(MultiQuery query) { } private Columns DataColumns() { var prop = typeof(TItem).GetProperties(BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy) .FirstOrDefault(x => x.PropertyType == typeof(ShellColumns)); var cols = prop?.GetValue(null) as ShellColumns; return cols?.Count > 0 ? cols?.Columns : Columns; } public IListModel Refresh(bool force) { if (!Loaded || force) Load(); return this; } public override void Refresh(bool force, Action loaded = null) { if (!Loaded || force) Load(loaded); else loaded?.Invoke(); } public override void Load(Action loaded = null) { MultiQuery query = new MultiQuery(); query.Add( Filter(), DataColumns(), Sort ); if (ImageColumn != null) { query.Add( new Filter(x => x.ID).InQuery(Filter(), ImageColumn), new Columns(x => x.ID) .Add(x => x.Data) ); } BeforeLoad(query); // If we have a valid transport, always try and get new data from the server if (Host.IsConnected()) { if (loaded != null) query.Query((q) => { if (Type == ModelType.Persistent) SaveToStorage(q); DoAfterLoad(q, loaded); }); else { query.Query(); if (Type == ModelType.Persistent) SaveToStorage(query); DoAfterLoad(query); } } else { if (Type == ModelType.Transient) { InitializeTables(query); } else if (Type == ModelType.Normal) { // Only load if (_allitems == null) InitializeTables(query); } else if (Type == ModelType.Persistent) { // Treat it as normal, unless its the first time through // in which case try to load it from storage, if the // data has been previously cached if (_allitems == null) LoadFromStorage(query); } DoAfterLoad(query, loaded); } } private void DoAfterLoad(MultiQuery query, Action loaded = null) { _allitems = new List(query.Get().Rows.Select(row => CreateItem(row))); if (ImageColumn != null) { Images.Clear(); query.Get().IntoDictionary(Images, x => x.ID, r => r.Get(x => x.Data)); } Search(null); AfterLoad(query); Loaded = true; loaded?.Invoke(); } protected T CreateItem(CoreRow row) where T : Shell, new() { var result = new T() { Row = row, Parent = (TParent)this }; result.PropertyChanged += (sender, args) => DoPropertyChanged(result, args); return result; } IEnumerator IEnumerable.GetEnumerator() { return Items.GetEnumerator(); } public IEnumerator GetEnumerator() { return Items.GetEnumerator(); } } }