using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; using Qrakhen.Qamp.Core.Abstractions; using Qrakhen.Qamp.Core.Values.Objects; using String = Qrakhen.Qamp.Core.Values.Objects.String; using T = Qrakhen.Qamp.Core.Values.ValueType; namespace Qrakhen.Qamp.Core.Values; public interface IValue { ValueType ValueType { get; } } [StructLayout(LayoutKind.Explicit, Size = 0x10)] public readonly struct Value : IValue, ISerialize, IDebug { public static readonly Value Void = new Value(); public static readonly Value True = new Value(true); public static readonly Value False = new Value(false); [FieldOffset(0x00)] [Serialized] public readonly ulong Unsigned; [FieldOffset(0x00)] [Serialized] public readonly long Signed; [FieldOffset(0x00)] [Serialized] public readonly bool Bool; [FieldOffset(0x00)] [Serialized] public readonly char Char; [FieldOffset(0x00)] [Serialized] public readonly double Decimal; [FieldOffset(0x00)] [Serialized] public readonly Address Address; [FieldOffset(0x00)] [Serialized] public readonly Ptr Ptr; [FieldOffset(0x00)] [Serialized] public readonly Ref Ref; [FieldOffset(0x08)] [Serialized] public readonly T Type; [FieldOffset(0x0c)] [Serialized] public readonly uint Meta; // like lengths of arrays or lists private Value(T type) => Type = type; public Value() : this(T.Void) { } public Value(ulong unsigned) : this(T.Unsigned) => Unsigned = unsigned; public Value(long signed) : this(T.Signed) => Signed = signed; public Value(char character) : this(T.Char) => Char = character; public Value(bool boolean) : this(T.Bool) => Bool = boolean; public Value(double @decimal) : this(T.Decimal) => Decimal = @decimal; public Value(Address address) : this(T.Address) => Address = address; public Value(Ptr ptr) : this(ptr.Value?.Type ?? T.Pointer) => Ptr = ptr; public Value(Ref @ref) : this(T.Reference) => Ref = @ref; public dynamic? Dynamic => AsDynamic(); public T ValueType => Type; public bool IsNumber => Type <= T.Decimal; public bool IsSigned => Is(T.Signed); public bool IsUnsigned => Is(T.Unsigned); public bool IsDecimal => Is(T.Decimal); public bool IsBool => Is(T.Bool); public bool IsString => Is(T.String); public bool IsRef => Is(T.Reference, false); public bool IsObj => Is(T.Object, false); public bool IsFalsy => IsBool ? Bool == false : Unsigned == 0; public bool Is(T type, bool exact = true) { return exact ? (Type & type) == type : (Type & type) > 0; } private unsafe dynamic? AsDynamic(bool throwWhenNull = true) { if (IsString) return Ptr.As()!.Value; if (IsObj) return Ptr.Value; return Type switch { T.Void => throwWhenNull ? throw new NullReferenceException() : null, T.Unsigned => Unsigned, T.Signed => Signed, T.Decimal => Decimal, T.Bool => Bool, T.Address => Address, T.Reference => (Value)(*Ref.Value), _ => throwWhenNull ? throw new NullReferenceException() : null, }; } public static Value FromAny(object? any = null) { if (any == null) return new Value(); Type type = any.GetType(); return new Value(); } public void __gcMark() { if (IsObj && Ptr.Value != null) { } } public override bool Equals([NotNullWhen(true)] object? obj) { return obj switch { Value value when Type == value.Type => Unsigned == value.Unsigned, Value value => Dynamic?.Equals(value.Dynamic), _ => false }; } public override int GetHashCode() => Unsigned.GetHashCode(); public override string ToString() => ToString(false); public string ToString(bool includeType) { if (!includeType) return $"{AsDynamic(false)}"; string type = Type.ToString(); if (Type.HasFlag(T.Pointer)) type = Ptr.Value?.Type.ToString() ?? "NullPtr"; return $"<{type}, {AsDynamic(false) ?? "null"}>"; } public string Debug(DebugLevel level = DebugLevel.None) { if (level < DebugLevel.Strong) return Debugger.GetContextString(this); string str = $"{Debugger.GetContextString(this)}\n"; string[] lines = [ $"{nameof(Meta)}: {Meta}", $"{nameof(Unsigned)}: {Unsigned}", $"{nameof(Signed)}: {Signed}", $"{nameof(Bool)}: {Bool}", $"{nameof(Char)}: {Char}", $"{nameof(Decimal)}: {Decimal}", $"{nameof(Address)}: {Address}", $"{nameof(Ref)}: {Ref}", $"{nameof(Ptr)}: {Ptr}" ]; return str + string.Join("\n - ", lines); } public byte[] Serialize() { List result = [.. ((ushort)Type).GetBytes() ]; byte[] bytes = Signed.GetBytes(); result.Add((byte)bytes.Length); result.AddRange(bytes); result.Add(0x7F); return result.ToArray(); } public static Value Deserialize(byte[] data) { return Void; } } public class SerializedAttribute : Attribute;