namespace Pilz.Data;

#pragma warning disable CA2231

[Serializable]
public struct PropertyObject<T>(T value) where T : class
{
    private readonly bool hasValue = true; // Do not rename (binary serialization)
    internal T? value = value; // Do not rename (binary serialization) or make readonly (can be mutated in ToString, etc.)

    public static PropertyObject<T> Empty => default;

    public readonly bool HasValue
    {
        get => hasValue;
    }

    public readonly T? Value
    {
        get
        {
            if (!hasValue)
                throw new InvalidOperationException("No value");
            return value;
        }
    }

    public readonly T? GetValueOrDefault() => value;

    public readonly T? GetValueOrDefault(T defaultValue) =>
        hasValue ? value : defaultValue;

    public override readonly bool Equals(object? other)
    {
        if (!hasValue || value is null) return other == null;
        if (other == null) return false;
        return value.Equals(other);
    }

    public override readonly int GetHashCode() => hasValue && value is not null ? value.GetHashCode() : 0;

    public override readonly string? ToString() => hasValue && value is not null ? value.ToString() : string.Empty;

    public static implicit operator PropertyObject<T>(T value) => new(value);

    public static explicit operator T?(PropertyObject<T> value) => value.Value;
}