Code/UI/TypePalette.cs
namespace Nodebox.UI;

public class TypePalette : Dictionary<int, TypePalette.Entry> {
    public struct Entry : IMeta {
        public Color Color { get; set; } = Color.Gray;
        public bool Propagate { get; set; } = true;
        public PinShape Shape { get; set; } = PinShape.RoundedBox;
        public Dictionary<string, object> Meta { get; set; } = [];

        public Entry() { }

        public Entry(Color color) : this() {
            Color = color;
        }

        public Entry(Color color, PinShape pinShape) : this(color) {
            Shape = pinShape;
        }

        public Entry(Color color, bool propagate) : this(color) {
            Propagate = propagate;
        }

        public Entry(Color color, bool propagate, PinShape pinShape) : this(color, pinShape) {
            Propagate = propagate;
        }
    }

    [JsonInclude]
    public Entry Fallback { get; set; } = default;
    [JsonInclude]
    public Entry? Class { get; set; } = null;
    [JsonInclude]
    public Entry? Enum { get; set; } = null;
    [JsonInclude]
    public Entry? Struct { get; set; } = null;

    public TypePalette() : base() { }

    public void Add<T>(Entry entry) => Add(typeof(T), entry);
    public void Add(Type type, Entry entry) => Add(TypeLibrary.GetType(type).Identity, entry);

    public void Remove<T>() => Remove(typeof(T));
    public void Remove(Type type) => Remove(TypeLibrary.GetType(type).Identity);

    public Entry Get<T>() => Get(typeof(T));
    public virtual Entry Get(Type type) {
        var identity = TypeLibrary.GetType(type).Identity;
        if (TryGetValue(identity, out var entry)) {
            return entry;
        }

        foreach (var pair in this) {
            var typeDescription = TypeLibrary.GetTypeByIdent(pair.Key);
            if (type == typeDescription.TargetType) {
                return pair.Value;
            }

            if (pair.Value.Propagate && type.IsAssignableTo(typeDescription.TargetType)) {
                return pair.Value;
            }

            if (TypeLibrary.GetType(type).TargetType == typeDescription.TargetType) {
                return pair.Value;
            }
        }

        if (Class is Entry classEntry && TypeLibrary.GetType(type).IsClass) {
            return classEntry;
        }

        if (Enum is Entry enumEntry && TypeLibrary.GetType(type).IsEnum) {
            return enumEntry;
        }

        if (Struct is Entry structEntry && TypeLibrary.GetType(type).IsValueType) {
            return structEntry;
        }

        return Fallback;
    }


    public static TypePalette DefaultForExecutionContext() {
        var palette = new TypePalette {
            Class = new(Color.Parse("#33CCEE").Value),
            Struct = new(Color.Parse("#6B6BCC").Value),
            Enum = new(Color.Parse("#006F6F").Value),
        };

        palette.Add<object>(new(Color.Parse("#999999").Value, false));

        palette.Add<Call>(new(Color.Parse("#FFFFFF").Value, PinShape.Arrow));

        palette.Add<bool>(new(Color.Parse("#EE4444").Value));

        palette.Add<float>(new(Color.Parse("#44FF00").Value));
        palette.Add<double>(new(Color.Parse("#BBFF00").Value));

        palette.Add<int>(new(Color.Parse("#22EEBB").Value));
        palette.Add<long>(new(Color.Parse("#20EECF").Value));

        palette.Add<uint>(new(Color.Parse("#55BB88").Value));
        palette.Add<ulong>(new(Color.Parse("#33BB99").Value));

        palette.Add<byte>(new(Color.Parse("#9dbdbb").Value)); // Also PinIndex

        palette.Add<string>(new(Color.Parse("#DD8855").Value));
        palette.Add<char>(new(Color.Parse("#AA6644").Value));

        palette.Add<Vector2>(new(Color.Parse("#D9B911").Value));
        palette.Add<Vector3>(new(Color.Parse("#F0CC22").Value));
        palette.Add<Vector4>(new(Color.Parse("#FFEE55").Value));

        palette.Add<Vector2Int>(new(Color.Parse("#895FB1").Value));
        palette.Add<Vector3Int>(new(Color.Parse("#A16FCA").Value));

        palette.Add<Angles>(new(Color.Parse("#8292c9ff").Value));
        palette.Add<Rotation>(new(Color.Parse("#A1A2EC").Value));

        palette.Add<Color>(new(Color.Parse("#d16c9bff").Value));

        palette.Add<Reference>(new(Color.Parse("#b1ac7fff").Value));

        return palette;
    }
}