using InABox.Clients; using InABox.Core; using InABox.WPF; using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows.Controls; using System.Windows.Media.Imaging; namespace InABox.DynamicGrid; public abstract class DynamicManyToManyCrossTab : DynamicGrid where TManyToMany : Entity, IRemotable, IPersistent, new() where TRow : Entity, IRemotable, IPersistent, new() where TColumn : Entity, IRemotable, IPersistent, new() { private static readonly BitmapImage tick = Wpf.Resources.tick.AsBitmapImage(); /// /// Property on which is an for . /// private readonly PropertyInfo rowProperty; /// /// Property on which is an for . /// private readonly PropertyInfo columnProperty; private CoreTable? ColumnData; /// /// {ColumnID : { RowID }} /// private Dictionary>? ManyToManyData; protected new CrossTabDataComponent DataComponent; public DynamicManyToManyCrossTab() { rowProperty = CoreUtils.GetManyToManyThisProperty(typeof(TManyToMany), typeof(TRow)); columnProperty = CoreUtils.GetManyToManyThisProperty(typeof(TManyToMany), typeof(TColumn)); HeaderHeight = 125; DataComponent = new CrossTabDataComponent(this); base.DataComponent = DataComponent; } protected override void Init() { } protected override void DoReconfigure(DynamicGridOptions options) { options.Clear(); } /// /// Load the required columns for . /// protected abstract DynamicGridColumns LoadRowColumns(); protected abstract Columns? LoadColumnColumns(); protected abstract SortOrder? LoadColumnSort(); protected abstract string FormatColumnHeader(CoreRow row); protected virtual Filter? RowFilter() => null; protected virtual Filter? ColumnFilter() => null; protected override DynamicGridColumns LoadColumns() { var columns = LoadRowColumns(); var client = Client.Create(typeof(TColumn)); var columnColumns = Columns.None().Add(x => x.ID); if(LoadColumnColumns() is Columns extra) { foreach(var col in extra) { columnColumns.Add(col); } } ColumnData = Client.Query(ColumnFilter(), columnColumns, LoadColumnSort()); ActionColumns.Clear(); foreach(var columnRow in ColumnData.Rows) { var colID = columnRow.Get(x => x.ID); ActionColumns.Add(new DynamicImageColumn( (row) => { if (row is null || ManyToManyData is null) return null; if(ManyToManyData.TryGetValue(colID, out var rowSet)) { var rowID = row.Get(x => x.ID); if (rowSet.ContainsKey(rowID)) { return tick; } } return null; }, (row) => { if (row is null) return false; var rowID = row.Get(x => x.ID); return CellClick(colID, rowID); } ) { HeaderText = FormatColumnHeader(columnRow), ContextMenu = (rows) => { var row = rows?.FirstOrDefault(); if (row is null) return null; var rowID = row.Get(x => x.ID); return CellMenu(colID, rowID); } }); } return columns; } private ContextMenu? CellMenu(Guid colID, Guid rowID) { if (ManyToManyData is null) return null; var menu = new ContextMenu(); if (ManyToManyData.TryGetValue(colID, out var rowSet) && rowSet.TryGetValue(rowID, out var obj)) { if (Security.CanEdit() && CanEditCell(obj)) { menu.AddItem("Edit Item", Wpf.Resources.pencil, obj, (obj) => { if (EditCell(obj)) { Refresh(false, true); } }); } if (Security.CanDelete()) { menu.AddItem("Delete Item", Wpf.Resources.delete, obj, DeleteCell); } } else { if (Security.CanEdit()) { menu.AddItem("Create Item", Wpf.Resources.add, (colID, rowID), CreateCell); } } if(menu.Items.Count > 0) { return menu; } else { return null; } } private void CreateCell((Guid colID, Guid rowID) obj) { var manyToMany = CreateManyToMany(obj.rowID, obj.colID); if (SaveManyToMany(manyToMany)) { Refresh(false, true); } } private void DeleteCell(TManyToMany obj) { if (DeleteManyToMany(obj)) { Refresh(false, true); } } protected virtual bool CanEditCell(TManyToMany obj) => false; /// /// Code to edit a cell; note that this must not allow for changing either /// the or the , otherwise we would easily get duplicate s. /// /// /// This method should also save the object. /// /// protected virtual bool EditCell(TManyToMany obj) { return false; } private bool CellClick(Guid columnID, Guid rowID) { if (ManyToManyData is null) return false; if (ManyToManyData.TryGetValue(columnID, out var rowSet) && rowSet.TryGetValue(rowID, out var obj)) { if (Security.CanDelete()) { return DeleteManyToMany(obj); } else { return false; } } else { if (Security.CanEdit()) { obj = CreateManyToMany(rowID, columnID); return SaveManyToMany(obj); } else { return false; } } } protected virtual TManyToMany CreateManyToMany(Guid rowID, Guid columnID) { var item = new TManyToMany(); (rowProperty.GetValue(item) as IEntityLink)!.ID = rowID; (columnProperty.GetValue(item) as IEntityLink)!.ID = columnID; return item; } protected virtual bool SaveManyToMany(TManyToMany obj) { Client.Save(obj, "Edited by user"); return true; } protected virtual bool DeleteManyToMany(TManyToMany obj) { Client.Delete(obj, "Deleted by user"); return true; } protected class CrossTabDataComponent : BaseDynamicGridDataComponent { private new DynamicManyToManyCrossTab Grid; public CrossTabDataComponent(DynamicManyToManyCrossTab grid): base(grid) { Grid = grid; } public override TRow LoadItem(CoreRow row) { var id = row.Get(x => x.ID); return Client.Query( new Filter(x => x.ID).IsEqualTo(id), DynamicGridUtils.LoadEditorColumns(Grid.DataColumns())) .ToObjects() .FirstOrDefault() ?? throw new Exception($"No {typeof(TRow).Name} with ID {id}"); } public override void Reload(Filters criteria, Columns columns, SortOrder? sort, CancellationToken token, Action action) { var filter = criteria.Add(Grid.RowFilter()).Combine(); var manyToManyColumns = Columns.None().Add(x => x.ID); manyToManyColumns.Add(Grid.rowProperty.Name + ".ID").Add(Grid.columnProperty.Name + ".ID"); Client.QueryMultiple( (results, e) => { if(e is not null) { action(new(e)); } else { var manyToManyTable = results!.Get(); Grid.ManyToManyData = new Dictionary>(); foreach(var row in manyToManyTable.Rows) { var obj = row.ToObject(); var rowID = (Grid.rowProperty.GetValue(obj) as IEntityLink)!.ID; var colID = (Grid.columnProperty.GetValue(obj) as IEntityLink)!.ID; var rowSet = Grid.ManyToManyData.GetValueOrAdd(colID); rowSet[rowID] = obj; } action(new(results!.Get())); } }, new KeyedQueryDef(filter, columns, sort), new KeyedQueryDef( new Filter(Grid.rowProperty.Name + ".ID").InQuery(filter, x => x.ID) .And(Grid.columnProperty.Name + ".ID").InQuery(Grid.ColumnFilter(), x => x.ID), manyToManyColumns)); } public override void SaveItem(TRow item) { // Never should get called. } public override void DeleteItems(CoreRow[] rows) { // Never should get called. } } }