using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; using MiraAPI.GameOptions.Attributes; namespace MiraAPI.GameOptions; /// /// A fixed-size array created specifically for modded options that automatically synchronizes with option attributes. /// /// The type of the values that the array holds. [CollectionBuilder(typeof(ModdedArrayBuilder), nameof(ModdedArrayBuilder.Create))] public sealed class ModdedArray : IList, IReadOnlyList, IList { private readonly T[] items; private int version; /// public int Count => items.Length; /// public bool IsReadOnly => false; /// bool IList.IsFixedSize => true; /// bool ICollection.IsSynchronized => false; /// object ICollection.SyncRoot => items.SyncRoot; /// /// Initializes a new instance of the class with the provided capacity. /// /// The capacity of the list. public ModdedArray(int capacity) { items = new T[capacity]; } /// /// Initializes a new instance of the class with initial values. /// /// The initial collection of values to initialise with. public ModdedArray(IEnumerable collection) { items = [.. collection]; } /// /// Initializes a new instance of the class with initial values. /// /// The initial span of values to initialise with. public ModdedArray(Span span) { items = [.. span]; } /// /// Initializes a new instance of the class with initial values. /// /// The initial span of values to initialise with. public ModdedArray(ReadOnlySpan span) { items = [.. span]; } /// /// Gets or sets the element at the specified index, syncing with the attribute if registered. /// /// The index of the value. public T this[int index] { get { if (ModdedOptionsManager.ListInstanceCache.TryGetValue(this, out var attribute)) { items[index] = attribute is ITypedOptionList typedAttr ? typedAttr.GetValueUnboxed(index) : (T)attribute.GetValue(index); } return items[index]; } set { if (ModdedOptionsManager.ListInstanceCache.TryGetValue(this, out var attribute)) { if (attribute is ITypedOptionList typedAttr) typedAttr.SetValueUnboxed(index, value); else attribute.SetValue(index, value!); items[index] = attribute is ITypedOptionList typedAttr2 ? typedAttr2.GetValueUnboxed(index) : (T)attribute.GetValue(index); } else { items[index] = value; } version++; } } /// object? IList.this[int index] { get => this[index]; set => this[index] = (T)value!; } /// public bool Contains(T item) { return Array.IndexOf(items, item) >= 0; } /// public void CopyTo(T[] array, int arrayIndex) { items.CopyTo(array, arrayIndex); } /// public int IndexOf(T item) { return Array.IndexOf(items, item); } /// public Enumerator GetEnumerator() { return new(this); } /// bool IList.Contains(object? value) { return ((IList)items).Contains(value); } /// int IList.IndexOf(object? value) { return ((IList)items).IndexOf(value); } /// void ICollection.CopyTo(Array array, int index) { items.CopyTo(array, index); } /// IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } /// IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } /// int IList.Add(object? value) { throw new NotSupportedException("ModdedArray is of a fixed size."); } /// void IList.Clear() { throw new NotSupportedException("ModdedArray is of a fixed size."); } /// void IList.Insert(int index, object? value) { throw new NotSupportedException("ModdedArray is of a fixed size."); } /// void IList.Remove(object? value) { throw new NotSupportedException("ModdedArray is of a fixed size."); } /// void IList.RemoveAt(int index) { throw new NotSupportedException("ModdedArray is of a fixed size."); } /// void IList.Insert(int index, T item) { throw new NotSupportedException("ModdedArray is of a fixed size."); } /// void IList.RemoveAt(int index) { throw new NotSupportedException("ModdedArray is of a fixed size."); } /// void ICollection.Add(T item) { throw new NotSupportedException("ModdedArray is of a fixed size."); } /// void ICollection.Clear() { throw new NotSupportedException("ModdedArray is of a fixed size."); } /// bool ICollection.Remove(T item) { throw new NotSupportedException("ModdedArray is of a fixed size."); } /// /// Enumerates the elements of a . /// public struct Enumerator : IEnumerator { private readonly ModdedArray array; private readonly int version; private int index; private T? current; internal Enumerator(ModdedArray moddedArray) { array = moddedArray; version = moddedArray.version; index = 0; current = default; } /// public readonly T Current => current!; /// readonly object? IEnumerator.Current { get { if (index == 0 || index == array.Count + 1) throw new InvalidOperationException("Enumeration has not started or has already finished."); return Current; } } /// public readonly void Dispose() { } /// public bool MoveNext() { if (version != array.version) throw new InvalidOperationException("Collection was modified; enumeration operation may not execute."); if ((uint)index < (uint)array.Count) { current = array[index]; index++; return true; } index = array.Count + 1; current = default; return false; } /// public void Reset() { if (version != array.version) throw new InvalidOperationException("Collection was modified; enumeration operation may not execute."); index = 0; current = default; } } } /// /// Provides a builder for to support C# 12 collection expressions. /// public static class ModdedArrayBuilder { /// /// Creates a new from a span of values. /// /// The the type of the values. /// The values to initialise with. /// The instance initialised with the provided values. public static ModdedArray Create(ReadOnlySpan values) { return new(values); } }