using System;
using System.Diagnostics;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

#pragma warning disable 0420

/// <title>Lock-free and wait-free generic concurrent dictionary</title>
/// <author>Glenn Slayden http://www.glennslayden.com</author>
/// <date>December 2010</date>
/// <keywords>lock-free, wait-free, non-blocking, concurrent, associative array, dictionary, hash map, hash table</keywords>
/// <typeparam name="K">Type of the key that is used to retrieve a data item. All key instances must be distinct. For reference 
/// type (object) keys and <seealso cref="Nullable&lt;T&gt;"/> keys, key instances may not be null. Value type key types should 
/// support a low-collision <seealso cref="GetHashCode"/> implementation.</typeparam>
/// <typeparam name="V">Type of the data item that is associated with a key.</typeparam>
public partial class LockFreeDictionary<K, V> : IDictionary<K, V>
	where K : IEquatable<K>
	where V : IEquatable<V>
{
	public LockFreeDictionary()
	{
		f_null_check = !EntryBlock.Entry.f_value_type_key || Nullable.GetUnderlyingType(EntryBlock.Entry.t_key) != null;

		m_config = new Config(this);
	}

	[Flags]
	public enum Options
	{
		None = 0,

		/// Atomic update of dictionary values can be costly, but for some value types, equality comparisons are also costly.
		/// Setting this flag elects that, when updating, value instances should be checked for equality against the existing 
		/// value for the key (if any) in order to possibly avoid transacting an atomic update.
		CheckForVacuousValueUpdates = 0x00000001,

		/// Entries are held in blocks, each of which is pre-allocated for a fixed number of entries. This fixed number of entries 
		/// per block can be calculated based on the number of entries that will cause the block to be allocated in the large object 
		/// heap. This calculation is approximate since it is based on both: implementation details of the GC, and; the marshaling 
		/// sizes--calculated when the dictionary is initialized--of the generic type arguments.
		AttemptScaleToLargeObjectHeap = 0x00000002,

		/// Enable the bucket table to be shrunk when the number of elements in the dictionary falls below half of what it 
		/// once was. This does not affect the size of the entry tables, which cannot be efficiently shrunk.
		ShrinkBucketTable = 0x00000004,
	};

	/// <summary>
	/// updating of value types can have 3 options:
	/// - unsafe
	/// - non-atomic readback-after-write with full fence (but torn read is still possible, and can match the readback)
	/// - atomic
	/// </summary>



	internal Options m_options = Options.AttemptScaleToLargeObjectHeap;

	public Int64 m_count = 0;

	public Int64 m_resize_counts;

	readonly bool f_null_check;
	public
	volatile Config m_config;

	V DefaultValue = default(V);

	enum Op
	{
		Remove,
		Get,
		Update,
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	// Atomic operations
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

	public V GetOrAdd(K key, V value)
	{
		throw new NotImplementedException();
	}

	public bool TryAdd(K key, V value)
	{
		return _try_add(key, value);
	}

	public bool TryGetValue(K key, out V value)
	{
		return _try_get_remove_update(Op.Get, key, ref DefaultValue, out value);
	}

	public bool TryRemove(K key, out V value)
	{
		return _try_get_remove_update(Op.Remove, key, ref DefaultValue, out value);
	}

	public bool TryUpdate(K key, V newValue, V comparisonValue)
	{
		return _try_update_atomic(key, ref newValue, ref comparisonValue);
	}


	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	// IDictionary<TKey, TValue>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

	/// <summary>
	/// Although this member is thread-safe, the value should not be considered added if another thread could have removed
	/// it during the time this function is returning.
	/// </summary>
	/// <param name="key"></param>
	/// <param name="value"></param>
	/// <exception cref="ArgumentException">An element with the same key already exists.</exception>
	/// <exception cref="ArgumentNullException"><paramref name="key"/> cannot be null.</exception>
	public void Add(K key, V value)
	{
		if (!_try_add(key, value))
			throw new ArgumentException("An item with the same key has already been added.");
	}

	/// <summary>
	/// Although this member is thread-safe, the result should be considered advisory if another thread could have re-inserted
	/// an item with the same key by the time the return value is examined.
	/// </summary>
	public bool Remove(K key)
	{
		V _dont_need;
		return _try_get_remove_update(Op.Remove, key, ref DefaultValue, out _dont_need);
	}

	public V this[K key]
	{
		get
		{
			V v;
			if (!_try_get_remove_update(Op.Get, key, ref DefaultValue, out v))
				throw new KeyNotFoundException();
			return v;
		}
		set
		{
			V _dont_need;
			if (!_try_get_remove_update(Op.Update, key, ref value, out _dont_need))
				throw new KeyNotFoundException();
		}
	}

	/// <summary>
	/// Although this member is thread-safe, the result should be considered advisory if another thread could
	/// have removed the item by the time the return value is examined.
	/// </summary>
	public bool ContainsKey(K key)
	{
		V _dont_need;
		return _try_get_remove_update(Op.Get, key, ref DefaultValue, out _dont_need);
	}

	public ICollection<K> Keys
	{
		get { throw new NotImplementedException(); }
	}

	public ICollection<V> Values
	{
		get { throw new NotImplementedException(); }
	}

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	// ICollection<KeyValuePair<TKey,TValue>> follows
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

	/// <summary>
	/// Although this property is thread-safe, the result should be considered advisory if another thread could have added or
	/// removed items by the time the return value is examined.
	/// </summary>
	public int Count
	{
		get
		{
			Config.BucketListResize bhs = m_config.m_buckets as Config.BucketListResize;
			if (bhs != null)
				bhs._check_assist();

			return m_config.m_count;
		}
	}

	/// <summary>
	/// Although this member is thread-safe, the item should not be considered added if another thread could have removed
	/// it during the time this function is returning.
	/// </summary>
	/// <param name="item"></param>
	void ICollection<KeyValuePair<K, V>>.Add(KeyValuePair<K, V> item)
	{
		if (!_try_add(item.Key, item.Value))
			throw new ArgumentException("An item with the same key has already been added.");
	}

	/// <summary>
	/// Although this member is thread-safe, the result should be considered advisory if another thread could have re-inserted
	/// an item with the same key by the time the return value is examined.
	/// </summary>
	/// <param name="item"></param>
	/// <returns></returns>
	bool ICollection<KeyValuePair<K, V>>.Remove(KeyValuePair<K, V> item)
	{
		V _dont_need;
		return _try_get_remove_update(Op.Remove, item.Key, ref DefaultValue, out _dont_need);
	}

	/// <summary>
	/// Although this member is thread-safe, the dictionary should not be considered empty if another thread could have
	/// added items to the dictionary during the time this function is returning. Changes that were initiated prior to 
	/// this call but which haven't completed yet may be applied to either before or after the clearing operation. 
	/// </summary>
	public void Clear()
	{
		Config.BucketListResize bhs = m_config.m_buckets as Config.BucketListResize;
		if (bhs != null)
			bhs._check_assist();

		m_config = new Config(this);
	}

	/// <summary>
	/// Although this member is thread-safe, the result should be considered advisory if another thread could
	/// have removed the item by the time the return value is examined.
	/// </summary>
	/// <param name="item"></param>
	/// <returns></returns>
	bool ICollection<KeyValuePair<K, V>>.Contains(KeyValuePair<K, V> item)
	{
		V v;
		return _try_get_remove_update(Op.Get, item.Key, ref DefaultValue, out v) && v.Equals(item.Value);
	}

	/// <summary>
	/// Copy a snapshot of the dictionary contents to an array of key-value pairs.
	/// </summary>
	/// <param name="array"></param>
	/// <param name="arrayIndex"></param>
	void ICollection<KeyValuePair<K, V>>.CopyTo(KeyValuePair<K, V>[] array, int arrayIndex)
	{
		throw new NotImplementedException();
	}

	/// <summary>
	/// This property has the value 'true'
	/// </summary>
	bool ICollection<KeyValuePair<K, V>>.IsReadOnly
	{
		get { return true; }
	}

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	// IEnumerable<KeyValuePair<TKey,TValue>> follows
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

	public IEnumerator<KeyValuePair<K, V>> GetEnumerator()
	{
		throw new NotImplementedException();
	}

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	// IEnumerable follows
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

	System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
	{
		return GetEnumerator();
	}
};