using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Linq.Expressions; namespace InABox.Core { public interface ISubObject { BaseObject GetLinkedParent(); void SetLinkedParent(BaseObject obj); string GetLinkedPath(); void SetLinkedPath(string path); } public class SubObjectOriginalValues : IOriginalValues { private ISubObject Object { get; set; } private IOriginalValues? _rootDictionary; private IOriginalValues RootDictionary { get { _rootDictionary ??= LinkedProperties.GetParent(Object).OriginalValueList; return _rootDictionary; } } private string? _rootPath; /// /// Path of this sub object according to the root, suffixed with a "." /// private string RootPath { get { _rootPath ??= $"{LinkedProperties.GetPath(Object)}."; return _rootPath; } } public SubObjectOriginalValues(ISubObject obj) { Object = obj; } private string ChangeKey(string key) => $"{RootPath}{key}"; public bool ContainsKey(string key) { return RootDictionary.ContainsKey(ChangeKey(key)); } public void Clear() { foreach(var (k, v) in RootDictionary) { if (k.StartsWith(RootPath)) { RootDictionary.Remove(k); } } } public void Remove(string key) { RootDictionary.Remove(ChangeKey(key)); } public bool TryAdd(string key, object? value) { return RootDictionary.TryAdd(ChangeKey(key), value); } public bool TryGetValue(string key, out object? value) { return RootDictionary.TryGetValue(ChangeKey(key), out value); } public IEnumerator> GetEnumerator() { return RootDictionary.Where(x => x.Key.StartsWith(RootPath)).Select(x => new KeyValuePair(x.Key[RootPath.Length..], x.Value)).GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); public object? this[string key] { get => RootDictionary[ChangeKey(key)]; set => RootDictionary[ChangeKey(key)] = value; } } public class SubObjectLoadedColumns : ILoadedColumns { private ISubObject Object { get; set; } private ILoadedColumns? _rootSet; private ILoadedColumns RootSet { get { _rootSet ??= LinkedProperties.GetParent(Object).LoadedColumns; return _rootSet; } } private string? _rootPath; /// /// Path of this sub object according to the root, suffixed with a "." /// private string RootPath { get { _rootPath ??= $"{LinkedProperties.GetPath(Object)}."; return _rootPath; } } public SubObjectLoadedColumns(ISubObject obj) { Object = obj; } private string ChangeKey(string key) => $"{RootPath}{key}"; public bool Add(string key) { return RootSet.Add(ChangeKey(key)); } public bool Contains(string key) { return RootSet.Contains(ChangeKey(key)); } public IEnumerator GetEnumerator() { return RootSet.Where(x => x.StartsWith(RootPath)).Select(x => x[RootPath.Length..]).GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } public static class LinkedProperties { private static readonly Dictionary> _LinkedProperties = new Dictionary>(); public static void Register(Expression> path, Expression> source, Expression> target, Func>? func = null) where TLinkedEntity : class where TEntityLink : class { var map = new LinkedProperty(path, source, target, func); if(!_LinkedProperties.TryGetValue(map.Type, out var props)) { props = new List(); _LinkedProperties[map.Type] = props; } if(!props.Any(x => x.Equals(map))) { props.Add(map); } } /*public static IEnumerable Find(object parent, object path) { var all = _LinkedProperties.Where(x => x.Type == parent.GetType()); var filtered = all.Where(x => CoreUtils.GetPropertyValue(parent,x.Path)?.GetType() == path?.GetType()); return filtered; }*/ public static BaseObject GetParent(ISubObject link) { while (true) { var parent = link.GetLinkedParent(); if(parent is ISubObject parentLink) { link = parentLink; } else { return parent; } } } public static string GetPath(ISubObject link) { var path = link.GetLinkedPath(); while (true) { var parent = link.GetLinkedParent(); if (parent is ISubObject parentLink) { link = parentLink; path = $"{link.GetLinkedPath()}.{path}"; } else { return path; } } } public static IEnumerable FindAll(Type parent) { if (_LinkedProperties.TryGetValue(parent, out var props)) { return props; } return Enumerable.Empty(); } public static IEnumerable Find(ISubObject subObject, out BaseObject parent) { parent = GetParent(subObject); if (!_LinkedProperties.TryGetValue(parent.GetType(), out var props)) { return Enumerable.Empty(); } var path = GetPath(subObject); return props.Where(x => x.Path == path); } public static bool Find(ISubObject subObject, string name, [NotNullWhen(true)] out ILinkedProperty? property, out BaseObject parent) { property = null; parent = GetParent(subObject); if (parent == null) return false; if(!_LinkedProperties.TryGetValue(parent.GetType(), out var props)) return false; var path = GetPath(subObject); property = props.FirstOrDefault(x => string.Equals( $"{x.Path}{(x.Path.IsNullOrWhiteSpace() ? "" : ".")}{x.Source}", $"{path}{(path.IsNullOrWhiteSpace() ? "" : ".")}{name}")); return property != null; } } }