MehCode/Core/Reference.cs
namespace Nodebox;


public class Reference : IEquatable<Reference> {
    public Reference() {
        Target = null;
        PropertyName = null;
    }

    public Reference(object target, PropertyDescription propertyDescription) {
        Target = target;
        PropertyName = propertyDescription.Name;
    }
    
    public Reference(object target, string propertyName) {
        Target = target;
        PropertyName = propertyName;
    }

    private object _target;
    private string _propertyName;
    private PropertyDescription _propertyDescription;

    public object Target {
		get => _target; set {
        _target = value;
        UpdatePropertyDescription();
    } }

    public string PropertyName {
		get => _propertyName; set {
        _propertyName = value;
        UpdatePropertyDescription();
    } }

    public PropertyDescription PropertyDescription => _propertyDescription;
    public Type PropertyType => PropertyDescription.PropertyType;


    public bool CanWrite => PropertyDescription.CanWrite;
    public bool CanRead => PropertyDescription.CanRead;

    public bool Write(object value) => Write<object>(value);
    public bool Write<T>(T value) {
        if (!IsValid()) return false;
        if (!CanWrite) return false;
        PropertyDescription.SetValue(Target, value);
        return true;
    }

    public bool TryRead(out object value) => TryRead<object>(out value);
    public bool TryRead<T>(out T value) {
        value = default;
        if (!IsValid()) return false;
        if (!CanRead) return false;
        value = Read<T>();
        return true;
    }

    public object Read() => PropertyDescription.GetValue(Target);
    public T Read<T>() => (T)PropertyDescription.GetValue(Target);

    public virtual bool IsValid() {
        return Target != null &&
            PropertyDescription != null &&
            PropertyDescription.TypeDescription.TargetType == Target.GetType();
    }

    private void UpdatePropertyDescription() {
        _propertyDescription = TypeLibrary.GetType(_target.GetType()).GetProperty(_propertyName);
    }

	public override bool Equals(object obj) {
        if (obj is Reference reference) {
            return Equals(reference);
        }
        
        return false;
	}

	public override int GetHashCode() {
        return HashCode.Combine(Target, PropertyDescription);
	}

	public override string ToString() => $"{PropertyDescription?.Name} on {Target?.ToString() ?? "null"}";

	public bool Equals(Reference other) => Target == other.Target && PropertyDescription.Name == other.PropertyDescription.Name;
}

public class Reference<T> : Reference {
    public Reference() {
        Target = null;
        PropertyName = null;
    }

    public Reference(object target, PropertyDescription propertyDescription) : base(target, propertyDescription) { }
    public Reference(object target, string propertyName) : base(target, propertyName) { }
    
    public Type Type { get; private set; } = typeof(T);

	public override bool IsValid() {
		return base.IsValid() && Type == PropertyType;
	}

	public bool Write(T value) => Write<T>(value);

    public bool TryRead(out T value) => TryRead<T>(out value);

    public new T Read() => Read<T>();
}