using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
// From http://www.codeproject.com/KB/recipes/GenericOrderedDictionary.aspx
namespace NW4RTools.Util
{
	/// 
	/// Represents a generic collection of key/value pairs that are ordered independently of the key and value.
	/// 
	/// The type of the keys in the dictionary
	/// The type of the values in the dictionary
	public class OrderedDictionary : IOrderedDictionary
	{
		private const int DefaultInitialCapacity = 0;
		private static readonly string _keyTypeName = typeof(TKey).FullName;
		private static readonly string _valueTypeName = typeof(TValue).FullName;
		private static readonly bool _valueTypeIsReferenceType = !typeof(ValueType).IsAssignableFrom(typeof(TValue));
		private Dictionary _dictionary;
		private List> _list;
		private IEqualityComparer _comparer;
		private object _syncRoot;
		private int _initialCapacity;
		/// 
		/// Initializes a new instance of the OrderedDictionary<TKey,TValue> class.
		/// 
		public OrderedDictionary()
			: this(DefaultInitialCapacity, null)
		{
		}
		/// 
		/// Initializes a new instance of the OrderedDictionary<TKey,TValue> class using the specified initial capacity.
		/// 
		/// The initial number of elements that the OrderedDictionary<TKey,TValue> can contain.
		///  is less than 0
		public OrderedDictionary(int capacity)
			: this(capacity, null)
		{
		}
		/// 
		/// Initializes a new instance of the OrderedDictionary<TKey,TValue> class using the specified comparer.
		/// 
		/// The IEqualityComparer<TKey> to use when comparing keys, or  to use the default EqualityComparer<TKey> for the type of the key.
		public OrderedDictionary(IEqualityComparer comparer)
			: this(DefaultInitialCapacity, comparer)
		{
		}
		/// 
		/// Initializes a new instance of the OrderedDictionary<TKey,TValue> class using the specified initial capacity and comparer.
		/// 
		/// The initial number of elements that the OrderedDictionary<TKey,TValue> collection can contain.
		/// The IEqualityComparer<TKey> to use when comparing keys, or  to use the default EqualityComparer<TKey> for the type of the key.
		///  is less than 0
		public OrderedDictionary(int capacity, IEqualityComparer comparer)
		{
			if(0 > capacity)
				throw new ArgumentOutOfRangeException("capacity", "'capacity' must be non-negative");
			_initialCapacity = capacity;
			_comparer = comparer;
		}
		/// 
		/// Converts the object passed as a key to the key type of the dictionary
		/// 
		/// The key object to check
		/// The key object, cast as the key type of the dictionary
		///  is .
		/// The key type of the OrderedDictionary<TKey,TValue> is not in the inheritance hierarchy of .
		private static TKey ConvertToKeyType(object keyObject)
		{
			if(null == keyObject)
			{
				throw new ArgumentNullException("key");
			}
			else
			{
				if(keyObject is TKey)
					return (TKey)keyObject;
			}
			throw new ArgumentException("'key' must be of type " + _keyTypeName, "key");
		}
		/// 
		/// Converts the object passed as a value to the value type of the dictionary
		/// 
		/// The object to convert to the value type of the dictionary
		/// The value object, converted to the value type of the dictionary
		///  is , and the value type of the OrderedDictionary<TKey,TValue> is a value type.
		/// The value type of the OrderedDictionary<TKey,TValue> is not in the inheritance hierarchy of .
		private static TValue ConvertToValueType(object value)
		{
			if(null == value)
			{
				if(_valueTypeIsReferenceType)
					return default(TValue);
				else
					throw new ArgumentNullException("value");
			}
			else
			{
				if(value is TValue)
					return (TValue)value;
			}
			throw new ArgumentException("'value' must be of type " + _valueTypeName, "value");
		}
		/// 
		/// Gets the dictionary object that stores the keys and values
		/// 
		/// The dictionary object that stores the keys and values for the OrderedDictionary<TKey,TValue>
		/// Accessing this property will create the dictionary object if necessary
		private Dictionary Dictionary
		{
			get
			{
				if(null == _dictionary)
				{
					_dictionary = new Dictionary(_initialCapacity, _comparer);
				}
				return _dictionary;
			}
		}
		/// 
		/// Gets the list object that stores the key/value pairs.
		/// 
		/// The list object that stores the key/value pairs for the OrderedDictionary<TKey,TValue>
		/// Accessing this property will create the list object if necessary.
		private List> List
		{
			get
			{
				if(null == _list)
				{
					_list = new List>(_initialCapacity);
				}
				return _list;
			}
		}
		IDictionaryEnumerator IOrderedDictionary.GetEnumerator()
		{
			return Dictionary.GetEnumerator();
		}
		IDictionaryEnumerator IDictionary.GetEnumerator()
		{
			return Dictionary.GetEnumerator();
		}
		IEnumerator IEnumerable.GetEnumerator()
		{
			return List.GetEnumerator();
		}
		IEnumerator> IEnumerable>.GetEnumerator()
		{
			return List.GetEnumerator();
		}
		public TKey GetKeyForIndex(int index) {
			return List[index].Key;
		}
		public TKey GetKeyForValue(TValue value) {
			foreach (var kv in List) {
				if (kv.Value.Equals(value))
					return kv.Key;
			}
			throw new KeyNotFoundException();
		}
		public int GetIndexForValue(TValue value) {
			int i = 0;
			foreach (var kv in List) {
				if (kv.Value.Equals(value))
					return i;
				i += 1;
			}
			throw new KeyNotFoundException();
		}
		/// 
		/// Inserts a new entry into the OrderedDictionary<TKey,TValue> collection with the specified key and value at the specified index.
		/// 
		/// The zero-based index at which the element should be inserted.
		/// The key of the entry to add.
		/// The value of the entry to add. The value can be  if the type of the values in the dictionary is a reference type.
		///  is less than 0.
		/// -or-
		///  is greater than .
		///  is .
		/// An element with the same key already exists in the OrderedDictionary<TKey,TValue>.
		public void Insert(int index, TKey key, TValue value)
		{
			if(index > Count || index < 0)
				throw new ArgumentOutOfRangeException("index");
			Dictionary.Add(key, value);
			List.Insert(index, new KeyValuePair(key, value));
		}
		/// 
		/// Inserts a new entry into the OrderedDictionary<TKey,TValue> collection with the specified key and value at the specified index.
		/// 
		/// The zero-based index at which the element should be inserted.
		/// The key of the entry to add.
		/// The value of the entry to add. The value can be  if the type of the values in the dictionary is a reference type.
		///  is less than 0.
		/// -or-
		///  is greater than .
		///  is .
		/// -or-
		///  is , and the value type of the OrderedDictionary<TKey,TValue> is a value type.
		/// The key type of the OrderedDictionary<TKey,TValue> is not in the inheritance hierarchy of .
		/// -or-
		/// The value type of the OrderedDictionary<TKey,TValue> is not in the inheritance hierarchy of .
		/// -or-
		/// An element with the same key already exists in the OrderedDictionary<TKey,TValue>.
		void IOrderedDictionary.Insert(int index, object key, object value)
		{
			Insert(index, ConvertToKeyType(key), ConvertToValueType(value));
		}
		/// 
		/// Removes the entry at the specified index from the OrderedDictionary<TKey,TValue> collection.
		/// 
		/// The zero-based index of the entry to remove.
		///  is less than 0.
		/// -or-
		/// index is equal to or greater than .
		public void RemoveAt(int index)
		{
			if(index >= Count || index < 0)
				throw new ArgumentOutOfRangeException("index", "'index' must be non-negative and less than the size of the collection");
			TKey key = List[index].Key;
			List.RemoveAt(index);
			Dictionary.Remove(key);
		}
		/// 
		/// Gets or sets the value at the specified index.
		/// 
		/// The zero-based index of the value to get or set.
		/// The value of the item at the specified index.
		///  is less than 0.
		/// -or-
		/// index is equal to or greater than .
		public TValue this[int index]
		{
			get
			{
				return List[index].Value;
			}
			set
			{
				if(index >= Count || index < 0)
					throw new ArgumentOutOfRangeException("index", "'index' must be non-negative and less than the size of the collection");
				TKey key = List[index].Key;
				List[index] = new KeyValuePair(key, value);
				Dictionary[key] = value;
			}
		}
		/// 
		/// Gets or sets the value at the specified index.
		/// 
		/// The zero-based index of the value to get or set.
		/// The value of the item at the specified index.
		///  is less than 0.
		/// -or-
		/// index is equal to or greater than .
		///  is a null reference, and the value type of the OrderedDictionary<TKey,TValue> is a value type.
		/// The value type of the OrderedDictionary<TKey,TValue> is not in the inheritance hierarchy of .
		object IOrderedDictionary.this[int index]
		{
			get
			{
				return this[index];
			}
			set
			{
				this[index] = ConvertToValueType(value);
			}
		}
		/// 
		/// Adds an entry with the specified key and value into the OrderedDictionary<TKey,TValue> collection with the lowest available index.
		/// 
		/// The key of the entry to add.
		/// The value of the entry to add. This value can be .
		/// A key cannot be , but a value can be.
		/// You can also use the  property to add new elements by setting the value of a key that does not exist in the OrderedDictionary<TKey,TValue> collection; however, if the specified key already exists in the OrderedDictionary<TKey,TValue>, setting the  property overwrites the old value. In contrast, the  method does not modify existing elements.
		///  is 
		/// An element with the same key already exists in the OrderedDictionary<TKey,TValue>
		void IDictionary.Add(TKey key, TValue value)
		{
			Add(key, value);
		}
		/// 
		/// Adds an entry with the specified key and value into the OrderedDictionary<TKey,TValue> collection with the lowest available index.
		/// 
		/// The key of the entry to add.
		/// The value of the entry to add. This value can be .
		/// The index of the newly added entry
		/// A key cannot be , but a value can be.
		/// You can also use the  property to add new elements by setting the value of a key that does not exist in the OrderedDictionary<TKey,TValue> collection; however, if the specified key already exists in the OrderedDictionary<TKey,TValue>, setting the  property overwrites the old value. In contrast, the  method does not modify existing elements.
		///  is 
		/// An element with the same key already exists in the OrderedDictionary<TKey,TValue>
		public int Add(TKey key, TValue value)
		{
			Dictionary.Add(key, value);
			List.Add(new KeyValuePair(key, value));
			return Count - 1;
		}
		/// 
		/// Adds an entry with the specified key and value into the OrderedDictionary<TKey,TValue> collection with the lowest available index.
		/// 
		/// The key of the entry to add.
		/// The value of the entry to add. This value can be .
		///  is .
		/// -or-
		///  is , and the value type of the OrderedDictionary<TKey,TValue> is a value type.
		/// The key type of the OrderedDictionary<TKey,TValue> is not in the inheritance hierarchy of .
		/// -or-
		/// The value type of the OrderedDictionary<TKey,TValue> is not in the inheritance hierarchy of .
		void IDictionary.Add(object key, object value)
		{
			Add(ConvertToKeyType(key), ConvertToValueType(value));
		}
		/// 
		/// Removes all elements from the OrderedDictionary<TKey,TValue> collection.
		/// 
		/// The capacity is not changed as a result of calling this method.
		public void Clear()
		{
			Dictionary.Clear();
			List.Clear();
		}
		/// 
		/// Determines whether the OrderedDictionary<TKey,TValue> collection contains a specific key.
		/// 
		/// The key to locate in the OrderedDictionary<TKey,TValue> collection.
		///  if the OrderedDictionary<TKey,TValue> collection contains an element with the specified key; otherwise, .
		///  is 
		public bool ContainsKey(TKey key)
		{
			return Dictionary.ContainsKey(key);
		}
		/// 
		/// Determines whether the OrderedDictionary<TKey,TValue> collection contains a specific key.
		/// 
		/// The key to locate in the OrderedDictionary<TKey,TValue> collection.
		///  if the OrderedDictionary<TKey,TValue> collection contains an element with the specified key; otherwise, .
		///  is 
		/// The key type of the OrderedDictionary<TKey,TValue> is not in the inheritance hierarchy of .
		bool IDictionary.Contains(object key)
		{
			return ContainsKey(ConvertToKeyType(key));
		}
		/// 
		/// Gets a value indicating whether the OrderedDictionary<TKey,TValue> has a fixed size.
		/// 
		///  if the OrderedDictionary<TKey,TValue> has a fixed size; otherwise, . The default is .
		bool IDictionary.IsFixedSize
		{
			get
			{
				return false;
			}
		}
		/// 
		/// Gets a value indicating whether the OrderedDictionary<TKey,TValue> collection is read-only.
		/// 
		///  if the OrderedDictionary<TKey,TValue> is read-only; otherwise, . The default is .
		/// 
		/// A collection that is read-only does not allow the addition, removal, or modification of elements after the collection is created.
		/// A collection that is read-only is simply a collection with a wrapper that prevents modification of the collection; therefore, if changes are made to the underlying collection, the read-only collection reflects those changes.
		/// 
		public bool IsReadOnly
		{
			get
			{
				return false;
			}
		}
		/// 
		/// Gets an  object containing the keys in the OrderedDictionary<TKey,TValue>.
		/// 
		/// An  object containing the keys in the OrderedDictionary<TKey,TValue>.
		/// The returned  object is not a static copy; instead, the collection refers back to the keys in the original OrderedDictionary<TKey,TValue>. Therefore, changes to the OrderedDictionary<TKey,TValue> continue to be reflected in the key collection.
		ICollection IDictionary.Keys
		{
			get
			{
				return (ICollection)Keys;
			}
		}
		/// 
		/// Returns the zero-based index of the specified key in the OrderedDictionary<TKey,TValue>
		/// 
		/// The key to locate in the OrderedDictionary<TKey,TValue>
		/// The zero-based index of , if  is found in the OrderedDictionary<TKey,TValue>; otherwise, -1
		/// This method performs a linear search; therefore it has a cost of O(n) at worst.
		public int IndexOfKey(TKey key)
		{
			if(null == key)
				throw new ArgumentNullException("key");
			for(int index = 0; index < List.Count; index++)
			{
				KeyValuePair entry = List[index];
				TKey next = entry.Key;
				if(null != _comparer)
				{
					if(_comparer.Equals(next, key))
					{
						return index;
					}
				}
				else if(next.Equals(key))
				{
					return index;
				}
			}
			return -1;
		}
		/// 
		/// Removes the entry with the specified key from the OrderedDictionary<TKey,TValue> collection.
		/// 
		/// The key of the entry to remove
		///  if the key was found and the corresponding element was removed; otherwise, 
		public bool Remove(TKey key)
		{
			if(null == key)
				throw new ArgumentNullException("key");
			int index = IndexOfKey(key);
			if(index >= 0)
			{
				if(Dictionary.Remove(key))
				{
					List.RemoveAt(index);
					return true;
				}
			}
			return false;
		}
		/// 
		/// Removes the entry with the specified key from the OrderedDictionary<TKey,TValue> collection.
		/// 
		/// The key of the entry to remove
		void IDictionary.Remove(object key)
		{
			Remove(ConvertToKeyType(key));
		}
		/// 
		/// Gets an  object containing the values in the OrderedDictionary<TKey,TValue> collection.
		/// 
		/// An  object containing the values in the OrderedDictionary<TKey,TValue> collection.
		/// The returned  object is not a static copy; instead, the  refers back to the values in the original OrderedDictionary<TKey,TValue> collection. Therefore, changes to the OrderedDictionary<TKey,TValue> continue to be reflected in the .
		ICollection IDictionary.Values
		{
			get
			{
				return (ICollection)Values;
			}
		}
		/// 
		/// Gets or sets the value with the specified key.
		/// 
		/// The key of the value to get or set.
		/// The value associated with the specified key. If the specified key is not found, attempting to get it returns , and attempting to set it creates a new element using the specified key.
		public TValue this[TKey key]
		{
			get
			{
				return Dictionary[key];
			}
			set
			{
				if(Dictionary.ContainsKey(key))
				{
					Dictionary[key] = value;
					List[IndexOfKey(key)] = new KeyValuePair(key, value);
				}
				else
				{
					Add(key, value);
				}
			}
		}
		/// 
		/// Gets or sets the value with the specified key.
		/// 
		/// The key of the value to get or set.
		/// The value associated with the specified key. If the specified key is not found, attempting to get it returns , and attempting to set it creates a new element using the specified key.
		object IDictionary.this[object key]
		{
			get
			{
				return this[ConvertToKeyType(key)];
			}
			set
			{
				this[ConvertToKeyType(key)] = ConvertToValueType(value);
			}
		}
		/// 
		/// Copies the elements of the OrderedDictionary<TKey,TValue> elements to a one-dimensional Array object at the specified index.
		/// 
		/// The one-dimensional  object that is the destination of the  objects copied from the OrderedDictionary<TKey,TValue>. The  must have zero-based indexing.
		/// The zero-based index in  at which copying begins.
		/// The  method preserves the order of the elements in the OrderedDictionary<TKey,TValue>
		void ICollection.CopyTo(Array array, int index)
		{
			((ICollection)List).CopyTo(array, index);
		}
		/// 
		/// Gets the number of key/values pairs contained in the OrderedDictionary<TKey,TValue> collection.
		/// 
		/// The number of key/value pairs contained in the OrderedDictionary<TKey,TValue> collection.
		public int Count
		{
			get
			{
				return List.Count;
			}
		}
		/// 
		/// Gets a value indicating whether access to the OrderedDictionary<TKey,TValue> object is synchronized (thread-safe).
		/// 
		/// This method always returns false.
		bool ICollection.IsSynchronized
		{
			get
			{
				return false;
			}
		}
		/// 
		/// Gets an object that can be used to synchronize access to the OrderedDictionary<TKey,TValue> object.
		/// 
		/// An object that can be used to synchronize access to the OrderedDictionary<TKey,TValue> object.
		object ICollection.SyncRoot
		{
			get
			{
				if(this._syncRoot == null)
				{
					System.Threading.Interlocked.CompareExchange(ref this._syncRoot, new object(), null);
				}
				return this._syncRoot;
			}
		}
		/// 
		/// Gets an ICollection<TKey> object containing the keys in the OrderedDictionary<TKey,TValue>.
		/// 
		/// An ICollection<TKey> object containing the keys in the OrderedDictionary<TKey,TValue>.
		/// The returned ICollection<TKey> object is not a static copy; instead, the collection refers back to the keys in the original OrderedDictionary<TKey,TValue>. Therefore, changes to the OrderedDictionary<TKey,TValue> continue to be reflected in the key collection.
		public ICollection Keys
		{
			get
			{
				return Dictionary.Keys;
			}
		}
		/// 
		/// Gets the value associated with the specified key.
		/// 
		/// The key of the value to get.
		/// When this method returns, contains the value associated with the specified key, if the key is found; otherwise, the default value for the type of . This parameter can be passed uninitialized.
		///  if the OrderedDictionary<TKey,TValue> contains an element with the specified key; otherwise, .
		public bool TryGetValue(TKey key, out TValue value)
		{
			return Dictionary.TryGetValue(key, out value);
		}
		/// 
		/// Gets an ICollection<TValue> object containing the values in the OrderedDictionary<TKey,TValue>.
		/// 
		/// An ICollection<TValue> object containing the values in the OrderedDictionary<TKey,TValue>.
		/// The returned ICollection<TKey> object is not a static copy; instead, the collection refers back to the values in the original OrderedDictionary<TKey,TValue>. Therefore, changes to the OrderedDictionary<TKey,TValue> continue to be reflected in the value collection.
		public ICollection Values
		{
			get
			{
				return Dictionary.Values;
			}
		}
		/// 
		/// Adds the specified value to the OrderedDictionary<TKey,TValue> with the specified key.
		/// 
		/// The KeyValuePair<TKey,TValue> structure representing the key and value to add to the OrderedDictionary<TKey,TValue>.
		void ICollection>.Add(KeyValuePair item)
		{
			Add(item.Key, item.Value);
		}
		/// 
		/// Determines whether the OrderedDictionary<TKey,TValue> contains a specific key and value.
		/// 
		/// The KeyValuePair<TKey,TValue> structure to locate in the OrderedDictionary<TKey,TValue>.
		///  if  is found in the OrderedDictionary<TKey,TValue>; otherwise, .
		bool ICollection>.Contains(KeyValuePair item)
		{
			return ((ICollection>)Dictionary).Contains(item);
		}
		/// 
		/// Copies the elements of the OrderedDictionary<TKey,TValue> to an array of type , starting at the specified index.
		/// 
		/// The one-dimensional array of type KeyValuePair<TKey,TValue> that is the destination of the KeyValuePair<TKey,TValue> elements copied from the OrderedDictionary<TKey,TValue>. The array must have zero-based indexing.
		/// The zero-based index in  at which copying begins.
		void ICollection>.CopyTo(KeyValuePair[] array, int arrayIndex)
		{
			((ICollection>)Dictionary).CopyTo(array, arrayIndex);
		}
		/// 
		/// Removes a key and value from the dictionary.
		/// 
		/// The KeyValuePair<TKey,TValue> structure representing the key and value to remove from the OrderedDictionary<TKey,TValue>.
		///  if the key and value represented by  is successfully found and removed; otherwise, . This method returns  if  is not found in the OrderedDictionary<TKey,TValue>.
		bool ICollection>.Remove(KeyValuePair item)
		{
			return Remove(item.Key);
		}
	}
}