// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// Standard Library
using System;
using System.Collections.Generic;
using System.Linq;
// Unity
using UnityEngine;
namespace AWS.GameKit.Runtime.Utils
{
///
/// A Serializable that remembers the order the elements were added.
///
///
/// [Serialization]
///
/// The class is not Serializable in C# by default.
/// This class is able to serialize a dictionary by breaking out the key/value pairs into a key list and value list which can be Serialized.
///
///
/// [Order Remembered]
///
/// This collection remembers the order the elements were added.
/// The ordered elements can be accessed through , , and .
/// The elements can be sorted in a custom order by calling .
///
///
/// [Time Complexity]
///
/// Each method has the following time complexity:
///
/// - - O(1)
/// - - O(1)
/// - - O(1)
/// - - O(1)
/// - - O(N)
/// - - depends on the provided callback function, usually O(N log(N))
/// - - O(N)
///
///
///
/// The type of the keys in the dictionary.
/// The type of the values in the dictionary.
[Serializable]
public class SerializableOrderedDictionary : ISerializationCallbackReceiver
{
protected IDictionary _dict = new Dictionary();
// Serialization
// The keys and values are stored in sorted order.
[HideInInspector] [SerializeField] protected List _keys = new List();
[HideInInspector] [SerializeField] protected List _values = new List();
///
/// The number of key/value pairs in the collection.
///
public int Count => _dict.Count;
///
/// Get all keys in the collection in the order they were added or later sorted with .
///
public IEnumerable Keys => _keys;
///
/// Get all values in the collection in the order they were added or later sorted with .
///
public IEnumerable Values => _values;
///
/// Get all key/value pairs in the collection in the order they were added or later sorted with .
///
public IEnumerable> KeyValuePairs => _keys.Zip(_values, (key, value) => new KeyValuePair(key, value));
///
/// This method is called by Unity when this class needs to be Serialized.
///
public virtual void OnBeforeSerialize()
{
// Nothing to do.
// The _keys and _values lists are already up-to-date. They will be used in OnAfterDeserialize() to reconstruct this object.
}
///
/// This method is called by Unity when this class needs to be Deserialized.
///
public virtual void OnAfterDeserialize()
{
// Rebuild the dictionary from the serialized keys/values.
_dict.Clear();
if (_keys.Count != _values.Count)
{
Logging.LogError($"There were {_keys.Count} keys and {_values.Count} values found while deserializing the {nameof(SerializableOrderedDictionary)}. " +
$"This collection may contain inaccurate data.");
}
int lowestCount = Math.Min(_keys.Count, _values.Count);
for (int i = 0; i < lowestCount; ++i)
{
_dict.Add(_keys[i], _values[i]);
}
}
///
/// Determine whether the collection contains the specified key.
///
/// The key to locate in the collection.
/// Thrown when the provided key is null.
public virtual bool ContainsKey(TKey key)
{
return _dict.ContainsKey(key);
}
///
/// Add the specified key and value to the dictionary at the end of the ordered list.
///
/// The key of the element to add.
/// The value of the element to add.
/// Thrown when the provided key is null.
/// Thrown when an element with the same key already exists in the collection.
public virtual void Add(TKey key, TValue value)
{
_dict.Add(key, value);
_keys.Add(key);
_values.Add(value);
}
///
/// Set the value associated with the specified key.
///
/// The key of the value to set.
/// The value associated with the specified key. If the specified key is not found, creates a new element with the specified key. Otherwise overwrites the existing element.
/// Thrown when the provided key is null.
public virtual void SetValue(TKey key, TValue value)
{
if (_dict.ContainsKey(key))
{
// Update existing value
_dict[key] = value;
}
else
{
// Add new element
Add(key, value);
}
}
///
/// Get the value associated with the specified key.
///
/// The key of the value to get.
/// The value associated with the specified key. If the specified key is not found, throws a .
/// Thrown when the provided key is null.
/// Thrown when the specified key does not exist in the collection.
public virtual TValue GetValue(TKey key)
{
return _dict[key];
}
///
/// Remove the value with the specified key from the .
///
/// The key of the element to remove.
/// Thrown when the provided key is null.
public virtual void Remove(TKey key)
{
if (!_dict.ContainsKey(key))
{
return;
}
_dict.Remove(key);
int indexOfElement = _keys.IndexOf(key);
_keys.RemoveAt(indexOfElement);
_values.RemoveAt(indexOfElement);
}
///
/// Remove all elements from the collection.
///
public virtual void Clear()
{
_dict.Clear();
_keys.Clear();
_values.Clear();
}
///
/// Sort the collection's elements in-place using using the provided sorting function.
///
///
/// After calling this method, the new ordering is reflected in , , and .
///
/// A function which sorts a list of key/value pairs. The function's input is this collection's ordered key/value pairs.
/// The function's output is the same key/value pairs in a newly sorted order.
public virtual void Sort(Func>, IEnumerable>> sortFunction)
{
IEnumerable> sortedElements = sortFunction(KeyValuePairs.ToList());
_keys.Clear();
_values.Clear();
foreach (KeyValuePair keyValuePair in sortedElements)
{
_keys.Add(keyValuePair.Key);
_values.Add(keyValuePair.Value);
}
}
}
}