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

using glue.Collections.XSpinLock;
using glue.Collections.ReadOnly;
using glue.Tokenization;

namespace agree.Parse
{
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// 
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	abstract public partial class ChartBase : ISysObj
	{
		static int i_next_id = 0;

		readonly protected ParserConfiguration config;
		readonly ISysObj so_owner;
		readonly int id;
		readonly int c_columns;
		readonly AtomicSequenceList<IParseChartEdge>[][] chart;
		readonly Span entire_span;
		readonly String source_text;
		AtomicSequencer ast = new AtomicSequencer();

		internal AtomicSequencer AtomicStamp { get { return ast; } }

		static public ParseChart active_chart = null; // temp
		public int manual_gcs = 0; // temp

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		internal ChartBase(ParserConfiguration config, ISysObj so_owner, String source_text, int c_columns)
		{
			active_chart = this as ParseChart;	// temp

			this.config = config;
			this.so_owner = so_owner;
			this.source_text = source_text;
			this.id = Interlocked.Increment(ref i_next_id);
			if (c_columns == 0)
			{
				Exception ex_inner = new ArgumentException("Value cannot be zero", "c_columns");
				throw new TypeInitializationException(this.GetType().FullName, ex_inner);
			}
			this.c_columns = c_columns;
			this.entire_span = new Span(0, (uint)c_columns);
			this.chart = new AtomicSequenceList<IParseChartEdge>[c_columns][];

			for (int i = 0; i < c_columns; i++)
				this.chart[i] = new AtomicSequenceList<IParseChartEdge>[c_columns - i];
		}

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

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

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public String SourceText { get { return source_text; } }

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// insertion of the interlocking list upon first use is lock-free
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		internal void AddEdge(IParseChartEdge e)
		{
			AtomicSequenceList<IParseChartEdge> lce = chart[e.ChartSpan.StartIndex][e.ChartSpan.Length - 1];
			if (lce == null)
			{
				lce = new AtomicSequenceList<IParseChartEdge>(ast);
				lce = Interlocked.CompareExchange(ref chart[e.ChartSpan.StartIndex][e.ChartSpan.Length - 1], lce, null) ?? lce;
			}
#if false && DEBUG
			T nc = e.Contents;
			if (lce.EnumerateSeq(int.MaxValue).Count(pe => pe.Contents.SpinCompare(nc)) > 2000)
			{
				String msg = String.Format("spin limit exceeded for '{0}' in span {1}", e.Contents, e.Span);
				throw new Exception(msg);
			}
#endif
			lce.Add(e);
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// Edges which end <b>at</b> the given zero-based column index
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		protected IEnumerable<IParseChartEdge> GetLeftAlignedEdges(int i_col, uint i_seq)
		{
			AtomicSequenceList<IParseChartEdge> lce;
			for (int i = 0, j = i_col; j >= 0; j--)
				if ((lce = chart[i++][j]) != null)
					foreach (IParseChartEdge ce in lce.EnumerateSeq(i_seq))
						yield return ce;
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// Edges which start <b>at</b> the given zero-based column index
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		protected IEnumerable<IParseChartEdge> GetRightAlignedEdges(int i_col, uint i_seq)
		{
			AtomicSequenceList<IParseChartEdge> lce;
			for (int span = 1; span <= c_columns - i_col; span++)
				if ((lce = chart[i_col][span - 1]) != null)
					foreach (IParseChartEdge ce in lce.EnumerateSeq(i_seq))
						yield return ce;
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		protected int GetEmptyColumnIndex()
		{
			for (int i = 0; i < c_columns; i++)
				if (!GetRightAlignedEdges(i, int.MaxValue).Any() && !GetLeftAlignedEdges(i, int.MaxValue).Any())
					return i;
			return -1;
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public IEnumerable<IParseChartEdge> AllEdges(uint i_seq = uint.MaxValue)
		{
			AtomicSequenceList<IParseChartEdge> lce;
			for (int i_col = 0; i_col < c_columns; i_col++)
				for (int span = 1; span <= c_columns - i_col; span++)
					if ((lce = chart[i_col][span - 1]) != null)
						foreach (IParseChartEdge ce in lce.EnumerateSeq(i_seq))
							yield return ce;
		}

		public IEnumerable<DerivedPassiveEdge> AllDerivedEdges(uint i_seq = uint.MaxValue)
		{
			return AllEdges(i_seq).OfType<DerivedPassiveEdge>();
		}

		internal IEnumerable<DerivedPassiveEdge> OrphanEdges(uint i_seq)
		{
			var ade = AllDerivedEdges(i_seq);
			return ade.Except(ade.SelectMany(dce => dce.Daughters.OfType<DerivedPassiveEdge>()));
		}

		public IEnumerable<DerivedPassiveEdge> DerivationTopEdges(uint i_seq = uint.MaxValue)
		{
			return OrphanEdges(i_seq).OfType<DerivedPassiveEdge>();
		}

		public IEnumerable<DerivedPassiveEdge> SpanningDerivedEdges()
		{
			return AllDerivedEdges(ast.InertValue).Where(dce => dce.ChartSpan == EntireSpan);
		}

		SysObjHelper<CompletedParse> completed_parses = null;

		internal SysObjHelper<CompletedParse> CompletedParsesKeyedCollection
		{
			get
			{
				SysObjHelper<CompletedParse> d = completed_parses;
				if (d == null)
				{
					AtomicSequenceList<IParseChartEdge> lce = chart[0][c_columns - 1];
					if (lce == null)
						d = SysObjHelper<CompletedParse>.Empty;
					else
					{
						d = new SysObjHelper<CompletedParse>();
						int i = 0;
						foreach (var x in lce.EnumerateSeq(uint.MaxValue).OfType<CompletedParse>())
						{
							x.ix = i++;
							d.Add(x);
						}
					}
					d = Interlocked.CompareExchange(ref completed_parses, d, null) ?? d;
				}
				return d;
			}
		}

		public ICollection<CompletedParse> CompletedParses
		{
			get { return CompletedParsesKeyedCollection; }
		}

		public IReadOnlyDictionary<String, ISysObj> SysObjChildren
		{
			get { return CompletedParsesKeyedCollection; }
		}

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

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

		public ISysObj SysObjParent
		{
			get { return so_owner; }
		}
	};
}