Code/Util/Math/UnaryOperator.cs
namespace Nodebox.Util.Math;

public abstract class UnaryOperator {
    public static Dictionary<Type, UnaryOperator> LazyStore { get; set; } = [];
    public static T Get<T>() where T : UnaryOperator, new() {
        if (LazyStore.TryGetValue(typeof(T), out var op)) {
            return (T)op;
        }

        var instance = new T();
        LazyStore[typeof(T)] = instance;
        return instance;
    }

    public Dictionary<Type, Func<object, object>> Lookup;
    public UnaryOperator() {
        Lookup = GenerateLookup();
    }

    public virtual Dictionary<Type, Func<object, object>> GenerateLookup() {
        var lookup = BaseLookup;
        var seq = TypeLibrary.GetMethodsWithAttribute<ModifierAttribute>().OrderBy(x => x.Method.GetCustomAttribute<OrderAttribute>()?.Value ?? 0);
        foreach (var (func, attr) in seq) {
            func.Invoke(null, [attr.Type, lookup]);
        }

        return lookup;
    }

    public abstract Dictionary<Type, Func<object, object>> BaseLookup { get; }
    public object Apply(object a) => Lookup[a.GetType()](a);
    public bool IsValid<T>() => Lookup.ContainsKey(typeof(T));
    public bool IsValid(Type a) => Lookup.ContainsKey(a);
    public bool IsValid(object a) => IsValid(a.GetType());


    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
    public class ModifierAttribute(Type type) : Attribute {
        public Type Type { get; set; } = type;
    }

    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
    public class ModifierAttribute<T> : ModifierAttribute {
        public ModifierAttribute() : base(typeof(T)) { }
    }
}


public static class UnaryOperatorExtensions {
    extension<T>(T unaryOperator) where T : UnaryOperator, new() {
        public static T Global => UnaryOperator.Get<T>();
    }
}