using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; using PkmnLib.Static.Utils; namespace PkmnLib.Dynamic.ScriptHandling; /// /// Interface for custom trigger arguments. /// public interface ICustomTriggerArgs; /// /// Static helper methods for working with . /// /// This class provides methods to get and set properties on custom trigger arguments. This allows for plugins and scripts /// to modify the behavior of custom triggers without needing to know the specific types of the arguments, which means /// they do not need to reference each other directly. /// public static class CustomTriggerArgsHelpers { private record TypeFuncs( Dictionary> Getters, Dictionary> Setters); private static readonly Dictionary TypeFuncInfo = new(); private static void PopulateForType(Type type) { if (TypeFuncInfo.ContainsKey(type)) return; var getters = type.GetProperties().Where(p => p.CanRead && p.GetIndexParameters().Length == 0).ToDictionary( p => new StringKey(p.Name), p => { var parameter = Expression.Parameter(typeof(ICustomTriggerArgs), "args"); var typeCast = Expression.Convert(parameter, type); var propertyAccess = Expression.Property(typeCast, p); var convert = Expression.Convert(propertyAccess, typeof(object)); return Expression.Lambda>(convert, parameter).Compile(); }); var setters = type.GetProperties().Where(p => p.CanWrite && p.GetIndexParameters().Length == 0).ToDictionary( p => new StringKey(p.Name), p => { var parameter = Expression.Parameter(typeof(ICustomTriggerArgs), "args"); var typeCast = Expression.Convert(parameter, type); var valueParameter = Expression.Parameter(typeof(object), "value"); var propertyAccess = Expression.Property(typeCast, p); var convert = Expression.Convert(valueParameter, p.PropertyType); var assign = Expression.Assign(propertyAccess, convert); return Expression.Lambda>(assign, parameter, valueParameter) .Compile(); }); TypeFuncInfo[type] = new TypeFuncs(getters, setters); } private static bool TryGetGetter(Type type, StringKey key, [NotNullWhen(true)] out Func? getter) { if (!TypeFuncInfo.ContainsKey(type)) PopulateForType(type); return TypeFuncInfo[type].Getters.TryGetValue(key, out getter); } private static bool TryGetSetter(Type type, StringKey key, [NotNullWhen(true)] out Action? setter) { if (!TypeFuncInfo.ContainsKey(type)) PopulateForType(type); return TypeFuncInfo[type].Setters.TryGetValue(key, out setter); } public static T Get(this ICustomTriggerArgs args, StringKey key) { if (args is null) throw new ArgumentNullException(nameof(args)); var type = args.GetType(); if (!TryGetGetter(type, key, out var getter)) throw new KeyNotFoundException($"Key '{key}' not found in {type.Name}."); var value = getter(args); if (value is T typedValue) return typedValue; throw new InvalidCastException( $"Value for key '{key}' in {type.Name} is of type {value?.GetType().Name}, expected {typeof(T).Name}."); } public static object? Get(this ICustomTriggerArgs args, StringKey key) { if (args is null) throw new ArgumentNullException(nameof(args)); var type = args.GetType(); if (!TryGetGetter(type, key, out var getter)) throw new KeyNotFoundException($"Key '{key}' not found in {type.Name}."); return getter(args); } public static bool TryGet(this ICustomTriggerArgs args, StringKey key, [NotNullWhen(true)] out T? value) { if (args is null) throw new ArgumentNullException(nameof(args)); var type = args.GetType(); if (!TryGetGetter(type, key, out var getter)) { value = default; return false; } var result = getter(args); if (result is T typedValue) { value = typedValue; return true; } value = default; return false; } public static void Set(this ICustomTriggerArgs args, StringKey key, object? value) { if (args is null) throw new ArgumentNullException(nameof(args)); var type = args.GetType(); if (!TryGetSetter(type, key, out var setter)) throw new KeyNotFoundException($"Key '{key}' not found in {type.Name}."); setter(args, value); } }