using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using InABox.Clients; using InABox.Core; using InABox.Wpf; using InABox.Reports.Common; using Syncfusion.Data.Extensions; namespace InABox.DynamicGrid { [Caption("Set Default Column Selections")] public class CanSetDefaultColumns : EnabledSecurityDescriptor { } [LibraryInitializer] public static class DynamicGridUtils { private static IEnumerable? _allm2mtypes; private static IEnumerable? _allm2mpages; private static IEnumerable? _allo2mtypes; private static IEnumerable? _allo2mpages; private static IEnumerable? _allcepages; private static IEnumerable? _alleltypes; private static Dictionary> _onetomanypages = new Dictionary>(); private static Dictionary> _manytomanytomanypages = new Dictionary>(); private static Dictionary> _enclosedlistpages = new Dictionary>(); private static Dictionary> _customeditorpages = new Dictionary>(); // HACK: These are really dumb public static Action? PreviewReport { get; set; } public static Action? PrintMenu { get; set; } public static readonly MainResources Resources = new(); public static void RegisterClasses() { // String assyname = "_" + Assembly.GetExecutingAssembly().GetName().Name; // AssemblyName assemblyName = new AssemblyName(assyname); // AppDomain appDomain = Thread.GetDomain(); // // String assyFile = String.Format("{0}.dll", assemblyName.Name); // String path = ""; // if (Assembly.GetEntryAssembly() != null) // { // path = Path.Combine( // Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), // Path.GetFileNameWithoutExtension(Assembly.GetEntryAssembly().Location) // ); // } // else // { // path = Path.Combine( // Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), // Path.GetFileNameWithoutExtension(Assembly.GetExecutingAssembly().Location) // ); // } // // if (!Directory.Exists(path)) // Directory.CreateDirectory(path); // AssemblyBuilder assemblyBuilder = appDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave, path); // // ModuleBuilder module = assemblyBuilder.DefineDynamicModule(assyFile); //,true); // // if (_allm2mtypes == null) // { // _allm2mtypes = CoreUtils.TypeList( // AppDomain.CurrentDomain.GetAssemblies(), // x => x.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IManyToMany<,>)) // ); // } // // var maps = _allm2mtypes.Where(x => x.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IManyToMany<,>) && i.GenericTypeArguments.Last().Equals(typeof(Document)))); // // foreach (var map in maps) // { // var intf = map.GetInterfaces().FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IManyToMany<,>) && i.GenericTypeArguments.Last().Equals(typeof(Document))); // Type entity = intf.GenericTypeArguments.First(); // Type basetype = typeof(DynamicDocumentGrid<,>).MakeGenericType(map, entity); // TypeBuilder tbService = module.DefineType(String.Format("{0}", map.EntityName().Replace(".", "_")), TypeAttributes.Public | TypeAttributes.Class); // tbService.SetParent(basetype); // Type final = tbService.CreateType(); // } // // try // { // assemblyBuilder.Save(assyFile); // } // catch (Exception e) // { // Logger.Send(LogType.Error, "", String.Format("*** Unknown Error: {0}\n{1}", e.Message, e.StackTrace)); // } } #region Pages public static IEnumerable GetManyToManyTypes(Type type) { _allm2mtypes ??= CoreUtils.TypeList( AppDomain.CurrentDomain.GetAssemblies(), x => x.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IManyToMany<,>)) ); return _allm2mtypes.Where(x => x.GetInterfaces().Any( i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IManyToMany<,>) && i.GenericTypeArguments[0] == type) ); } public static void LoadManyToManyPages(Type type, DynamicEditorPages pages) { if (!_manytomanytomanypages.ContainsKey(type)) { var cache = new List(); var maps = GetManyToManyTypes(type); foreach (var map in maps) { if (ClientFactory.IsSupported(map)) { _allm2mpages ??= CoreUtils.TypeList( AppDomain.CurrentDomain.GetAssemblies(), x => x.IsClass && !x.IsAbstract && !x.IsGenericType && x.GetInterfaces().Any( i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IDynamicManyToManyGrid<,>) ) ); var subtypes = _allm2mpages.Where( x => x.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IDynamicManyToManyGrid<,>) && i.GenericTypeArguments.First().Equals(map) && i.GenericTypeArguments.Last().Equals(type)) ); IDynamicEditorPage page; if (subtypes.Any()) { page = (Activator.CreateInstance(subtypes.First()) as IDynamicEditorPage)!; } else { var defType = typeof(DynamicManyToManyGrid<,>).MakeGenericType(map, type); page = (Activator.CreateInstance(defType) as IDynamicEditorPage)!; } cache.Add(page); } } _manytomanytomanypages[type] = cache; } pages.AddRange(_manytomanytomanypages[type]); } public static IEnumerable GetOneToManyTypes(Type type) { _allo2mtypes ??= CoreUtils.TypeList( AppDomain.CurrentDomain.GetAssemblies(), x => x.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IOneToMany<>) && x.GetCustomAttribute() == null) ); return _allo2mtypes .Where(x => x.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IOneToMany<>) && i.GenericTypeArguments.Contains(type))) .OrderBy(x => x.EntityName()); } public static void LoadOneToManyPages(Type type, DynamicEditorPages pages) { if (!_onetomanypages.ContainsKey(type)) { var cache = new List(); var maps = GetOneToManyTypes(type); foreach (var map in maps) { if (ClientFactory.IsSupported(map)) { _allo2mpages ??= CoreUtils.TypeList( AppDomain.CurrentDomain.GetAssemblies(), x => x.IsClass && !x.IsAbstract && !x.IsGenericType && x.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IDynamicOneToManyGrid<,>) ) ); var subtypes = _allo2mpages.Where(x => x.GetInterfaces().Any( i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IDynamicOneToManyGrid<,>) && i.GenericTypeArguments.First().Equals(type) && i.GenericTypeArguments.Last().Equals(map) ) ); IDynamicEditorPage page; if (subtypes.Any()) { page = (Activator.CreateInstance(subtypes.First()) as IDynamicEditorPage)!; } else { var defType = typeof(DynamicOneToManyGrid<,>).MakeGenericType(type, map); page = (Activator.CreateInstance(defType) as IDynamicEditorPage)!; } cache.Add(page); } } _onetomanypages[type] = cache; } pages.AddRange(_onetomanypages[type]); } public static void LoadCustomEditorPages(Type type, DynamicEditorPages pages) { if (!_customeditorpages.ContainsKey(type)) { var cache = new List(); _allcepages ??= CoreUtils.TypeList( AppDomain.CurrentDomain.GetAssemblies(), x => x.IsClass && !x.IsAbstract && !x.IsGenericType && x.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IDynamicCustomEditorPage<>) ) ); var pagetypes = _allcepages.Where(x => x.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IDynamicCustomEditorPage<>) && i.GenericTypeArguments.First().Equals(type))); foreach (var pagetype in pagetypes) { var page = (Activator.CreateInstance(pagetype) as IDynamicEditorPage)!; cache.Add(page); } _customeditorpages[type] = cache; } pages.AddRange(_customeditorpages[type]); } public static void LoadEnclosedListPages(Type type, DynamicEditorPages pages) { if (!_enclosedlistpages.ContainsKey(type)) { var cache = new List(); foreach (var property in type.GetProperties()) { if (property.PropertyType.GetInterfaces().Contains(typeof(IList))) { var curtype = property.PropertyType; var gentype = property.PropertyType.GetGenericArguments().FirstOrDefault(); while (gentype == null && curtype?.BaseType != null) { curtype = curtype.BaseType; gentype = curtype?.GetGenericArguments().FirstOrDefault(); } if (gentype != null) if (gentype.IsSubclassOf(typeof(BaseObject))) { var editor = property.GetCustomAttributes().FirstOrDefault(x => x is BaseEditor); if (editor == null || !(editor is NullEditor)) { _alleltypes ??= CoreUtils.TypeList( AppDomain.CurrentDomain.GetAssemblies(), x => x.IsClass && !x.IsAbstract && !x.IsGenericType && x.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IDynamicEnclosedListGrid<,>) ) ); var subtypes = _alleltypes.Where( x => x.GetInterfaces().Any( i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IDynamicEnclosedListGrid<,>) && i.GenericTypeArguments.First().Equals(type) && i.GenericTypeArguments.Last().Equals(gentype) ) ); IDynamicEditorPage page; if (subtypes.Any()) { page = (Activator.CreateInstance(subtypes.First(), property) as IDynamicEditorPage)!; cache.Add(page); } else { subtypes = _alleltypes.Where(x => x.GetInterfaces().Any(i => i.GenericTypeArguments.Last().Equals(gentype))); if (subtypes.Any()) { var defType = subtypes.First().MakeGenericType(type); page = (Activator.CreateInstance(defType, property) as IDynamicEditorPage)!; cache.Add(page); } else { try { var defType = typeof(DynamicEnclosedListGrid<,>).MakeGenericType(type, gentype); page = (Activator.CreateInstance(defType, property) as IDynamicEditorPage)!; cache.Add(page); } catch (Exception e) { Logger.Send(LogType.Error, "", string.Format("*** Unknown Error: {0}\n{1}", e.Message, e.StackTrace)); } } } } } } } _enclosedlistpages[type] = cache; } pages.AddRange(_enclosedlistpages[type]); } #endregion #region Editor Values public static Dictionary GetValues(BaseObject item, string name, IEnumerable? props = null) { var result = new Dictionary(); props ??= DatabaseSchema.Properties(item.GetType()).Where(x => x.Editor is not NullEditor); foreach (var prop in props) if (prop is CustomProperty) try { result[prop.Name] = item.UserProperties[prop.Name]; } catch (Exception e) { Logger.Send(LogType.Error, "", string.Format("*** Unknown Error: {0}\n{1}", e.Message, e.StackTrace)); } else try { object value; var getter = prop.Getter(); if (getter != null) value = getter.Invoke(item); else value = CoreUtils.GetPropertyValue(item, prop.Name); result[prop.Name] = value; } catch (Exception e) { Logger.Send(LogType.Error, "", string.Format("*** Unknown Error: {0}\n{1}", e.Message, e.StackTrace)); } return result; } public static Dictionary UpdateEditorValue(BaseObject[] items, string name, object value) { Logger.Send(LogType.Information, "", string.Format("DynamicGridUtils.UpdateEditorValue({0},{1},{2})", items.Length, name, value)); var sw = new Stopwatch(); var changes = new Dictionary(); var props = DatabaseSchema.Properties(items.First().GetType()).Where(x => x.Editor is not NullEditor); foreach (var item in items) { //Dictionary previous = new Dictionary(); var previous = GetValues(item, name, props); //if (item.OriginalValues != null) //{ // foreach (var key in item.OriginalValues.Keys) // previous[key] = item.OriginalValues[key]; //} var prop = DatabaseSchema.Property(item.GetType(), name); if (prop is CustomProperty) { if (!item.HasOriginalValue(name)) item.SetOriginalValue(name, item.UserProperties[name]); item.UserProperties[name] = value; } else { if (prop != null) try { var getter = prop.Getter(); var oldvalue = getter != null ? getter.Invoke(item) : CoreUtils.GetPropertyValue(item, name); item.OnPropertyChanged(name, oldvalue, value); var setter = prop.Setter(); if (setter != null && value != null) setter.Invoke(item, value); else CoreUtils.SetPropertyValue(item, name, value); } catch (Exception) { Logger.Send(LogType.Error, "", string.Format("Unable to Set Value for [{0}.{1}] (Value is {2})", item.GetType().Name, name, value)); } } var current = GetValues(item, name, props); foreach (var key in previous.Keys) if (previous[key] == null) { if (current[key] != null) changes[key] = current[key]; } else { if (current[key] == null) changes[key] = current[key]; else if (!object.Equals(previous[key], current[key])) changes[key] = current[key]; } } return changes; } public static void UpdateEditorValue(BaseObject[] items, string name, object value, Dictionary changes) { var results = UpdateEditorValue(items, name, value); foreach (var key in results.Keys) changes[key] = results[key]; } #endregion #region Dynamic Grid Creation public static IDynamicGrid CreateDynamicGrid(Type gridType, Type entityType) { var type = FindDynamicGrid(gridType, entityType); return (Activator.CreateInstance(type) as IDynamicGrid) ?? throw new ArgumentException("Argument must be a type of IDynamicGrid", nameof(gridType)); } private static Dictionary _dynamicGrids = new(); public static Type FindDynamicGrid(Type gridType, Type entityType) { if(!_dynamicGrids.TryGetValue(gridType, out var grids)) { grids = CoreUtils.TypeList( AppDomain.CurrentDomain.GetAssemblies(), myType => myType.IsClass && !myType.IsAbstract && !myType.IsGenericType && CoreUtils.IsSubclassOfRawGeneric(myType, gridType) && !myType.IsAssignableTo(typeof(ISpecificGrid)) ).ToArray(); _dynamicGrids[gridType] = grids; } var entityGrids = grids.Where(x => x.ContainsInheritedGenericType(entityType)).ToList(); var defaults = entityGrids.Where(x => x.IsAssignableTo(typeof(IDefaultGrid))).ToList(); if(defaults.Count > 0) { if(defaults.Count > 1) { Logger.Send(LogType.Information, ClientFactory.UserID, $"Error: {defaults.Count} IDefaultGrid derivations for {gridType.Name} of {entityType.Name}"); } return defaults.First(); } return entityGrids.FirstOrDefault() ?? gridType.MakeGenericType(entityType); } public static Window CreateGridWindow(string title, BaseDynamicGrid dynamicGrid) { dynamicGrid.Margin = new Thickness(5); var window = new ThemableWindow { Title = title, Content = dynamicGrid }; (dynamicGrid as IDynamicGrid)!.Refresh(true, true); return window; } public static Window CreateGridWindow(string title, Type entityType, Type? gridType = null) { gridType ??= typeof(DynamicGrid<>); var grid = CreateDynamicGrid(gridType, entityType) as BaseDynamicGrid; return CreateGridWindow(title, grid!); } public static Window CreateGridWindow(string title) where TEntity : BaseObject where TGrid : IDynamicGrid { return CreateGridWindow(title, typeof(TEntity), typeof(TGrid)); } public static Window CreateGridWindow(string title) where TEntity : BaseObject { return CreateGridWindow(title, typeof(TEntity)); } #endregion public static void PopulateFormMenu(ItemsControl menu, Guid entityID) where TEntityForm : EntityForm, new() where TEntity : Entity where TEntityLink : IEntityLink, new() { var task = Task.Run(() => { return new Client().Query( new Filter(x => x.Parent.ID).IsEqualTo(entityID), null).Rows.Select(x => x.ToObject()).ToList(); }); var manageForms = new MenuItem { Header = "Manage Forms..." }; manageForms.Click += (o, e) => { var window = new ThemableWindow() { Title = $"Manage {typeof(TEntity).Name} Forms" }; var grid = new DynamicEntityFormGrid(entityID); grid.Refresh(true, true); grid.Margin = new Thickness(5); window.Content = grid; window.ShowDialog(); }; menu.Items.Add(new MenuItem() { Header = "Loading...", IsEnabled = false }); menu.Items.Add(new Separator()); menu.Items.Add(manageForms); task.ContinueWith((task) => { var entityForms = task.Result; menu.Items.Clear(); if (entityForms.Any()) { foreach (var entityForm in entityForms) { var description = entityForm.Description; if (string.IsNullOrWhiteSpace(description)) { description = entityForm.Form.Description; } var formItem = new MenuItem { Header = description }; formItem.Click += (o, e) => { if (DynamicFormEditWindow.EditDigitalForm(entityForm, out var dataModel)) { dataModel.Update(null); } }; menu.Items.Add(formItem); } } else { menu.Items.Add(new MenuItem() { Header = "No Forms", IsEnabled = false }); } menu.Items.Add(new Separator()); menu.Items.Add(manageForms); }, TaskScheduler.FromCurrentSynchronizationContext()); } } }