Code/Core/Contexts/ExecutionContext/ExecutionContext.Impls.cs
namespace Nodebox;

public partial class ExecutionContext {
    public HashSet<Type> GenericArguments { get; set; } = [];
    protected virtual HashSet<Type> GenerateAvailableTypes() {
        return [.. TypeLibrary.IntrinsicTypes.Where(x => !x.IsGenericType)];
    }

    public abstract class Implementation {
        public abstract Type Target { get; }
        public virtual bool IsValid => Node.GetAreValidGenerics(Target);
        public abstract void Evaluate(ExecutionContext context, Node node);
    }

    public Dictionary<Type, Implementation> Implementations { get; set; } = [];

    protected virtual Dictionary<Type, Implementation> GenerateImplementations() {
        var implementations = new Dictionary<Type, Implementation>();
        var availableTypes = GenericArguments.ToList();

        foreach (var implementationType in TypeLibrary.GetTypes<Implementation>()) {
            if (implementationType.TargetType == typeof(Implementation)) { continue; }
            if (!implementationType.IsGenericType) {
                var implementation = implementationType.Create<Implementation>([]);
                implementations.Add(implementation.Target, implementation);
                continue;
            }

            var genericArgumentsCount = implementationType.GenericArguments.Length;

            var permuteSource = new List<Type>[genericArgumentsCount];
            foreach (var i in Enumerable.Range(0, genericArgumentsCount)) {
                permuteSource[i] = availableTypes;
            }

            foreach (var typeArgs in Permutations.Calculate(permuteSource)) {
                try {
                    implementationType.MakeGenericType(typeArgs);
                }
                catch (Exception) {
                    continue;
                }

                var implementation = implementationType.CreateGeneric<Implementation>(typeArgs, []);

                if (implementation == null || !implementation.IsValid) {
                    continue;
                }

                implementations.Add(implementation.Target, implementation);
            }
        }

        return implementations;
    }

    public class MissingImplementationException(Node node) : Exception($"Missing evaluation implementation for {DisplayInfo.ForGenericType(node.GetType()).Name}") {
        public Node Node { get; set; } = node;
    }
}