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


public partial class LockFreeDictionary<K, V> : IDictionary<K, V>
	where K : IEquatable<K>
	where V : IEquatable<V>
{
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// 
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	uint _check_key(ref K key, out int mtid)
	{
		if (Config.EntryBlock.Entry.f_key_null_check && key == null)
			throw new ArgumentNullException("key");

		mtid = -1;
		m_config.CheckAssist(ref mtid);

		/// hash zero indicates a freelist entry so ensure that it's not used
		uint h = (uint)key.GetHashCode();
		return h == 0 ? 1 : h;
	}

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// Obtain a chain head that doesn't contain the key and attempt to atomically insert the new Entry into it.
	/// If the atomic operation fails, start over.
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	bool _try_add(ref K key, V value)
	{
		int mtid;
		uint h = _check_key(ref key, out mtid);

		Config.EntryBlock.Entry e_new = new Config.EntryBlock.Entry(h, key, value);
		while (true)
		{
			Config.BucketList.BucketHead bh;
			if (!m_config.m_buckets.GetBucketHead(ref mtid, h, out bh))
				continue;

			int gx;
			if (!bh.TryFindKey(h, key, out gx))
				continue;

			if (gx != -1)
				return false;

			if (bh.TryInsertFirstItem(ref mtid, ref e_new))
			{
				bh.Config.IncrementCountAndCheck(ref mtid);
				return true;
			}
		}
	}

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// Obtain a chain head that doesn't contain the key and attempt to atomically insert the new Entry into it.
	/// If the atomic operation fails, start over.
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	V _transact_get_add(ref K key, V in_value)
	{
		int mtid;
		uint h = _check_key(ref key, out mtid);

		Config.EntryBlock.Entry e_new = new Config.EntryBlock.Entry(h, key, in_value);
		while (true)
		{
			Config.BucketList.BucketHead bh;
			int gx;
			if (m_config.m_buckets.GetBucketHead(ref mtid, h, out bh) && bh.TryFindKey(h, key, out gx))
			{
				/// get
				if (gx != -1)
				{
					V out_value;
					if (bh.TryGetValue(gx, out out_value))
						return out_value;
				}

				/// add
				if (bh.TryInsertFirstItem(ref mtid, ref e_new))
				{
					bh.Config.IncrementCountAndCheck(ref mtid);
					return in_value;
				}
			}
		}
	}


	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// 
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	bool _try_get(ref K key, out V out_value)
	{
		int mtid;
		uint h = _check_key(ref key, out mtid);

		while (true)
		{
			Config.BucketList.BucketHead bh;
			int gx;
			if (m_config.m_buckets.GetBucketHead(ref mtid, h, out bh) && bh.TryFindKey(h, key, out gx))
			{
				if (gx == -1)
				{
					out_value = default(V);
					return false;
				}

				if (bh.TryGetValue(gx, out out_value))
					return true;
			}
		}
	}


	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// 
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	bool _try_remove(ref K key, out V out_value)
	{
		int mtid;
		uint h = _check_key(ref key, out mtid);

		while (true)
		{
			Config.BucketList.BucketHead bh;
			if (!m_config.m_buckets.GetBucketHead(ref mtid, h, out bh))
				continue;

			int c = bh.CapturedEntryCount;
			if (c == 0)
				break;

			int gx_found, i, gx;
			if (!bh.TryFindKey(h, ref key, out gx_found, out i, out gx))
				continue;

			if (gx_found == -1)
				break;

			if (!bh.TryGetValue(gx_found, out out_value))
				continue;

			TryResult tr = TryResult.Impossible;
			if (c == 1)
			{
				/// remove the only one
				gx = -1;
				/// continues below...
			}
			else if (i == 0 || (tr = bh.CanCarouselTo(gx, gx_found, ref bh)) != TryResult.Impossible)
			{
				if (tr == TryResult.Expired)
					continue;

				/// remove the first one
				gx = bh.Config.GetNextHot(gx_found);
				/// continues below...
			}
			else if (i == c - 1 || bh.TryRotateToEnd(ref mtid, gx, i, ref bh))
			{
				/// remove the last one by decrementing the count
				gx = bh.CapturedFirstEntryIndex;
				/// continues below...
			}
			else
				continue;

			/// ...final update
			if (bh.TryUpdate(gx, c - 1))
			{
				bh.Config.ReleaseIndex(ref mtid, gx_found);
				Interlocked.Decrement(ref bh.Config.m_count);
				return true;
			}
		}
		/// nothing to remove
		out_value = default(V);
		return false;
	}

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// 
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	bool _try_update(ref K key, ref V value)
	{
		int mtid;
		uint h = _check_key(ref key, out mtid);

		Config.EntryBlock.Entry e_update = new Config.EntryBlock.Entry(h, key, value);
		while (true)
		{
			Config.BucketList.BucketHead bh;
			if (!m_config.m_buckets.GetBucketHead(ref mtid, h, out bh))
				continue;

			int c = bh.CapturedEntryCount;
			if (c == 0)
				return false;

			int gx_found, i, gx_last;
			if (!bh.TryFindKey(h, ref key, out gx_found, out i, out gx_last))
				continue;

			if (gx_found == -1)
				return false;

			Config cfg = bh.Config;
			if ((cfg.d.m_options & Options.CheckForVacuousValueUpdates) > 0)
			{
				V cur_val;
				if (bh.TryGetValue(gx_found, out cur_val) && Config.EntryBlock.Entry.CompareValues(cur_val, value))
					return true;
			}

			TryResult tr = TryResult.Impossible;
			if (c == 1)
			{
				e_update.gx_next = -1;
				/// continues below...
			}
			else if (i == 0 || (tr = bh.CanCarouselTo(gx_last, gx_found, ref bh)) != TryResult.Impossible)
			{
				if (tr == TryResult.Expired)
					continue;

				e_update.gx_next = cfg.GetNextHot(bh.CapturedFirstEntryIndex);
				/// continues below...
			}
			else if (i == c - 1 || bh.TryRotateToEnd(ref mtid, gx_last, i, ref bh))
			{
				e_update.gx_next = bh.CapturedFirstEntryIndex;
				/// continues below...
			}
			else
				continue;

			/// ...try e_update
			if (bh.TryReplaceFirst(ref mtid, ref e_update))
			{
				cfg.ReleaseIndex(ref mtid, gx_found);
				return true;
			}
		}
	}


	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// 
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	bool _transact_update(ref K key, ref V in_value, ref V cmp_value)
	{
		int mtid;
		uint h = _check_key(ref key, out mtid);
		Config.EntryBlock.Entry e_update = new Config.EntryBlock.Entry(h, key, in_value);

		while (true)
		{
			Config.BucketList.BucketHead bh;
			if (!m_config.m_buckets.GetBucketHead(ref mtid, h, out bh))
				continue;

			int c = bh.CapturedEntryCount;
			if (c == 0)
				return false;

			int gx_found, i, gx_last;
			if (!bh.TryFindKey(h, ref key, out gx_found, out i, out gx_last))
				continue;

			if (gx_found == -1)
				return false;

			V cur_val;
			if (!bh.TryGetValue(gx_found, out cur_val))
				continue;

			if (!Config.EntryBlock.Entry.CompareValues(cur_val, cmp_value))
				return false;

			Config cfg = bh.Config;
			if ((cfg.d.m_options & Options.CheckForVacuousValueUpdates) > 0 && Config.EntryBlock.Entry.CompareValues(in_value, cur_val))
				return true;

			TryResult tr = TryResult.Impossible;
			if (c == 1)
			{
				e_update.gx_next = -1;
				/// continues below...
			}
			else if (i == 0 || (tr = bh.CanCarouselTo(gx_last, gx_found, ref bh)) != TryResult.Impossible)
			{
				if (tr == TryResult.Expired)
					continue;

				e_update.gx_next = cfg.GetNextHot(bh.CapturedFirstEntryIndex);
				/// continues below...
			}
			else if (i == c - 1 || bh.TryRotateToEnd(ref mtid, gx_last, i, ref bh))
			{
				e_update.gx_next = bh.CapturedFirstEntryIndex;
				/// continues below...
			}
			else
				continue;

			/// ...try e_update
			if (bh.TryReplaceFirst(ref mtid, ref e_update))
			{
				cfg.ReleaseIndex(ref mtid, gx_found);
				return true;
			}
		}
	}
};