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

using glue;
using glue.Debugging;
using glue.Collections.XSpinLock;

#pragma warning disable 0649

namespace agree
{
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// 
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public class HybridSpinLockTray : HybridTray
	{
		public HybridSpinLockTray(int tix, TypeMgr tm, int last_mark, int next_id)
			: base(tix, tm, last_mark, next_id)
		{
			for (int i = 0; i < Pools.Length; i++)
				Pools[i] = new Pool(this, i);
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		sealed class Pool : ConstraintPool
		{
			public Pool(Tray tr, int i_feat)
				: base(tr, i_feat)
			{
				Nop.X(fsp1, fsp2);
				singl = new Dictionary<Int32, Edge>();
				var o = new FalseSharing.Padding2048();
				multi = new Dictionary<Int32, Edge>();
				Nop.X(o);
			}

			FalseSharing.Padding60 fsp1;
			SpinLock m_lock = new SpinLock(false);	// do not make this 'readonly'
			FalseSharing.Padding60 fsp2;

			Dictionary<Int32, Edge> singl;
			Dictionary<Int32, Edge> multi;

			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// 
			/// </summary>
			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			public override Edge GetEdge(Int32 m)
			{
				Debug.Assert(m != 0);
				Edge c;
				if (m <= ((HybridTray)tr).last_frozen_mark)
					singl.TryGetValue(m, out c);
				else
				{
					bool entered = false;
					try
					{
						m_lock.Enter(ref entered);
						multi.TryGetValue(m, out c);
					}
					finally
					{
						if (entered)
							m_lock.Exit(false);
					}
				}
				return c;
			}

			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// 
			/// </summary>
			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			public override bool TryGetEdge(int m, out Edge e)
			{
				Debug.Assert(m != 0);
				if (m <= ((HybridTray)tr).last_frozen_mark)
					return singl.TryGetValue(m, out e);
				bool entered = false;
				try
				{
					m_lock.Enter(ref entered);
					return multi.TryGetValue(m, out e);
				}
				finally
				{
					if (entered)
						m_lock.Exit(false);
				}
			}

			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// Store the constraint for the specified mark. Handle Edge value semantics properly.
			/// Allows detached mark to be set for bare or atomic types
			/// </summary>
			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			public override void SetEdge(Int32 m, Edge c)
			{
				Debug.Assert(m != 0 && m >= tr._protect_mark);
				if (m <= ((HybridTray)tr).last_frozen_mark)
					singl[m] = c;
				else
				{
					bool entered = false;
					try
					{
						m_lock.Enter(ref entered);
						multi[m] = c;
					}
					finally
					{
						if (entered)
							m_lock.Exit(false);
					}
				}
			}

			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// 
			/// </summary>
			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			public override void RemoveEdge(Int32 m)
			{
				Debug.Assert(m != 0 && m >= tr._protect_mark);
				if (m <= ((HybridTray)tr).last_frozen_mark)
					singl.Remove(m);
				else
				{
					bool entered = false;
					try
					{
						m_lock.Enter(ref entered);
						multi.Remove(m);
					}
					finally
					{
						if (entered)
							m_lock.Exit(false);
					}
				}
			}

			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// 
			/// </summary>
			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			public override bool TryRemoveEdge(Int32 m, out Edge e)
			{
				Debug.Assert(m != 0 && m >= tr._protect_mark);
				if (m <= ((HybridTray)tr).last_frozen_mark)
					return singl.TryGetValue(m, out e) ? singl.Remove(m) : false;
				bool entered = false;
				try
				{
					m_lock.Enter(ref entered);
					return multi.TryGetValue(m, out e) ? multi.Remove(m) : false;
				}
				finally
				{
					if (entered)
						m_lock.Exit(false);
				}
			}

			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// 
			/// </summary>
			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			public override bool ContainsInMark(Int32 m)
			{
				Debug.Assert(m != 0);
				if (m <= ((HybridTray)tr).last_frozen_mark)
					return singl.ContainsKey(m);
				bool entered = false;
				try
				{
					m_lock.Enter(ref entered);
					return multi.ContainsKey(m);
				}
				finally
				{
					if (entered)
						m_lock.Exit(false);
				}
			}

			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// 
			/// </summary>
			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			public override int Count
			{
				get { return singl.Count + multi.Count; }
			}

			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// 
			/// </summary>
			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			public override IEnumerable<int> Marks
			{
				get
				{
					tr.mre_truncate_ok.WaitOne();
					return singl.Keys.Concat(multi.Keys);
				}
			}

			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// 
			/// </summary>
			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			public override IEnumerable<Edge> Edges
			{
				get
				{
					tr.mre_truncate_ok.WaitOne();
					return singl.Values.Concat(multi.Values);
				}
			}

			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// 
			/// </summary>
			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			public override IEnumerable<KeyValuePair<int, Edge>> PoolEdges
			{
				get
				{
					tr.mre_truncate_ok.WaitOne();
					return singl.Concat(multi);
				}
			}
		};
	};


	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// 
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public class HybridSpinLockRwTray : HybridTray
	{
		public HybridSpinLockRwTray(int tix, TypeMgr tm, int last_mark, int next_id)
			: base(tix, tm, last_mark, next_id)
		{
			for (int i = 0; i < Pools.Length; i++)
				Pools[i] = new Pool(this, i);
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		sealed class Pool : ConstraintPool
		{
			public Pool(Tray tr, int i_feat)
				: base(tr, i_feat)
			{
				Nop.X(fsp1, fsp2, fsp3, fsp4);
				singl = new Dictionary<Int32, Edge>(100000);
				var o = new FalseSharing.Padding2048();
				multi = new Dictionary<Int32, Edge>(100000);
				Nop.X(o);
			}
			private const int MAXIMUM_WAITERS = 0x7ffffffe;
			private const int SPINNING_FACTOR = 100;
			private const int SLEEP_ONE_FREQUENCY = 40;
			private const int SLEEP_ZERO_FREQUENCY = 10;
			private const int LOCK_ANONYMOUS_OWNED = 1;
			private const int WAITERS_MASK = 0x7ffffffe;
			private const int LOCK_UNOWNED = 0;

			FalseSharing.Padding128 fsp1;
			private volatile int m_lock;
			FalseSharing.Padding128 fsp2;
			int c_readers = 0;
			FalseSharing.Padding128 fsp3;

			Dictionary<Int32, Edge> singl;
			Dictionary<Int32, Edge> multi;

			FalseSharing.Padding128 fsp4;

			readonly int processorCount = Environment.ProcessorCount;

			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// 
			/// </summary>
			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			public override Edge GetEdge(Int32 m)
			{
				Debug.Assert(m != 0);
				Edge c;
				if (m <= ((HybridTray)tr).last_frozen_mark)
					singl.TryGetValue(m, out c);
				else
				{
					AddReader();
					multi.TryGetValue(m, out c);
					Interlocked.Decrement(ref c_readers);
				}
				return c;
			}

			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// 
			/// </summary>
			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			public override bool TryGetEdge(int m, out Edge e)
			{
				Debug.Assert(m != 0);
				if (m <= ((HybridTray)tr).last_frozen_mark)
					return singl.TryGetValue(m, out e);

				AddReader();
				bool b = multi.TryGetValue(m, out e);
				Interlocked.Decrement(ref c_readers);
				return b;
			}

			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// Store the constraint for the specified mark. Handle Edge value semantics properly.
			/// Allows detached mark to be set for bare or atomic types
			/// </summary>
			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			public override void SetEdge(Int32 m, Edge c)
			{
				Debug.Assert(m != 0 && m >= tr._protect_mark);
				if (m <= ((HybridTray)tr).last_frozen_mark)
					singl[m] = c;
				else
				{
					EnterWriter();
					multi[m] = c;
					m_lock--;
				}
			}

			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// 
			/// </summary>
			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			public override void RemoveEdge(Int32 m)
			{
				Debug.Assert(m != 0 && m >= tr._protect_mark);
				if (m <= ((HybridTray)tr).last_frozen_mark)
					singl.Remove(m);
				else
				{
					EnterWriter();
					multi.Remove(m);
					m_lock--;
				}
			}

			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// 
			/// </summary>
			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			public override bool TryRemoveEdge(Int32 m, out Edge e)
			{
				Debug.Assert(m != 0 && m >= tr._protect_mark);
				if (m <= ((HybridTray)tr).last_frozen_mark)
					return singl.TryGetValue(m, out e) ? singl.Remove(m) : false;

				EnterWriter();
				bool b = multi.TryGetValue(m, out e) ? multi.Remove(m) : false;
				m_lock--;
				return b;
			}

			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// 
			/// </summary>
			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			public override bool ContainsInMark(Int32 m)
			{
				Debug.Assert(m != 0);
				if (m <= ((HybridTray)tr).last_frozen_mark)
					return singl.ContainsKey(m);

				AddReader();
				bool b = multi.ContainsKey(m);
				Interlocked.Decrement(ref c_readers);
				return b;
			}

			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// 
			/// </summary>
			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			public override int Count
			{
				get { return singl.Count + multi.Count; }
			}

			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// 
			/// </summary>
			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			public override IEnumerable<int> Marks
			{
				get
				{
					tr.mre_truncate_ok.WaitOne();
					return singl.Keys.Concat(multi.Keys);
				}
			}

			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// 
			/// </summary>
			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			public override IEnumerable<Edge> Edges
			{
				get
				{
					tr.mre_truncate_ok.WaitOne();
					return singl.Values.Concat(multi.Values);
				}
			}

			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// 
			/// </summary>
			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			public override IEnumerable<KeyValuePair<int, Edge>> PoolEdges
			{
				get
				{
					tr.mre_truncate_ok.WaitOne();
					return singl.Concat(multi);
				}
			}

			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// 
			/// </summary>
			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			void AddReader()
			{
				int owner = m_lock;
				if ((owner & LOCK_ANONYMOUS_OWNED) == LOCK_UNOWNED)
				{
					if (Interlocked.CompareExchange(ref m_lock, owner | 1, owner) == owner)
					{
						Interlocked.Increment(ref c_readers);
						m_lock--;
						return;
					}
				}

				SpinWait wait = new SpinWait();
				while (true)
				{
					owner = m_lock;
					if ((owner & LOCK_ANONYMOUS_OWNED) == LOCK_UNOWNED)
					{
						if (Interlocked.CompareExchange(ref m_lock, owner | 1, owner) == owner)
						{
							Interlocked.Increment(ref c_readers);
							m_lock--;
							return;
						}
					}
					else if ((owner & WAITERS_MASK) == MAXIMUM_WAITERS || Interlocked.CompareExchange(ref m_lock, owner + 2, owner) == owner)
						break;
					wait.SpinOnce();
				}

				int c_wait = ((owner + 2) & WAITERS_MASK) / 2;
				if (c_wait < processorCount)
				{
					int j = 1;
					for (int i = 1; i <= c_wait * SPINNING_FACTOR; i++)
					{
						Thread.SpinWait((c_wait + i) * SPINNING_FACTOR * j);
						if (j < processorCount)
							j++;

						owner = m_lock;
						if ((owner & LOCK_ANONYMOUS_OWNED) == LOCK_UNOWNED)
						{
							if (Interlocked.CompareExchange(ref m_lock, ((owner & WAITERS_MASK) == 0 ? 0 : owner - 2) | 1, owner) == owner)
							{
								Interlocked.Increment(ref c_readers);
								m_lock--;
								return;
							}
						}
					}
				}

				for (int iter = 0; ; iter++)
				{
					owner = m_lock;
					if ((owner & LOCK_ANONYMOUS_OWNED) == LOCK_UNOWNED)
					{
						if (Interlocked.CompareExchange(ref m_lock, ((owner & WAITERS_MASK) == 0 ? 0 : owner - 2) | 1, owner) == owner)
						{
							Interlocked.Increment(ref c_readers);
							m_lock--;
							return;
						}
					}
					if ((iter % SLEEP_ONE_FREQUENCY) == 0)
						Thread.Sleep(1);
					else if ((iter % SLEEP_ZERO_FREQUENCY) == 0)
						Thread.Sleep(0);
					else
						Thread.Yield();
				}

			}

			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// 
			/// </summary>
			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			void EnterWriter()
			{
				int owner = m_lock;
				if ((owner & LOCK_ANONYMOUS_OWNED) == LOCK_UNOWNED)
				{
					if (Interlocked.CompareExchange(ref m_lock, owner | 1, owner) == owner)
					{
						while (c_readers > 0)
							Thread.SpinWait(0);
						return;
					}
				}

				SpinWait wait = new SpinWait();
				while (true)
				{
					owner = m_lock;
					if ((owner & LOCK_ANONYMOUS_OWNED) == LOCK_UNOWNED)
					{
						if (Interlocked.CompareExchange(ref m_lock, owner | 1, owner) == owner)
						{
							while (c_readers > 0)
								Thread.SpinWait(0);
							return;
						}
					}
					else if ((owner & WAITERS_MASK) == MAXIMUM_WAITERS || Interlocked.CompareExchange(ref m_lock, owner + 2, owner) == owner)
						break;
					wait.SpinOnce();
				}

				int c_wait = ((owner + 2) & WAITERS_MASK) / 2;
				if (c_wait < processorCount)
				{
					int j = 1;
					for (int i = 1; i <= c_wait * SPINNING_FACTOR; i++)
					{
						Thread.SpinWait((c_wait + i) * SPINNING_FACTOR * j);
						if (j < processorCount)
							j++;

						owner = m_lock;
						if ((owner & LOCK_ANONYMOUS_OWNED) == LOCK_UNOWNED)
						{
							if (Interlocked.CompareExchange(ref m_lock, ((owner & WAITERS_MASK) == 0 ? 0 : owner - 2) | 1, owner) == owner)
							{
								while (c_readers > 0)
									Thread.SpinWait(0);
								return;
							}
						}
					}
				}

				for (int iter = 0; ; iter++)
				{
					owner = m_lock;
					if ((owner & LOCK_ANONYMOUS_OWNED) == LOCK_UNOWNED)
					{
						if (Interlocked.CompareExchange(ref m_lock, ((owner & WAITERS_MASK) == 0 ? 0 : owner - 2) | 1, owner) == owner)
						{
							while (c_readers > 0)
								Thread.SpinWait(0);
							return;
						}
					}
					if ((iter % SLEEP_ONE_FREQUENCY) == 0)
						Thread.Sleep(1);
					else if ((iter % SLEEP_ZERO_FREQUENCY) == 0)
						Thread.Sleep(0);
					else
						Thread.Yield();
				}
			}

		};
	};

}