using System.Collections.ObjectModel;
namespace Prefab.Base;
///
/// Provides a customizable attribute bag with change tracking for Prefab domain objects.
///
public class GenericAttributes : ICanBeCustomized
{
private readonly Dictionary _attributes = new();
private readonly List _changes = new();
///
IReadOnlyDictionary IHasGenericAttributes.GenericAttributes => new ReadOnlyDictionary(_attributes);
///
/// Gets the ordered list of attribute mutations recorded since the last call to .
///
public IReadOnlyList Changes => _changes.AsReadOnly();
///
/// Provides an extension point for enforcing key/value requirements before persisting the attribute.
///
/// The attribute key being modified.
/// The proposed attribute value.
protected virtual void ValidateAttribute(string key, object value)
{
// Default: no validation. Override in derived classes for custom logic.
}
///
/// Sets or replaces an attribute value and records the mutation.
///
/// The attribute key to set.
/// The attribute value.
public void SetAttribute(string key, object value)
{
_attributes.TryGetValue(key, out var oldValue);
ValidateAttribute(key, value);
_attributes[key] = value;
_changes.Add(new GenericAttributeChange(key, oldValue, value));
}
///
/// Removes an attribute if present and records the removal.
///
/// The attribute key to remove.
/// true when the key existed and was removed; otherwise false.
public bool RemoveAttribute(string key)
{
if (_attributes.TryGetValue(key, out var oldValue) && _attributes.Remove(key))
{
_changes.Add(new GenericAttributeChange(key, oldValue, null));
return true;
}
return false;
}
///
/// Attempts to read an attribute value and cast it to the requested type.
///
/// The attribute key to read.
/// Receives the typed attribute value when successful.
/// The expected attribute type.
/// true when the attribute exists and matches the requested type.
public bool TryGetAttribute(string key, out T value)
{
if (_attributes.TryGetValue(key, out var obj) && obj is T t)
{
value = t;
return true;
}
value = default!;
return false;
}
///
/// Creates an immutable snapshot copy of the current attributes.
///
public IReadOnlyDictionary Snapshot() => new ReadOnlyDictionary(new Dictionary(_attributes));
///
/// Clears all recorded attribute changes while keeping the current attribute values intact.
///
public void ClearChanges() => _changes.Clear();
}