using InABox.Configuration; using InABox.Core.Postable; using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Runtime; using System.Text; namespace InABox.Core { public static class PosterUtils { #region Postable Settings private static PostableSettings FixPostableSettings(Type TPostable, PostableSettings settings) { if (string.IsNullOrWhiteSpace(settings.PostableType)) { settings.PostableType = TPostable.EntityName(); } return settings; } private static PostableSettings LoadPostableSettings(Type T) { return FixPostableSettings(T, new GlobalConfiguration(T.Name).Load()); } public static PostableSettings LoadPostableSettings() where T : Entity, IPostable { return LoadPostableSettings(typeof(T)); } public static void SavePostableSettings(PostableSettings settings) where T : Entity, IPostable { new GlobalConfiguration(typeof(T).Name).Save(FixPostableSettings(typeof(T), settings)); } #endregion #region Poster Settings private static TSettings FixPosterSettings(TSettings settings) where TPostable : IPostable where TSettings : PosterSettings, new() { if (string.IsNullOrWhiteSpace(settings.PostableType)) { settings.PostableType = typeof(TPostable).EntityName(); } return settings; } private static MethodInfo _loadPosterSettingsMethod = typeof(PosterUtils).GetMethods(BindingFlags.Static | BindingFlags.Public) .Where(x => x.Name == nameof(LoadPosterSettings) && x.IsGenericMethod) .Single(); private static MethodInfo _savePosterSettingsMethod = typeof(PosterUtils).GetMethods(BindingFlags.Static | BindingFlags.Public) .Where(x => x.Name == nameof(SavePosterSettings) && x.IsGenericMethod) .Single(); public static TSettings LoadPosterSettings() where TPostable : IPostable where TSettings : PosterSettings, new() { return FixPosterSettings(new GlobalConfiguration(typeof(TPostable).Name).Load()); } public static PosterSettings LoadPosterSettings(Type TPostable, Type TSettings) { return (_loadPosterSettingsMethod.MakeGenericMethod(TPostable, TSettings).Invoke(null, Array.Empty()) as PosterSettings)!; } public static void SavePosterSettings(TSettings settings) where TPostable : IPostable where TSettings : PosterSettings, new() { new GlobalConfiguration(typeof(TPostable).Name).Save(FixPosterSettings(settings)); } public static void SavePosterSettings(Type TPostable, Type TSettings, PosterSettings settings) { _savePosterSettingsMethod.MakeGenericMethod(TPostable, TSettings).Invoke(null, new object[] { settings }); } #endregion #region Global Poster Settings private static MethodInfo _loadGlobalPosterSettingsMethod = typeof(PosterUtils).GetMethods(BindingFlags.Static | BindingFlags.Public) .Where(x => x.Name == nameof(LoadGlobalPosterSettings) && x.IsGenericMethod) .Single(); private static MethodInfo _saveGlobalPosterSettingsMethod = typeof(PosterUtils).GetMethods(BindingFlags.Static | BindingFlags.Public) .Where(x => x.Name == nameof(SaveGlobalPosterSettings) && x.IsGenericMethod) .Single(); public static IGlobalPosterSettings LoadGlobalPosterSettings(Type TGlobalSettings) { return (_loadGlobalPosterSettingsMethod.MakeGenericMethod(TGlobalSettings).Invoke(null, Array.Empty()) as IGlobalPosterSettings)!; } public static TGlobalSettings LoadGlobalPosterSettings() where TGlobalSettings : BaseObject, IGlobalPosterSettings, new() { return new GlobalConfiguration().Load(); } public static void SaveGlobalPosterSettings(TGlobalSettings settings) where TGlobalSettings : BaseObject, IGlobalPosterSettings, new() { new GlobalConfiguration().Save(settings); } public static void SaveGlobalPosterSettings(Type TGlobalSettings, IGlobalPosterSettings settings) { _saveGlobalPosterSettingsMethod.MakeGenericMethod(TGlobalSettings).Invoke(null, new object[] { settings }); } #endregion #region Poster Engines private class EngineType { public Type Engine { get; set; } public Type Entity { get; set; } public Type Poster { get; set; } } private static EngineType[]? _posterEngines; private static Type[]? _posters = null; public static Type[] GetPosters() { _posters ??= CoreUtils.Entities.Where(x => x.IsClass && !x.IsGenericType && x.HasInterface(typeof(IPoster<,>))).ToArray(); return _posters; } public static Type GetPoster(Type TPoster) { return GetPosters().Where(x => TPoster.IsAssignableFrom(x)).FirstOrDefault() ?? throw new Exception($"No poster of type {TPoster}."); } public static Type? GetPoster(string posterType) { return GetPosters()?.FirstOrDefault(x => x.EntityName() == posterType)!; } private static EngineType[] GetPosterEngines() { _posterEngines ??= CoreUtils.TypeList( AppDomain.CurrentDomain.GetAssemblies(), x => x.IsClass && !x.IsAbstract && x.GetTypeInfo().GenericTypeParameters.Length == 1 && x.HasInterface(typeof(IPosterEngine<,,>)) ).Select(x => { var poster = x.GetInterfaceDefinition(typeof(IPosterEngine<,,>))!.GenericTypeArguments[1]; return new EngineType { Engine = x, Entity = x.GetInterfaceDefinition(typeof(IPosterEngine<,,>))!.GenericTypeArguments[0], Poster = poster.IsGenericType ? poster.GetGenericTypeDefinition() : poster }; }).ToArray(); return _posterEngines; } /// /// Get the for /// based on the current for . /// /// public static Result GetEngine(Type T) { var settings = LoadPostableSettings(T); if (string.IsNullOrWhiteSpace(settings.PosterType)) { return Result.Error(new MissingSettingsException(T)); } var poster = GetPoster(settings.PosterType); if(poster is null) { return Result.Error(new Exception($"No poster of type {settings.PosterType}.")); } var engines = GetPosterEngines().Where(x => { return x.Poster.IsInterface ? poster.HasInterface(x.Poster) : poster.IsSubclassOfRawGeneric(x.Poster); }).ToList(); if (!engines.Any()) { return Result.Error(new Exception("No poster for the given settings")); } else if(engines.Count == 1) { return Result.Ok(engines[0].Engine.MakeGenericType(T)); } else { return Result.Ok(engines.Single(x => x.Entity == T).Engine.MakeGenericType(T)); } } /// /// Get the for /// based on the current for . /// /// /// public static Type GetEngine() where T : Entity, IPostable, IRemotable, IPersistent, new() { if (GetEngine(typeof(T)).Get(out var engineType, out var e)) { return engineType; } else { throw e; } } public static IPosterEngine CreateEngine() where T : Entity, IPostable, IRemotable, IPersistent, new() { var engine = GetEngine(); return (Activator.CreateInstance(engine) as IPosterEngine)!; } #endregion #region Auto-Refresh /// /// Check if needs to be reposted; if it does, will be set to /// once this function is finished. /// /// /// This will only do something if the currently set poster for this type has the interface. /// /// /// if the item's status has been updated and needs to be reposted. public static bool CheckPostedStatus(PostableSettings settings, T item) where T : BaseObject { if (!(item is IPostable postable)) { return false; } if (postable.PostedStatus != PostedStatus.Posted || item?.HasOriginalValue(nameof(IPostable.PostedStatus)) == true) { return false; } settings = FixPostableSettings(typeof(T), settings); if (settings.PosterType.IsNullOrWhiteSpace()) { return false; } var poster = GetPoster(settings.PosterType); if (poster is null) { return false; } var iautoRefresh = poster.GetInterfaceDefinition(typeof(IAutoRefreshPoster<,>)); if(iautoRefresh != null) { var autoRefresher = Activator.CreateInstance(iautoRefresh.GenericTypeArguments[1]) as IAutoRefresher; if (autoRefresher != null && autoRefresher.ShouldRepost(item!)) { postable.PostedStatus = PostedStatus.RequiresRepost; return true; } } return false; } #endregion #region Process /// /// Process with the currently set /// for . /// /// The type of that needs to be processed. /// /// If there are no items to post. In this case, nothing happens. /// If any of the have already been processed. In this case, nothing happens. /// If the for do not exist. /// If the post has been cancelled by the user. /// if post was unsuccessful. /// public static IPostResult? Process(IDataModel model) where T : Entity, IPostable, IRemotable, IPersistent, new() { return CreateEngine().Process(model); } /// /// Import with the currently set . /// /// /// if post was unsuccessful. /// If the engine is not a public static IPullResult? Pull() where T : Entity, IPostable, IRemotable, IPersistent, new() { var engine = CreateEngine(); if(engine is IPullerEngine puller) { return puller.Pull(); } else { var intDef = engine.GetType().GetInterfaceDefinition(typeof(IPosterEngine<,,>)); if(intDef != null) { throw new InvalidPullerException(typeof(T), intDef.GenericTypeArguments[1]); } else { throw new InvalidPullerException(typeof(T)); } } } #endregion } }