using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Drawing;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using InABox.Clients;
using InABox.Configuration;
using InABox.Core;
using InABox.WPF;
using Expression = System.Linq.Expressions.Expression;
namespace InABox.DynamicGrid;
public interface IDynamicDataGrid : IDynamicGrid
{
///
/// The tag the the DynamicGridColumns are stored against. If set to ,
/// the name of is used as a default.
///
string? ColumnsTag { get; set; }
IColumns LoadEditorColumns();
}
public class DynamicDataGrid : DynamicGrid, IDynamicDataGrid where TEntity : Entity, IRemotable, IPersistent, new()
{
private Button MergeBtn = null!; //Late-initialised
protected DynamicGridCustomColumnsComponent ColumnsComponent;
public DynamicGridFilterButtonComponent FilterComponent;
public DynamicDataGrid() : base()
{
var fields = DatabaseSchema.Properties(typeof(TEntity));
foreach (var field in fields)
if (!MasterColumns.Any(x => x.ColumnName == field.Name))
MasterColumns.Add(new DynamicGridColumn { ColumnName = field.Name });
var cols = LookupFactory.DefineColumns();
// Minimum Columns for Lookup values
foreach (var col in cols)
HiddenColumns.Add(CoreUtils.CreateLambdaExpression(col.Property));
// Minimum Columns for Successful Saving
// This should be cross-checked with the relevant Store<>
// so that clients will (usually) provide sufficient columns for saving
foreach (var col in LookupFactory.RequiredColumns())
HiddenColumns.Add(CoreUtils.CreateLambdaExpression(col.Property));
//HiddenColumns.Add(x => x.ID);
if (typeof(TEntity).GetInterfaces().Contains(typeof(IIssues)))
{
HiddenColumns.Add(x => (x as IIssues)!.Issues);
var coltype = typeof(DynamicIssuesColumn<>).MakeGenericType(typeof(TEntity));
ActionColumns.Add((Activator.CreateInstance(coltype, this) as DynamicActionColumn)!);
}
}
protected override void Init()
{
FilterComponent = new(this,
new GlobalConfiguration(GetTag()),
new UserConfiguration(GetTag()));
FilterComponent.OnFilterRefresh += () => Refresh(false, true);
ColumnsComponent = new DynamicGridCustomColumnsComponent(this, GetTag());
var dataComponent = new DynamicGridClientDataComponent(this);
dataComponent.OnReload += DataComponent_OnReload;
DataComponent = dataComponent;
MergeBtn = AddButton("Merge", Wpf.Resources.merge.AsBitmapImage(Color.White), DoMerge);
}
protected override void SelectItems(CoreRow[]? rows)
{
base.SelectItems(rows);
MergeBtn.Visibility = Options.MultiSelect && typeof(T).IsAssignableTo(typeof(IMergeable)) && Security.CanMerge()
&& rows != null && rows.Length > 1
? Visibility.Visible
: Visibility.Collapsed;
}
private void DataComponent_OnReload(object sender, Filters criteria, Columns columns, ref SortOrder? sortby)
{
criteria.Add(FilterComponent.GetFilter());
}
protected override void OptionsChanged()
{
base.OptionsChanged();
FilterComponent.ShowFilterList = Options.FilterRows && !Options.HideDatabaseFilters;
if (MergeBtn != null)
MergeBtn.Visibility = Visibility.Collapsed;
}
protected override void DoReconfigure(DynamicGridOptions options)
{
if (Security.CanEdit())
{
options.AddRows = true;
options.EditRows = true;
}
if (Security.CanDelete())
options.DeleteRows = true;
if (Security.CanImport() && typeof(TEntity).HasInterface())
options.ImportData = true;
if (Security.CanExport() && typeof(TEntity).HasInterface())
options.ExportData = true;
if (Security.CanMerge())
options.MultiSelect = true;
}
protected override void BeforeLoad(IDynamicEditorForm form, TEntity[] items)
{
form.ReadOnly = form.ReadOnly || !Security.CanEdit();
base.BeforeLoad(form, items);
}
private string? _columnsTag;
public string? ColumnsTag
{
get => _columnsTag;
set
{
_columnsTag = value;
ColumnsComponent.Tag = GetTag();
}
}
public override void LoadEditorButtons(TEntity item, DynamicEditorButtons buttons)
{
base.LoadEditorButtons(item, buttons);
if (ClientFactory.IsSupported())
buttons.Add("Audit Trail", Wpf.Resources.view.AsBitmapImage(), item, AuditTrailClick);
}
private void AuditTrailClick(object sender, object? item)
{
var entity = (item as TEntity)!;
var window = new AuditWindow(entity.ID);
window.ShowDialog();
}
protected override DynamicGridColumns LoadColumns()
{
return ColumnsComponent.LoadColumns();
}
protected override void SaveColumns(DynamicGridColumns columns)
{
ColumnsComponent.SaveColumns(columns);
}
protected override void LoadColumnsMenu(ContextMenu menu)
{
base.LoadColumnsMenu(menu);
ColumnsComponent.LoadColumnsMenu(menu);
}
private string GetTag()
{
var tag = typeof(TEntity).Name;
if (!string.IsNullOrWhiteSpace(ColumnsTag))
tag = string.Format("{0}.{1}", tag, ColumnsTag);
return tag;
}
protected override DynamicGridSettings LoadSettings()
{
var tag = GetTag();
var user = Task.Run(() => new UserConfiguration(tag).Load());
user.Wait();
//var global = Task.Run(() => new GlobalConfiguration(tag).Load());
//global.Wait();
//Task.WaitAll(user, global);
//var columns = user.Result.Any() ? user.Result : global.Result;
return user.Result;
}
protected override void SaveSettings(DynamicGridSettings settings)
{
var tag = GetTag();
new UserConfiguration(tag).Save(settings);
}
#region Duplicate
protected bool Duplicate(
CoreRow row,
Expression> codefield,
Type[] childtypes)
{
var id = row.Get(x => x.ID);
var code = row.Get(codefield) as string;
var tasks = new List();
var itemtask = Task.Run(() =>
{
var filter = new Filter(x => x.ID).IsEqualTo(id);
var result = new Client().Load(filter).FirstOrDefault()
?? throw new Exception("Entity does not exist!");
return result;
});
tasks.Add(itemtask);
//itemtask.Wait();
Task>? codetask = null;
if (!string.IsNullOrWhiteSpace(code))
{
codetask = Task.Run(() =>
{
var columns = Columns.None().Add(codefield);
//columns.Add(codefield);
var filter = new Filter(codefield).BeginsWith(code);
var table = new Client().Query(filter, columns);
var result = table.Rows.Select(x => x.Get(codefield) as string).ToList();
return result;
});
tasks.Add(codetask);
}
//codetask.Wait();
var children = new Dictionary();
foreach (var childtype in childtypes)
{
var childtask = Task.Run(() =>
{
var prop = childtype.GetProperties().FirstOrDefault(x =>
x.PropertyType.GetInterfaces().Contains(typeof(IEntityLink)) &&
x.PropertyType.GetInheritedGenericTypeArguments().FirstOrDefault() == typeof(TEntity));
if (prop is not null)
{
var filter = Core.Filter.Create(childtype);
filter.Expression = CoreUtils.GetMemberExpression(childtype, prop.Name + ".ID");
filter.Operator = Operator.IsEqualTo;
filter.Value = id;
var sort = LookupFactory.DefineSort(childtype);
var client = ClientFactory.CreateClient(childtype);
var table = client.Query(filter, null, sort);
foreach (var r in table.Rows)
{
r["ID"] = Guid.Empty;
r[prop.Name + ".ID"] = Guid.Empty;
}
children[childtype] = table;
}
else
{
Logger.Send(LogType.Error, "", $"DynamicDataGrid<{typeof(TEntity)}>.Duplicate(): No parent property found for child type {childtype}");
}
});
tasks.Add(childtask);
//childtask.Wait();
}
//var manytomanys = CoreUtils.TypeList(
// AppDomain.CurrentDomain.GetAssemblies(),
// x => x.GetInterfaces().Any(intf => intf.IsGenericType && intf.GetGenericTypeDefinition() == typeof(IManyToMany<,>) && intf.GenericTypeArguments.Contains(typeof(T)))
//);
//Task childtask = Task.Run(() =>
//{
// var result = new CoreTable();
// result.LoadColumns(typeof(TChild));
// var children = new Client().Load(new Filter(x => linkfield).IsEqualTo(id));
// foreach (var child in children)
// {
// child.ID = Guid.Empty;
// String linkprop = CoreUtils.GetFullPropertyName(linkfield, ".");
// CoreUtils.SetPropertyValue(child, linkprop, Guid.Empty);
// var newrow = result.NewRow();
// result.LoadRow(newrow, child);
// result.Rows.Add(newrow);
// }
// return result;
//});
//tasks.Add(childtask);
Task.WaitAll(tasks.ToArray());
var item = itemtask.Result;
item.ID = Guid.Empty;
if (codetask != null)
{
var codes = codetask.Result;
var i = 1;
while (codes.Contains(string.Format("{0} ({1})", code, i)))
i++;
var codeprop = CoreUtils.GetFullPropertyName(codefield, ".");
CoreUtils.SetPropertyValue(item, codeprop, string.Format("{0} ({1})", code, i));
}
var grid = new DynamicDataGrid();
return grid.EditItems(new[] { item }, t => children.ContainsKey(t) ? children[t] : null, true);
}
protected override IEnumerable LoadDuplicatorItems(CoreRow[] rows)
{
return rows.Select(x => x.ToObject());
}
#endregion
protected override bool BeforePaste(IEnumerable items, ClipAction action)
{
if (action == ClipAction.Copy)
{
foreach (var item in items)
item.ID = Guid.Empty;
return true;
}
return base.BeforePaste(items, action);
}
protected override IEnumerable> LoadExportTables(Filters filter, IEnumerable> tableColumns)
{
var queries = new Dictionary();
var columns = tableColumns.ToList();
foreach (var table in columns)
{
var tableType = table.Item1;
PropertyInfo? property = null;
var m2m = CoreUtils.GetManyToMany(tableType, typeof(TEntity));
IFilter? queryFilter = null;
if (m2m != null)
{
property = CoreUtils.GetManyToManyThisProperty(tableType, typeof(TEntity));
}
else
{
var o2m = CoreUtils.GetOneToMany(tableType, typeof(TEntity));
if (o2m != null)
{
property = CoreUtils.GetOneToManyProperty(tableType, typeof(TEntity));
}
}
if (property != null)
{
var subQuery = new SubQuery();
subQuery.Filter = filter.Combine();
subQuery.Column = new Column(x => x.ID);
queryFilter = (Activator.CreateInstance(typeof(Filter<>).MakeGenericType(tableType)) as IFilter)!;
queryFilter.Expression = CoreUtils.GetMemberExpression(tableType, property.Name + ".ID");
queryFilter.InQuery(subQuery);
queries[tableType.Name] = new QueryDef(tableType)
{
Filter = queryFilter,
Columns = table.Item2
};
}
}
var results = Client.QueryMultiple(queries);
return columns.Select(x => new Tuple(x.Item1, results[x.Item1.Name]));
}
protected override CoreTable LoadImportKeys(String[] fields)
{
return Client.Query(null, Columns.None().Add(fields));
}
#region Merge
private bool DoMerge(Button arg1, CoreRow[] arg2)
{
if (arg2 == null || arg2.Length <= 1)
return false;
var targetid = arg2.Last().Get(x => x.ID);
var target = arg2.Last().ToObject().ToString();
var otherids = arg2.Select(r => r.Get(x => x.ID)).Where(x => x != targetid).ToArray();
string[] others = arg2.Where(r => otherids.Contains(r.Get("ID"))).Select(x => x.ToObject().ToString()!).ToArray();
var rows = arg2.Length;
if (MessageBox.Show(
string.Format(
"This will merge the following items:\n\n- {0}\n\n into:\n\n- {1}\n\nAfter this, the items will be permanently removed.\nAre you sure you wish to do this?",
string.Join("\n- ", others),
target
),
"Merge Items Warning",
MessageBoxButton.YesNo,
MessageBoxImage.Stop) != MessageBoxResult.Yes
)
return false;
using (new WaitCursor())
{
var types = CoreUtils.Entities.Where(
x =>
x.IsClass
&& !x.IsGenericType
&& x.IsSubclassOf(typeof(Entity))
&& !x.Equals(typeof(AuditTrail))
&& !x.Equals(typeof(T))
&& x.GetCustomAttribute() == null
&& x.HasInterface()
&& x.HasInterface()
).ToArray();
foreach (var type in types)
{
var props = CoreUtils.PropertyList(
type,
x =>
x.PropertyType.GetInterfaces().Contains(typeof(IEntityLink))
&& x.PropertyType.GetInheritedGenericTypeArguments().Contains(typeof(T))
);
foreach (var prop in props)
{
var propname = string.Format(prop.Name + ".ID");
var filter = Core.Filter.Create(type);
filter.Expression = CoreUtils.CreateMemberExpression(type, propname);
filter.Operator = Operator.InList;
filter.Value = otherids;
var columns = Columns.None(type)
.Add("ID")
.Add(propname);
var updates = ClientFactory.CreateClient(type).Query(filter, columns).Rows.Select(r => r.ToObject(type)).ToArray();
if (updates.Any())
{
foreach (var update in updates)
CoreUtils.SetPropertyValue(update, propname, targetid);
ClientFactory.CreateClient(type).Save(updates,
string.Format("Merged {0} Records", typeof(T).EntityName().Split('.').Last()));
}
}
}
var histories = new Client()
.Query(
new Filter(x => x.EntityID).InList(otherids),
Columns.None()
.Add(x => x.ID).Add(x => x.EntityID))
.ToArray();
foreach (var history in histories)
history.EntityID = targetid;
if (histories.Length != 0)
new Client().Save(histories, "");
var deletes = new List