using System; using System.Collections; using System.Collections.Generic; using System.Linq; using UnityEngine; namespace Nullframes.Intrigues.Utils { public class SerializableDictionary { } [Serializable] public class SerializableDictionary : SerializableDictionary, IDictionary, IReadOnlyDictionary, ISerializationCallbackReceiver { [SerializeField] private List list = new(); [Serializable] public struct SerializableKeyValuePair { public TKey Key; public TValue Value; public SerializableKeyValuePair(TKey key, TValue value) { Key = key; Value = value; } } private Dictionary KeyPositions => _keyPositions.Value; private Lazy> _keyPositions; public SerializableDictionary() { _keyPositions = new Lazy>(MakeKeyPositions); } public SerializableDictionary(IDictionary dictionary) { _keyPositions = new Lazy>(MakeKeyPositions); if (dictionary == null) throw new ArgumentException("The passed dictionary is null."); foreach (var pair in dictionary) Add(pair.Key, pair.Value); } private Dictionary MakeKeyPositions() { var numEntries = list.Count; var result = new Dictionary(numEntries); for (var i = 0; i < numEntries; ++i) result[list[i].Key] = (uint)i; return result; } public void OnBeforeSerialize() { } public void OnAfterDeserialize() { // After deserialization, the key positions might be changed _keyPositions = new Lazy>(MakeKeyPositions); } #region IDictionary public TValue this[TKey key] { get => list[(int)KeyPositions[key]].Value; set { if (KeyPositions.TryGetValue(key, out var index)) { list[(int)index] = new SerializableKeyValuePair(list[(int)index].Key, value); } else { KeyPositions[key] = (uint)list.Count; list.Add(new SerializableKeyValuePair(key, value)); } } } IEnumerable IReadOnlyDictionary.Keys => Keys; IEnumerable IReadOnlyDictionary.Values => Values; public ICollection Keys => list.Select(tuple => tuple.Key).ToArray(); public ICollection Values => list.Select(tuple => tuple.Value).ToArray(); public void Add(TKey key, TValue value) { if (KeyPositions.ContainsKey(key)) { throw new ArgumentException("An element with the same key already exists in the dictionary."); } else { KeyPositions[key] = (uint)list.Count; list.Add(new SerializableKeyValuePair(key, value)); } } public bool ContainsKey(TKey key) { return KeyPositions.ContainsKey(key); } public bool Remove(TKey key) { if (KeyPositions.TryGetValue(key, out var index)) { var kp = KeyPositions; kp.Remove(key); list.RemoveAt((int)index); var numEntries = list.Count; for (var i = index; i < numEntries; i++) kp[list[(int)i].Key] = i; return true; } return false; } public bool TryGetValue(TKey key, out TValue value) { if (KeyPositions.TryGetValue(key, out var index)) { value = list[(int)index].Value; return true; } value = default; return false; } #endregion #region ICollection public int Count => list.Count; public bool IsReadOnly => false; public void Add(KeyValuePair kvp) { Add(kvp.Key, kvp.Value); } public void Clear() { list.Clear(); KeyPositions.Clear(); } public bool Contains(KeyValuePair kvp) { return KeyPositions.ContainsKey(kvp.Key); } public void CopyTo(KeyValuePair[] array, int arrayIndex) { var numKeys = list.Count; if (array.Length - arrayIndex < numKeys) throw new ArgumentException("arrayIndex"); for (var i = 0; i < numKeys; ++i, ++arrayIndex) { var entry = list[i]; array[arrayIndex] = new KeyValuePair(entry.Key, entry.Value); } } public bool Remove(KeyValuePair kvp) { return Remove(kvp.Key); } #endregion #region IEnumerable public IEnumerator> GetEnumerator() { return list.Select(ToKeyValuePair).GetEnumerator(); KeyValuePair ToKeyValuePair(SerializableKeyValuePair skvp) { return new KeyValuePair(skvp.Key, skvp.Value); } } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } #endregion } }