using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace UnityAtoms
{
///
/// A Serializable dictionary used by AtomCollection.
///
/// Key type.
/// Value type.
[Serializable]
public abstract class SerializableDictionary : IDictionary, ISerializationCallbackReceiver, IEnumerable>, IEnumerable, ICollection>
where K : IEquatable
{
public Action Added { get => _added; set => _added = value; }
public Action Removed { get => _removed; set => _removed = value; }
public Action Cleared { get => _cleared; set => _cleared = value; }
private event Action _added;
private event Action _removed;
private event Action _cleared;
private Dictionary _dict = new Dictionary();
[SerializeField]
private List _serializedKeys = new List();
[SerializeField]
private List _serializedValues = new List();
///
/// Needed in order to keep track of duplicate keys in the dictionary.
///
///
///
[SerializeField]
private List _duplicateKeyIndices = new List();
public void OnAfterDeserialize()
{
if (_serializedKeys != null && _serializedValues != null)
{
var keyCount = _serializedKeys.Count;
var valueCount = _serializedValues.Count;
// This is a precaution and might not be necessay. However, we make sure that _serializedKeys have the same length as _serializedValues.
// Everything is assuming that the lists are in sync. The larger list will be reduced in length to the same as the smaller of the 2.
if (keyCount != valueCount)
{
if (keyCount > valueCount)
{
_serializedKeys.RemoveRange(valueCount, keyCount - valueCount);
}
else
{
_serializedValues.RemoveRange(keyCount, valueCount - keyCount);
}
}
_dict.Clear();
_duplicateKeyIndices.Clear();
var length = _serializedKeys.Count;
for (var i = 0; i < length; ++i)
{
if (!_dict.ContainsKey(_serializedKeys[i]))
{
_dict.Add(_serializedKeys[i], _serializedValues[i]);
}
else
{
_duplicateKeyIndices.Add(i);
}
}
}
}
public void OnBeforeSerialize()
{
var enumerator = _dict.GetEnumerator();
try
{
while (enumerator.MoveNext())
{
var keyIndex = _serializedKeys.IndexOf(enumerator.Current.Key);
if (keyIndex != -1)
{
_serializedKeys[keyIndex] = enumerator.Current.Key;
_serializedValues[keyIndex] = enumerator.Current.Value;
}
else
{
_serializedKeys.Add(enumerator.Current.Key);
_serializedValues.Add(enumerator.Current.Value);
}
}
}
finally
{
enumerator.Dispose();
}
}
#region IDictionary
public ICollection Keys { get => _dict.Keys; }
public ICollection Values { get => _dict.Values; }
public int Count { get => _dict.Count; }
public bool IsReadOnly { get => false; }
public V this[K key]
{
get => _dict[key];
set => _dict[key] = value;
}
public void Add(K key, V value)
{
if (ContainsKey(key)) return;
_dict.Add(key, value);
_serializedKeys.Add(key);
_serializedValues.Add(value);
_added?.Invoke(value);
}
public void Add(KeyValuePair kvp)
{
Add(kvp.Key, kvp.Value);
}
public bool ContainsKey(K key) { return _dict.ContainsKey(key); }
public bool Remove(K key)
{
if (!_dict.ContainsKey(key)) return false;
var value = _dict[key];
_dict.Remove(key);
var index = _serializedKeys.IndexOf(key);
_serializedKeys.RemoveAt(index);
_serializedValues.RemoveAt(index);
_removed?.Invoke(value);
return true;
}
public bool Remove(KeyValuePair kvp)
{
return Remove(kvp.Key);
}
public bool TryGetValue(K key, out V value) { return _dict.TryGetValue(key, out value); }
public void Clear()
{
_dict.Clear();
_cleared?.Invoke();
}
public bool Contains(KeyValuePair kvp)
{
V value;
return _dict.TryGetValue(kvp.Key, out value) && value.Equals(kvp.Value);
}
public void CopyTo(KeyValuePair[] array, int index)
{
if (array == null)
{
throw new ArgumentNullException();
}
if (index < 0 || index > array.Length)
{
throw new ArgumentOutOfRangeException();
}
var enumerator = _dict.GetEnumerator();
var cur = 0;
try
{
while (enumerator.MoveNext())
{
if (cur >= index)
{
array[cur] = new KeyValuePair(enumerator.Current.Key, enumerator.Current.Value);
}
++cur;
}
}
finally
{
enumerator.Dispose();
}
}
public IEnumerator> GetEnumerator() { return _dict.GetEnumerator(); }
IEnumerator IEnumerable.GetEnumerator() { return _dict.GetEnumerator(); }
#endregion
}
}