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

using miew.ReadOnly;
using miew.Tokenization;
using miew.Concurrency;
using miew.Enumerable;

namespace agree
{
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// 
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	abstract public unsafe partial class ChartBase : ISysObj, IEnumerable<IParseObj>
	{
		static int next_chart_id = 0;

		readonly public ParseControl ctrl;
		readonly int chart_id;
		readonly protected int c_columns;
		protected ChartCell[][] chart;
		Span entire_span;

		//public int g_dseq = 1;

		/// a global interlocking sequence between the edge subscription mechanism (used by active edges) and the chart cell
		/// contents (passive edges). Valid sequence numbers are even numbers; a non-zero LSB indicates that (id-1)
		/// has been claimed and is in the process of being inserted into the respective list, which should be a short
		/// enough duration to spin-wait on.
		int next_edge_id = 2;

		public void StampSequence(IAtomicSequence obj)
		{
			while (true)
			{
				int ci = next_edge_id;
				if ((ci & 1) == 0 && Interlocked.CompareExchange(ref next_edge_id, ci + 1, ci) == ci)
				{
					obj.SetSequenceId(ci);
					/// can rely on the _next_ CMPXCHG of the global sequence counter to ensure that the write we just
					/// did to the object is flushed prior to the possibility of that next counter value being issued and
					/// published. Hence a full fence right here should not be required.
					next_edge_id = ci + 2;
					obj.SetStateNew();
					return;
				}
			}
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		internal ChartBase(ParseControl ctrl)
		{
			this.c_columns = ctrl.la.ChartSize;
			if (c_columns == 0)
			{
				Exception ex_inner = new ArgumentException("Parse chart cannot have zero columns", "c_columns");
				throw new TypeInitializationException(this.GetType().FullName, ex_inner);
			}

			this.ctrl = ctrl;
			this.chart_id = Interlocked.Increment(ref next_chart_id);
			this.entire_span = new Span(0, (uint)c_columns);

			this.chart = new ChartCell[c_columns][];
			for (int i = 0; i < c_columns; i++)
			{
				ChartCell[] cc = chart[i] = new ChartCell[c_columns - i];
				for (int j = 0; j < c_columns - i; j++)
					cc[j] = new ChartCell(this);
			}
		}

		public void ReleaseChart()
		{
			this.chart = null;
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public ChartCell this[Span span]
		{
			get { return chart[span.StartIndex][span.Length - 1]; }
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		//public IEnumerable<IParseObj> GetCellEdges(int i_col, int c_span)
		//{
		//    return chart[i_col][c_span - 1];
		//}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public int ColumnCount { get { return c_columns; } }

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public Span EntireSpan { get { return entire_span; } }

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public String InputText { get { return ctrl.InputText; } }

		internal ConcurrentList<PassiveEdge.Completed> _completed_edges = new ConcurrentList<PassiveEdge.Completed>();
		public IEnumerable<PassiveEdge.Completed> CompletedEdges
		{
			get
			{
				if (ctrl.parser_config.packing == Config.Parser.PackingOpts.None)
					return _completed_edges;
				return _completed_edges.Where(p =>
					{
						int q = p.State;
						if ((q & SequenceControl.stb_Hold) > 0)
							throw new Exception();
						if (q == SequenceControl.ACTIVE)
							return true;
						return false;
					});
			}
		}

		IReadOnlyDictionary<String, ISysObj> _completed_parses_dict = null;
		public IReadOnlyDictionary<String, ISysObj> SysObjChildren
		{
			get
			{
				if (_completed_parses_dict == null)
					_completed_parses_dict = _completed_edges.ToReadOnlyDictionary(p => p.SysObjName, p => (ISysObj)p);
				return _completed_parses_dict;
			}
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// enumerator for all edges in the chart. this is also the type of enumerator for the chart itself.
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

		public IEnumerator<IParseObj> GetEnumerator() { return new ChartCell._enum_all_edges(this); }

		IEnumerator IEnumerable.GetEnumerator() { return new ChartCell._enum_all_edges(this); }

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// each cell of the chart is a concurrent list of objects which implement the IParseObj interface
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public sealed class ChartCell : IEnumerable<IParseObj>
		{
			IParseObj m_first = null;
			IParseObj m_last = null;
			ChartBase m_cb;

			public ChartCell(ChartBase cb)
			{
				m_cb = cb;
			}

			public void Add(IParseObj po)
			{
				Debug.Assert(po.State == default(byte));
				po.SetStateRaw(SequenceControl.PENDING);

				if (m_first != null || Interlocked.CompareExchange(ref m_first, po, null) != null)
				{
					IParseObj ml = m_last ?? m_first;
					while (ml.Next != null || !ml.TrySetTail(po))
						ml = ml.Next;
					m_last = ml;
				}
				m_cb.StampSequence(po);
			}

			public struct _enum : IEnumerator<IParseObj>
			{
				ChartCell cc;
				IParseObj po;

				public _enum(ChartCell cc)
				{
					this.cc = cc;
					this.po = null;
				}

				public bool MoveNext()
				{
					return (po = po == null ? cc.m_first : po.Next) != null;
				}

				public void Reset() { this.po = null; }
				public IParseObj Current
				{
					get { return po; }
					set { po = value; }
				}
				object IEnumerator.Current { get { return po; } }
				public void Dispose() { }
			};

			public struct _enum_all_edges : IEnumerator<IParseObj>
			{
				ChartBase cb;
				IParseObj po;
				int i_col, i_span;

				public _enum_all_edges(ChartBase cb)
				{
					Debug.Assert(cb.ctrl.f_parse_phase_complete);
					this.cb = cb;
					this.po = null;
					this.i_col = this.i_span = 0;
				}

				public void Reset()
				{
					i_col = i_span = 0;
					po = null;
				}

				public bool MoveNext()
				{
					while (true)
					{
						while ((po = po == null ? cb.chart[i_col][i_span].m_first : po.Next) != null)
							if (po.IsActive())
								return true;

						if (++i_span >= cb.c_columns - i_col)
						{
							if (++i_col >= cb.c_columns)
								return false;
							i_span = 0;
						}
					}
				}

				public IParseObj Current { get { return po; } }
				object IEnumerator.Current { get { return Current; } }
				public void Dispose() { }
			};


			public struct _multi_cell_enum : IEnumerator<IParseObj>
			{
				int i, i_incr, j, j_incr;
				ChartCell cc;
				IParseObj po;
				ChartCell[][] rgcc;

				public _multi_cell_enum(ChartBase chart, int i_min, int i_incr, int j_min, int j_incr)
				{
					this.i = i_min;
					this.i_incr = i_incr;
					this.j = j_min;
					this.j_incr = j_incr;
					this.rgcc = chart.chart;
					this.cc = rgcc[i][j];
					this.po = null;
				}

				public bool MoveNext()
				{
					while ((po = po == null ? cc.m_first : po.Next) == null)
					{
						if ((j += j_incr) < 0 || (i += i_incr) < 0 || i >= rgcc.Length)
						{
							this.rgcc = null;
							return false;
						}
						ChartCell[] arr = rgcc[i];
						if (j >= arr.Length)
						{
							this.rgcc = null;
							return false;
						}
						cc = arr[j];
						po = null;
					}
					return true;
				}

				public IParseObj Current { get { return po; } }
				object IEnumerator.Current { get { return po; } }
				public void Reset() { throw new NotImplementedException(); }
				public void Dispose() { }
			};

			public IEnumerator<IParseObj> GetEnumerator() { return new _enum(this); }
			IEnumerator IEnumerable.GetEnumerator() { return new _enum(this); }
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

		public String SysObjName
		{
			get { return String.Format("pc{0}", chart_id); }
		}

		public string SysObjDescription
		{
			get { return InputText ?? SysObjName; }
		}

		public ISysObj SysObjParent
		{
			get { return ctrl.g; }
		}
	};
}