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
{
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// 
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public interface ISysObj
	{
		String SysObjName { get; }

		String SysObjDescription { get; }

		IReadOnlyDictionary<String, ISysObj> SysObjChildren { get; }

		ISysObj SysObjParent { get; }
	};

	public class SysObjHelper<T> : KeyedCollection<String, T>, IReadOnlyDictionary<String, ISysObj> where T : class, ISysObj
	{
		public SysObjHelper()
			: base(StringComparer.OrdinalIgnoreCase)
		{
		}

		public static IEnumerable<ISysObj> GetNamePrefixMatches(ISysObj sys_obj, String s_pfx)
		{
			if (s_pfx == "*")
				s_pfx = "";
			foreach (var kvp in sys_obj.SysObjChildren)
			{
				Debug.Assert(kvp.Value.SysObjName == kvp.Key);
				if (kvp.Key.StartsWith(s_pfx))
					yield return kvp.Value;
			}
		}

		static Dictionary<String, T> _ed = null;
		static SysObjHelper<T> _empty = null;
		static public SysObjHelper<T> Empty
		{
			get { return _empty = _empty ?? new SysObjHelper<T>(); }
		}

		protected override String GetKeyForItem(T item)
		{
			return item.SysObjName;
		}

		public new IDictionary<String, T> Dictionary
		{
			get
			{
				if (base.Dictionary == null)
					return _ed = _ed ?? new Dictionary<String, T>();
				return base.Dictionary;
			}
		}

		bool IReadOnlyDictionary<String, ISysObj>.ContainsKey(String key)
		{
			if (base.Dictionary == null)
				return false;
			return base.Dictionary.ContainsKey(key);
		}

		ICollection<String> IReadOnlyDictionary<String, ISysObj>.Keys
		{
			get { return base.Dictionary.Keys; }
		}

		ICollection<ISysObj> IReadOnlyDictionary<String, ISysObj>.Values
		{
			get { return ((IEnumerable<T>)this).Cast<ISysObj>().ToArray(); }
		}

		bool IReadOnlyDictionary<String, ISysObj>.TryGetValue(String key, out ISysObj value)
		{
			T v;
			if (base.Dictionary == null || !base.Dictionary.TryGetValue(key, out v))
			{
				value = null;
				return false;
			}
			value = v;
			return true;
		}

		ISysObj IReadOnlyDictionary<String, ISysObj>.this[String key]
		{
			get
			{
				if (base.Dictionary == null)
					throw new KeyNotFoundException();
				return base.Dictionary[key];
			}
		}

		bool IReadOnlyDictionary<String, ISysObj>.IsReadOnly
		{
			get { return true; }
		}

		int IReadOnlyDictionary<String, ISysObj>.Count
		{
			get { return base.Count; }
		}

		IEnumerator<KeyValuePair<String, ISysObj>> IEnumerable<KeyValuePair<String, ISysObj>>.GetEnumerator()
		{
			if (base.Dictionary == null)
				yield break;
			foreach (var kvp in base.Dictionary)
				yield return new KeyValuePair<String, ISysObj>(kvp.Key, kvp.Value);
		}

		System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
		{
			return GetEnumerator();
		}
	}
}

namespace agree.Parse
{
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// 
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	abstract public partial class ChartBase<T> : ISysObj where T : ChartBase<T>.IMotherDaughter, IEquatable<T>
	{
		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; } }

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		internal ChartBase(ParserConfiguration config, ISysObj so_owner, String source_text, int c_columns)
		{
			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; }
		}
	};
}