using System.Collections.Generic;
using System.Numerics;
namespace AcDream.App.UI.Layout;
///
/// Dat property value kinds carried by a retail StateDesc. The enum mirrors
/// BasePropertyType without exposing DatReaderWriter types to the retained UI.
///
public enum UiPropertyKind : byte
{
Enum,
Bool,
DataId,
Float,
Integer,
StringInfo,
Color,
Array,
Struct,
Vector,
Bitfield32,
Bitfield64,
InstanceId,
}
/// Exact byte representation of a retail ARGB property value.
public readonly record struct UiColorValue(byte Blue, byte Green, byte Red, byte Alpha);
/// Dat-independent snapshot of a retail StringInfo.
public readonly record struct UiStringInfoValue(
byte Token,
uint StringId,
uint TableId,
byte Override,
byte English,
byte Comment);
///
/// Lossless, serializable representation of one supported retail base property.
/// Only the field selected by is meaningful. Arrays and structs
/// recursively preserve their members and each member's master-property id.
///
public sealed class UiPropertyValue
{
public UiPropertyKind Kind;
public uint MasterPropertyId;
public ulong UnsignedValue;
public int IntegerValue;
public float FloatValue;
public bool BoolValue;
public UiStringInfoValue StringInfoValue;
public UiColorValue ColorValue;
public Vector3 VectorValue;
public List ArrayValue = new();
public Dictionary StructValue = new();
public UiPropertyValue Clone()
{
var clone = new UiPropertyValue
{
Kind = Kind,
MasterPropertyId = MasterPropertyId,
UnsignedValue = UnsignedValue,
IntegerValue = IntegerValue,
FloatValue = FloatValue,
BoolValue = BoolValue,
StringInfoValue = StringInfoValue,
ColorValue = ColorValue,
VectorValue = VectorValue,
};
foreach (var item in ArrayValue)
clone.ArrayValue.Add(item.Clone());
foreach (var (key, value) in StructValue)
clone.StructValue[key] = value.Clone();
return clone;
}
}
///
/// State-scoped retail properties. Presence of a key is authoritative: an explicit
/// false, zero, centered justification, or empty collection still overrides a base
/// value during inheritance.
///
public sealed class UiPropertyBag
{
public Dictionary Values = new();
public bool TryGetValue(uint id, out UiPropertyValue value)
=> Values.TryGetValue(id, out value!);
public UiPropertyBag Clone()
{
var clone = new UiPropertyBag();
foreach (var (key, value) in Values)
clone.Values[key] = value.Clone();
return clone;
}
public static UiPropertyBag Merge(UiPropertyBag baseProperties, UiPropertyBag derivedProperties)
{
var merged = baseProperties.Clone();
foreach (var (key, value) in derivedProperties.Values)
merged.Values[key] = value.Clone();
return merged;
}
}
/// Primary render-surface media for a retail UI state.
public readonly record struct UiImageMedia(uint File, int DrawMode);
///
/// Dat-independent state descriptor. DirectState uses
/// so it cannot collide with UIStateId.Undef == 0.
///
public sealed class UiStateInfo
{
public const uint DirectStateId = uint.MaxValue;
public uint Id;
public string Name = "";
public bool PassToChildren;
public uint IncorporationFlags;
public UiImageMedia? Image;
public UiCursorMedia? Cursor;
public UiPropertyBag Properties = new();
public UiStateInfo Clone()
=> new()
{
Id = Id,
Name = Name,
PassToChildren = PassToChildren,
IncorporationFlags = IncorporationFlags,
Image = Image,
Cursor = Cursor,
Properties = Properties.Clone(),
};
public static UiStateInfo Merge(UiStateInfo baseState, UiStateInfo derivedState)
=> new()
{
Id = derivedState.Id,
Name = derivedState.Name,
PassToChildren = derivedState.PassToChildren,
IncorporationFlags = derivedState.IncorporationFlags,
Image = derivedState.Image ?? baseState.Image,
Cursor = derivedState.Cursor ?? baseState.Cursor,
Properties = UiPropertyBag.Merge(baseState.Properties, derivedState.Properties),
};
}