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

#pragma warning disable 0420

[DebuggerDisplay("{ToString(),nq}")]
public partial class LockFreeDictionary<K, V> : IDictionary<K, V>
	where K : IEquatable<K>
	where V : IEquatable<V>
{
	/// <summary>
	/// Read a 64-bit value (a.) without tearing and (b.) with cache coherence across multiple processors. This requires
	/// coordination with the source compiler as well as the runtime JIT compiler.
	/// </summary>
	static Int64 AtomicRead64(ref Int64 p)
	{
		/// When running in a 32-bit environment, use the CLR support.
		if (IntPtr.Size == 4)
			return Interlocked.CompareExchange(ref p, 0, 0);

		/// In the 64-bit environment, tearing is not an issue so we only need to request cache coherence. The following 
		/// is the same as 'return Thread.VolatileRead(ref p)'
		Int64 i64 = p;
		Thread.MemoryBarrier();
		return i64;
	}

	public
	partial class Config
	{
		//	[DebuggerDisplay("{ToString(),nq}")]
		public partial
		class EntryBlock
		{
			readonly int i_block;
			internal int BlockIndex { get { return i_block; } }

			[DebuggerDisplay("{ToString(),nq}")]
			public partial struct Entry
			{
				public override string ToString()
				{
					if (gx_next == 0 && hash == 0 && Object.Equals(key, default(K)) && Object.Equals(value, default(V)))
						return "";
					return String.Format("              next: {0:X8} hash: {1:X8} key: {2}"/* value: {4}"*/,
						gx_next,
						hash,
						key/*, 
					e.value*/);
				}

				public String BucketCurrent
				{
					get
					{
						Config cfg = Config.BucketList.BucketHead._cfg;
						if (cfg == null)
							return "n/a";
						uint c = cfg.m_buckets.BucketCount;
						return String.Format("bx for {0} buckets: {1:X8}", c, hash % c);
					}
				}
				public String BucketPrevious
				{
					get
					{
						Config cfg = Config.BucketList.BucketHead._cfg;
						if (cfg == null)
							return "n/a";
						Config.BucketListResize bls = cfg.m_buckets as Config.BucketListResize;
						if (bls == null)
							return "n/a";
						uint c = bls._blp.BucketCount;
						return String.Format("bx for {0} buckets: {1:X8}", c, hash % c);
					}
				}

			};

			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// 
			/// </summary>
			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			[DebuggerDisplay("{ToString(),nq}")]
			public partial struct SlotAddr
			{
				public SlotAddr(EntryBlock eb, int offs)
				{
					this.eb = eb;
					this.offs = offs;
				}
				readonly int offs;
				readonly EntryBlock eb;

				public int GlobalIndex { get { return (eb.i_block << eb.cfg.EntriesPerBlockPower) + offs; } }

				public bool TryGetEntryNext(out int gx)
				{
					gx = eb.GetNextHot(offs);
					return (uint)gx < (uint)Thread.VolatileRead(ref eb.cfg.m_gx_next);
				}

				public Entry _entry
				{
					get
					{
						Entry e;
						eb.GetEntryHot(offs, out e);
						return e;
					}
				}
				public override string ToString()
				{
					return new Config.BucketList.BucketListDebugView.BucketDisplay.SlotAddrDisplay(this).ToString();
				}

				public EntryBlock _eb { get { return eb; } }
			};

			public Config _cfg { get { return cfg; } }

			public override string ToString()
			{
				return String.Format("block: {0} length: {1} last=={2}", i_block, rg.Length, next == null);
			}

		};

		//		public double DataLoad { get { return (double)dict.Count / m_buckets.BucketCount; } }
		//	public double TotalLoad { get { return (double)(dict.Count + m_c_free) / m_buckets.BucketCount; } }
		internal partial class BucketListResize : BucketList
		{
			public BucketList _blp { get { return blp; } }
		}

		public void CheckBuckets()
		{
			m_buckets.Check();
		}

		[DebuggerDisplay("{DebugInfo(),nq} ")]
		[DebuggerTypeProxy(typeof(LockFreeDictionary<,>.Config.BucketList.BucketListDebugView))]
		internal partial class BucketList
		{


			public class BucketListDebugView
			{
				BucketList bl;
				public BucketListDebugView(LockFreeDictionary<K, V>.Config.BucketList bl)
				{
					this.bl = bl;
				}

				[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
				public BucketDisplay[] buckets
				{
					get
					{
						return bl.rg
							.Select((v, i) => new BucketDisplay(bl, i))
							//.Where(bd => bd.v != 0)
							.ToArray();
					}
				}

				[DebuggerDisplay("{ToString(),nq}")]
				//	[DebuggerTypeProxy(typeof(LockFreeDictionary<,>.Config.BucketList.BucketListDebugView.BucketDisplay.BucketEntryDebugView))]
				public struct BucketDisplay
				{
					[DebuggerBrowsable(DebuggerBrowsableState.Never)]
					public BucketList bl;
					[DebuggerBrowsable(DebuggerBrowsableState.Never)]
					public uint bx;
					[DebuggerBrowsable(DebuggerBrowsableState.Never)]
					public UInt64 v;
					public BucketDisplay(BucketList bl, int ix)
					{
						this.bl = bl;
						this.bx = (uint)ix;
						this.v = bl != null && bl.rg != null && ix < bl.rg.Length ? (UInt64)bl.rg[ix] : 0;
					}
					public BucketDisplay(LockFreeDictionary<K, V>.Config.BucketList.BucketHead bh)
					{
						this.bl = bh._bl;
						this.bx = bh._bx;
						this.v = (UInt64)bh._v;
					}

					public override string ToString()
					{
						if (bl == null)
							return "(null)";
						if (bl.rg == null)
							return "(rg null)";

						int c = BucketHead.ExtractEntryCount((long)v);

						String sc0;
						if (c != 0 && ValidCount() != c)
							sc0 = "✖";
						else if (v == (UInt64)AtomicRead64(ref bl.rg[bx]))
							sc0 = "✓";
						else
							sc0 = "Ⓧ";

						String sc3 = c == 0 ? "     " : "c: " + c.ToString().PadRight(2);

						return String.Format("{0} bx: {1:X8} {2} gx: {3:X8}  seq: {4:X8}  ({5:X16})",
							sc0,
							bx,
							sc3,
							BucketHead.CapturedFirstSlotIndexV((long)v),
							BucketHead._seq_no_V((long)v),
							v);
					}

					[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
					public SlotAddrDisplay[] entries
					{
						get
						{
							return GetEntries().ToArray();
						}
					}
					IEnumerable<SlotAddrDisplay> GetEntries()
					{
						if (bl == null)
							yield break;
						int count = BucketHead.ExtractEntryCount((long)v);

						int gx = BucketHead.CapturedFirstSlotIndexV((long)v);
						for (int i = 0; i < count; i++)
						{
							yield return new SlotAddrDisplay(bl.cfg, gx);
							if ((uint)gx >= (uint)Thread.VolatileRead(ref bl.cfg.m_gx_next))
								yield break;
							EntryBlock.Entry e;
							bl.cfg.GetEntryHot(gx, out e);
							if (gx == e.gx_next)
								yield break;
							gx = e.gx_next;
						}
					}
					int ValidCount()
					{
						if (bl == null)
							return 0;
						int count = BucketHead.ExtractEntryCount((long)v);
						int actual = 0;

						int gx = BucketHead.CapturedFirstSlotIndexV((long)v);
						for (int i = 0; i < count; i++)
						{
							if ((uint)gx >= (uint)Thread.VolatileRead(ref bl.cfg.m_gx_next))
								break;

							EntryBlock.Entry e;
							bl.cfg.GetEntryHot(gx, out e);
							if (gx == e.gx_next)
								break;

							gx = e.gx_next;
							actual++;
						}
						return actual;
					}

					[DebuggerDisplay("{ToString(),nq}")]
					public struct SlotAddrDisplay
					{
						[DebuggerBrowsable(DebuggerBrowsableState.Never)]
						int gx;
						[DebuggerBrowsable(DebuggerBrowsableState.Never)]
						Config cfg;
						public SlotAddrDisplay(Config cfg, int gx)
						{
							this.gx = gx;
							this.cfg = cfg;
						}
						public SlotAddrDisplay(EntryBlock.SlotAddr sa)
						{
							this.cfg = null;
							this.gx = 0;
							if (sa._eb != null)
							{
								this.cfg = sa._eb._cfg;
								this.gx = sa.GlobalIndex;
							}
						}

						public override string ToString()
						{
							if (gx == Freelist.FreeListTerminator)
								return String.Format("Ⓧ {0:X8} Freelist terminator is not a valid global index", gx);
							if (gx == -1)
								return String.Format("Ⓧ FFFFFFFF is not a valid global index", gx);
							if (cfg == null)
								return "(no config)";
							if ((uint)gx >= (uint)Thread.VolatileRead(ref cfg.m_gx_next))
								return String.Format("Ⓧ {0:X8} was never issued as a valid global index", gx);

							EntryBlock.Entry e;
							cfg.GetEntryHot(gx, out e);
							String s = String.Format(" gx: {0:X8} {1}", gx, e.ToString().Trim());
							if (gx == e.gx_next)
								s = "!loop" + s;
							return s;
						}
					};
				};
			}

			public void Check()
			{
				for (uint i = 0; i < BucketCount; i++)
				{
					var bh = new BucketHead(this, i);
					int gx = bh.CapturedFirstEntryIndex;
					K[] keys = new K[bh.CapturedEntryCount];
					for (int j = 0; j < bh.CapturedEntryCount; j++)
					{
						if ((uint)gx >= (uint)Thread.VolatileRead(ref cfg.m_gx_next))
						{
							if (Debugger.IsAttached)
								Debugger.Break();
							throw new Exception("check: global index out of range");
						}
						EntryBlock.Entry e;
						cfg.GetEntryHot(gx,out e);
						keys[j] = e.key;
						if (gx == e.gx_next)
						{
							if (Debugger.IsAttached)
								Debugger.Break();
							throw new Exception("check: loop");
						}
						gx = e.gx_next;
					}
					if (keys.Distinct().Count() != keys.Length)
					{
						if (Debugger.IsAttached)
							Debugger.Break();
						throw new Exception("check: duplicate key");
					}
				}
			}

			internal int EntryCount
			{
				get
				{
					int c = 0;
					for (int i = 0; i < rg.Length; i++)
						c += BucketHead.ExtractEntryCount(AtomicRead64(ref rg[i]));
					return c;
				}
			}

			public String DebugInfo()
			{
				return String.Format("{0} ({1}) buckets: {2} entries: {3}",
					this.GetType().Name,
					cfg.d.m_config.m_buckets == this ? "current" : "not current",
					rg.Length,
					this.EntryCount);
			}

#if false
			public override string ToString()
			{
				return String.Format("{0} {1} ({2})",
					this.GetType().Name,
					rg.Length,
					_config.dict.m_config.m_buckets == this ? "current" : "not current");
			}
#endif

			public IEnumerable<EntryBlock.SlotAddr> _Slots()
			{
				BucketHead._cfg = cfg;
				for (uint i = 0; i < rg.Length; i++)
				{
					if (rg[i] != 0)
					{
						BucketHead bh = new BucketHead(this, i);
						foreach (EntryBlock.SlotAddr sa in bh.Entries)
						{
							var xyz = sa;
							yield return xyz;
						}
					}
				}
			}
			[DebuggerBrowsable(DebuggerBrowsableState.Never)]
			public EntryBlock.SlotAddr[] Slots
			{
				get
				{
					return _Slots().ToArray();
				}
			}

			[DebuggerBrowsable(DebuggerBrowsableState.Never)]
			public BucketHead[] _buckets
			{
				get
				{
					return rg.Select((v, ix) => new BucketHead(this, (uint)ix)).ToArray();
				}
			}

			public bool ContainsFirstIndex(int gx)
			{
				for (int i = 0; i < rg.Length; i++)
				{
					Int64 v = AtomicRead64(ref rg[i]);
					if (BucketHead.CapturedFirstSlotIndexV(v) == gx)
						return true;
				}
				return false;
			}

			[DebuggerDisplay("{ToString(),nq}")]
			[DebuggerTypeProxy(typeof(LockFreeDictionary<,>.Config.BucketList.BucketListDebugView.BucketDisplay))]
			internal partial struct BucketHead
			{
				public static Config _cfg;
				public BucketList _bl { get { return bl; } }
				public uint _bx { get { return bx; } }
				public Int64 _v { get { return v; } }

				public bool IsInitialized { get { return bl != null; } }

				internal static int ExtractEntryCount(Int64 v) { return (int)(v >> 32) & EntriesPerBucketMask; }

				public override string ToString()
				{
					return new BucketList.BucketListDebugView.BucketDisplay(this).ToString();
				}

				public String LiveStatus
				{
					get
					{
						Int64 va = AtomicRead64(ref bl.rg[bx]);
						return String.Format("v: {0:X16}  cur: {1:X16}  {2}", v, va, va == v ? "ok" : "Ⓧ");
					}
				}

				public static String VDisplay(Int64 v)
				{
					return String.Format("seq: {0:X6} c: {1,-2} gx: {2:X8}", BucketHead._seq_no_V(v), BucketHead.ExtractEntryCount(v), BucketHead.CapturedFirstSlotIndexV(v));

				}

				public static int CapturedFirstSlotIndexV(Int64 v) { return (int)v; }
				public int _seq_no { get { return (int)(v >> 40); } }
				public static int _seq_no_V(Int64 v) { return (int)(v >> 40); }

#if false
			public override string ToString()
			{
				if (!IsInitialized)
					return "(not initialized)";
				if (bx != -1)
				{
					Int64 newv = bl.rg[bx];
					if (v != newv)
					{
						BucketHead bhnew = new BucketHead(bl, bx);
						return String.Format("{7} bx: {0:X8} @{1}({2}) first: {3:X8} ({4:X8}) count: {5} ({6})",
							bx,
							_seq_no, bhnew._seq_no,
							FirstSlotIndex, bhnew.FirstSlotIndex,
							_slot_count, bhnew._slot_count,
							LiveStatus);
					}
				}
				String chain = "";// String.Join(" ", _cfg.FollowChain(FirstSlotIndex).Take(Math.Min(SlotCount, 10)).Select(sa => "(" + sa.ToString() + ")"));
				return String.Format("{5} bx: {0:X8}@ {1} first: {2:X8} count: {3} {4}", bx, _seq_no, FirstSlotIndex, _slot_count, chain, LiveStatus);
			}
#endif

				//			[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
				[DebuggerBrowsable(DebuggerBrowsableState.Never)]
				public EntryBlock.SlotAddr[] Entries
				{
					get
					{
						return _cfg.FollowChain(CapturedFirstEntryIndex).Take(CapturedEntryCount).ToArray();
					}
				}
			};
		};




		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// DEBUGGING CODE FOR 'Config' FOLLOWS
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


		public bool GetExistingSlotAddr(int gx, out EntryBlock.SlotAddr sa)
		{
			if ((uint)gx >= (uint)Thread.VolatileRead(ref m_gx_next))
			{
				sa = default(EntryBlock.SlotAddr);
				return false;
			}
			sa = new EntryBlock.SlotAddr(GetBlock(gx >> EntriesPerBlockPower), gx & GlobalIndexMask);
			return true;
		}

		public override string ToString()
		{
			return String.Format("count: {0} next index: {1}", d.Count, Thread.VolatileRead(ref m_gx_next));
		}
		public IEnumerable<EntryBlock.SlotAddr> FollowChain(int i)
		{
			while (true)
			{
				EntryBlock.SlotAddr sa;
				if (!GetExistingSlotAddr(i, out sa))
					yield break;
				yield return sa;
				if (!sa.TryGetEntryNext(out i))
					yield break;
			}
		}

		public String Dump()
		{
			using (StringWriter sw = new StringWriter())
			{
				sw.WriteLine(new String('%', 114));
				sw.WriteLine("count: {0}", d.Count);
				sw.WriteLine("=== primary BucketHead entries ({0}) ===", m_buckets.BucketCount);
				uint c = m_buckets.BucketCount;
				for (uint i = 0; i < c; i++)
				{
					BucketList.BucketHead bh = new BucketList.BucketHead(m_buckets, i);
					if (bh.CapturedEntryCount > 0)
					{
						//if (i != prev + 1)
						//sw.WriteLine("...");
						sw.WriteLine("bucket: {0,4} v.{1,-3} : {2,2} entries",
							i,
							bh._seq_no,
							bh.CapturedEntryCount);
						foreach (EntryBlock.SlotAddr sa in FollowChain(bh.CapturedFirstEntryIndex).Take(bh.CapturedEntryCount))
						{
							EntryBlock.Entry ent = sa._entry;
							String sk = ent.key == null ? "null" : "\"" + ent.key.ToString() + "\"";
							String sv = ent.value == null ? "null" : "\"" + ent.value.ToString() + "\"";
							sw.WriteLine("          {0:X8} hash: {1:X8}/{2,-2} k: {3,-43} v: {4,-43} next: {5:X8}",
								sa.GlobalIndex,
								ent.hash,
								ent.hash % c,
								sk,
								sv,
								ent.gx_next);
						}
					};
				}
				sw.WriteLine();


				for (EntryBlock eb = m_first_block; eb != null; eb = eb.next)
				{
					sw.WriteLine("=== Entry block #{0} ===", eb.BlockIndex);

					for (int i = 0; i < EntriesPerBlock; i++)
					{
						EntryBlock.Entry ent;
						eb.GetEntryHot(i, out ent);

						if (ent.hash != 0)
						{
							String sk = ent.key == null ? "null" : "\"" + ent.key.ToString() + "\"";
							String sv = ent.value == null ? "null" : "\"" + ent.value.ToString() + "\"";
							int gx_ = (eb.BlockIndex << EntriesPerBlockPower) + i;
							sw.WriteLine("{0:X8}: {1:X8} hash: {2:X8}    k: {3,-43} v: {4,-43} next: {5:X8}   bx1: {6,-3}",
								i,
								gx_,
								ent.hash,
								sk,
								sv,
								ent.gx_next,
								ent.hash % m_buckets.BucketCount);
						}
					}

					sw.WriteLine();
				}

				sw.WriteLine("=== Free list {0} ===");

				return sw.ToString();
			}
		}

		//[DebuggerDisplay("{ToString(),nq}")]
		public partial struct Freelist
		{
			public override string ToString()
			{
				return String.Format("head: {0:X16} count: {2}", m_head, _ied().Count());
			}

			[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
			EntryBlock.SlotAddr[] _debug
			{
				get { return _ied().ToArray(); }
			}

			public IEnumerable<EntryBlock.SlotAddr> _ied()
			{
				EntryBlock.SlotAddr sa;
				Int64 cur = m_head;
				int gx = (int)cur;
				do
				{
					if (!cfg.GetExistingSlotAddr(gx, out sa))
						yield break;
					EntryBlock.Entry e = sa._entry;
					yield return sa;

					if (gx == e.gx_next)
						yield break;
					gx = e.gx_next;
				}
				while (gx != FreeListTerminator && gx != -1);
			}
		};
	};

}