Aragas/Bannerlord.MBOptionScreen

View on GitHub
src/MCM.UI/Extensions/SettingsBuilderExtensions.cs

Summary

Maintainability
B
4 hrs
Test Coverage
using Bannerlord.ButterLib.Common.Extensions;
using Bannerlord.ButterLib.SubSystems;
using Bannerlord.ButterLib.SubSystems.Settings;

using HarmonyLib.BUTR.Extensions;

using MCM.Abstractions.FluentBuilder;
using MCM.Common;

using Microsoft.Extensions.DependencyInjection;

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

using TaleWorlds.Core;
using TaleWorlds.Localization;

namespace MCM.UI.Extensions
{
    internal static class SettingsBuilderExtensions
    {
        private static PropertyInfo? GetPropertyInfo<T, TResult>(Expression<Func<T, TResult>> expression) =>
            expression is LambdaExpression { Body: MemberExpression { Member: PropertyInfo propertyInfo } } ? propertyInfo : null;

        private static void AddForSubSystem<TSubSystem>(TSubSystem subSystem, ISettingsBuilder settingsBuilder) where TSubSystem : ISubSystem
        {
            if (subSystem is not ISubSystemSettings<TSubSystem> settings)
                return;

            foreach (var declaration in settings.Declarations)
            {
                settingsBuilder.CreateGroup($"SubSystem {subSystem.Id}", groupBuilder =>
                {
                    _ = declaration switch
                    {
                        SubSystemSettingsPropertyBool<TSubSystem> sp when GetPropertyInfo(sp.Property) is { } pi =>
                            groupBuilder.AddBool($"{subSystem.GetType().Name}_{pi.Name}", new TextObject(sp.Name).ToString(), new PropertyRef(pi, subSystem), builder =>
                            {
                                builder.SetHintText(sp.Description);
                            }),
                        SubSystemSettingsPropertyDropdown<TSubSystem> sp when GetPropertyInfo(sp.Property) is { } pi =>
                            groupBuilder.AddDropdown($"{subSystem.GetType().Name}_{pi.Name}", new TextObject(sp.Name).ToString(), sp.SelectedIndex, new PropertyRef(pi, subSystem), builder =>
                            {
                                builder.SetHintText(sp.Description);
                            }),
                        SubSystemSettingsPropertyFloat<TSubSystem> sp when GetPropertyInfo(sp.Property) is { } pi =>
                            groupBuilder.AddFloatingInteger($"{subSystem.GetType().Name}_{pi.Name}", new TextObject(sp.Name).ToString(), sp.MinValue, sp.MaxValue, new PropertyRef(pi, subSystem), builder =>
                            {
                                builder.SetHintText(sp.Description);
                            }),
                        SubSystemSettingsPropertyInt<TSubSystem> sp when GetPropertyInfo(sp.Property) is { } pi =>
                            groupBuilder.AddInteger($"{subSystem.GetType().Name}_{pi.Name}", new TextObject(sp.Name).ToString(), sp.MinValue, sp.MaxValue, new PropertyRef(pi, subSystem), builder =>
                            {
                                builder.SetHintText(sp.Description);
                            }),
                        SubSystemSettingsPropertyText<TSubSystem> sp when GetPropertyInfo(sp.Property) is { } pi =>
                            groupBuilder.AddText($"{subSystem.GetType().Name}_{pi.Name}", new TextObject(sp.Name).ToString(), new PropertyRef(pi, subSystem), builder =>
                            {
                                builder.SetHintText(sp.Description);
                            }),
                    };
                });
            }
        }

        public static ISettingsBuilder AddButterLibSubSystems(this ISettingsBuilder settings)
        {
            foreach (var subSystem in DependencyInjectionExtensions.GetServiceProvider((null as Game)!).GetRequiredService<IEnumerable<ISubSystem>>())
            {
                if (!subSystem.CanBeDisabled)
                    continue;

                var prop = new ProxyRef<bool>(() => subSystem.IsEnabled, state =>
                {
                    if (state)
                        subSystem.Enable();
                    else
                        subSystem.Disable();
                });
                settings = settings.CreateGroup($"SubSystem {subSystem.Id}", builder => builder.AddBool($"{subSystem.Id} Enabled", "Enabled", prop,
                    bBuilder => bBuilder.SetHintText(subSystem.Description).SetRequireRestart(!subSystem.CanBeSwitchedAtRuntime)));

                var subSystemType = subSystem.GetType();
                if (subSystemType.GetInterfaces().Any(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(ISubSystemSettings<>)))
                {
                    var method = AccessTools2.DeclaredMethod(typeof(SettingsBuilderExtensions), "AddForSubSystem")?.MakeGenericMethod(subSystemType);
                    method?.Invoke(null, [subSystem, settings]);
                }
            }
            return settings;
        }
    }
}