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>
{

	//	[DebuggerDisplay("{ToString(),nq}")]
	public partial
	class EntryBlock
	{
		[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 void StoreEntry(ref Entry e)
			{
				eb.StoreEntryLoose(offs, ref e);
			}

			public void SetEntryNext(int gx)
			{
				eb.SetEntryNext(offs, gx);
			}

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

			public Entry _entry
			{
				get
				{
					Entry e;
					eb.GetEntryLoose(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);
		}

	};

	//[DebuggerDisplay("{ToString(),nq}")]
	public
	partial class Config
	{
		public int m_c_free = 0;

		//		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; } }
		}


		[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.CapturedSlotCountV((long)v);

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

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

						return String.Format("{0} bx: {1:X8} {2} gx: {3:X8}  seq: {4:X8}  ({5:X16})",
							sc2,
							bx,
							sc,
							BucketHead.CapturedSlotCountV((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.CapturedSlotCountV((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)bl.cfg.m_gx_next)
								yield break;
							EntryBlock.Entry e;
							bl.cfg.GetEntryLoose(gx, out e);
							if (gx == e.gx_next)
								yield break;
							gx = e.gx_next;
						}
					}
					int ValidCount()
					{
						if (bl == null)
							return 0;
						int count = BucketHead.CapturedSlotCountV((long)v);
						int actual = 0;

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

							EntryBlock.Entry e;
							bl.cfg.GetEntryLoose(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)cfg.m_gx_next)
								return String.Format("Ⓧ {0:X8} was never issued as a valid global index", gx);

							EntryBlock.Entry e;
							cfg.GetEntryLoose(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 bool Check()
			{
				for (uint i = 0; i < BucketCount; i++)
				{
					var bh = new BucketHead(this, i);
					int gx = bh.CapturedFirstSlotIndex;
					for (int j = 0; j < bh.CapturedSlotCount; j++)
					{
						if ((uint)gx >= (uint)cfg.m_gx_next)
						{
							if (Debugger.IsAttached)
								Debugger.Break();
							return false;
						}
						//EntryBlock.Entry e = cfg.GetSlotAddr(gx)._entry;
						int gx_next = cfg.GetEntryNext(gx);
						if (gx == gx_next)
						{
							if (Debugger.IsAttached)
								Debugger.Break();
							return false;
						}
						gx = gx_next;
					}
				}
				return true;
			}

			public String DebugInfo()
			{
				return String.Format("{0} ({1}) buckets: {2} entries: {3}",
					this.GetType().Name,
					cfg.dict.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; } }

				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.CapturedSlotCountV(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(CapturedFirstSlotIndex).Take(CapturedSlotCount).ToArray();
					}
				}
			};
		};




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


		public bool GetExistingSlotAddr(int gx, out EntryBlock.SlotAddr sa)
		{
			if ((uint)gx >= (uint)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} free: {1} next: {2}", dict.Count, m_c_free, 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}", dict.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.CapturedSlotCount > 0)
					{
						//if (i != prev + 1)
						//sw.WriteLine("...");
						sw.WriteLine("bucket: {0,4} v.{1,-3} : {2,2} entries",
							i,
							bh._seq_no,
							bh.CapturedSlotCount);
						foreach (EntryBlock.SlotAddr sa in FollowChain(bh.CapturedFirstSlotIndex).Take(bh.CapturedSlotCount))
						{
							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.GetEntryLoose(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);
			}
		};
	};

}