//#define ASYNC_UNIFY

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

using glue.Collections.XSpinLock;
using glue.Tasks;
using glue.Extensions.Enumerable;
using glue.Debugging;
using glue.Tokenization;

namespace agree.Parse
{
	abstract public partial class SubscriptionChart<T> : TaskCancellationChart<T> where T : ChartBase<T>.IMotherDaughter, IEquatable<T>
	{
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// Active chart edge: binds a sequence interlock to a passive edge notification closure
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public abstract class ActiveEdge : AtomicSequenceObject, IDisposable
		{
			public ActiveEdge(UnificationParseChart<T> c, IGrammarRule m, IParseChartEdge[] rgce, bool f_span_only)
				: base(c.AtomicStamp)
			{
				this.m_chart = c;
				this.mother = m;
				this.rgce = rgce;
				this.f_span_only = f_span_only;
			}

			readonly protected UnificationParseChart<T> m_chart;
			protected IGrammarRule mother;
			readonly protected IParseChartEdge[] rgce;
			readonly protected bool f_span_only;

			public abstract void TryMatchEdge(IParseChartEdge ce);

			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// 
			/// </summary>
			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			public void Dispose()
			{
				IGrammarRule m = Interlocked.CompareExchange(ref mother, null, mother);
				if (m != null)
					m.Dispose();
			}

			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// 
			/// </summary>
			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			protected void FinishMatchedEdge(T result, Span span, IParseChartEdge[] rgce_new)
			{
				//if (new System.Text.RegularExpressions.Regex("FinishMatchedEdge").Matches(new StackTrace().ToString()).Count > 20)
				//{
				//    m_chart.DumpChart(@"d:\chart_dump.html");
				//    Environment.Exit(0);
				//    throw new ParseException("cyclic");
				//}

				DerivedPassiveEdge nce = null;

				// try to match the start symbol
				if (span == m_chart.EntireSpan)
				{
					foreach (ISelf start_sym in m_chart.start_symbols)
					{
						if (m_chart.CanUnify(start_sym.Self, result))
						{
							nce = new CompletedParse(this.m_chart, span, result, rgce_new, start_sym);
							if (m_chart.debug_output != null)
								m_chart.debug_output.WriteLineColor("$darkgreen {0}$ matched start symbol $magenta {1}", nce, start_sym);
							break;
						}
					}
				}

				nce = nce ?? new DerivedPassiveEdge(m_chart, span, result, rgce_new);
				m_chart.StartAttachedChildTask(m_chart.AddEdgeInner, nce);/*.ContinueWith(t =>
					{
						if (t.Exception != null)
							Debugger.Break();
					});*/
			}
		};

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// Active right edge: a partial left edge.
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public class ActiveRightEdge : ActiveEdge
		{
			readonly T daughter;
			readonly int i_arg, i_chart_limit;

			public ActiveRightEdge(UnificationParseChart<T> c, IGrammarRule m, IParseChartEdge[] rgce, bool f_span_only)
				: base(c, m, rgce, f_span_only)
			{
				IList<T> rd = mother.RuleDaughters;
				this.i_arg = rgce == null ? 0 : rgce.Length;
				this.i_chart_limit = m_chart.ColumnCount - (rd.Count - i_arg);
				this.daughter = rd[i_arg];
			}

			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// The active edge tries to match the given PassiveChart edge. Three cases are possible:
			/// 1.) the specified edge does not extend this active edge; or
			/// 2.) the rule is complete, create a new passive edge and insert it into the chart; or
			/// 3.) the rule is still not complete: create a new active edge which incorporates the new partial result.
			/// In any of these three cases, this rule continues to accept future edges as before.
			/// This function preserves the thread-safety of the user-supplied Unification callback, if any.
			/// </summary>
			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			public override void TryMatchEdge(IParseChartEdge ce)
			{
				// if the candidate edge doesn't fit, don't bother unifying
				if (ce.ChartSpan.EndIndex > i_chart_limit)
					return;

				if (m_chart.QuickCheck(daughter, ce.Self) == UnificationParseChart<T>.QuickCheckResult.Failed)
					return;

				// allows client to (e.g.) delete rule daughters if the unification succeeds
				bool f_last = i_arg == mother.RuleDaughters.Count - 1;

				if (f_last && f_span_only && ce.ChartSpan.EndIndex < m_chart.ColumnCount - 1)
					return;

#if ASYNC_UNIFY
				m_chart.UnifyPartialAsync(mother, daughter, ce.Contents, f_last).ContinueWith(t =>
					{
						if (!t.Result.Equals(default(T)))
							ProcessMatchedEdge(ce, t.Result);
					},
					m_chart.CancelTok,
					TaskContinuationOptions.AttachedToParent,
					TaskScheduler.Default);
#else
				T result;
				if (m_chart.UnifyPartial(mother, daughter, ce.Self, out result, f_last))
					ProcessMatchedEdge(ce, result);
#endif
			}

			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// 
			/// </summary>
			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			void ProcessMatchedEdge(IParseChartEdge ce, T result)
			{
				IParseChartEdge[] rgce_new = rgce.Append(ce).ToArray();

				if (i_arg < mother.RuleDaughters.Count - 1)
				{
					IGrammarRule gr_result = m_chart.IncorporatePart(mother, result);
					m_chart.SubscribeRightOf(ce.ChartSpan.EndIndex, new ActiveRightEdge(m_chart, gr_result, rgce_new, f_span_only));
				}
				else
					FinishMatchedEdge(result, new Span(rgce_new[0].ChartSpan.StartIndex, ce.ChartSpan.EndIndex), rgce_new);
			}
		};

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// Active left edge: a partial right edge.
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public class ActiveLeftEdge : ActiveEdge
		{
			readonly T daughter;
			readonly int i_arg;

			public ActiveLeftEdge(UnificationParseChart<T> c, IGrammarRule m, IParseChartEdge[] rgce, bool f_span_only)
				: base(c, m, rgce, f_span_only)
			{
				IList<T> rd = m.RuleDaughters;
				this.i_arg = rd.Count - (rgce == null ? 0 : rgce.Length) - 1;
				this.daughter = rd[i_arg];
			}

			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// The active edge tries to match the given PassiveChart edge. Three cases are possible:
			/// 1.) the specified edge does not extend this active edge; or
			/// 2.) the rule is complete, create a new passive edge and insert it into the chart; or
			/// 3.) the rule is still not complete: create a new active edge which incorporates the new partial result.
			/// In any of these three cases, this rule continues to accept future edges as before.
			/// This function preserves the thread-safety of the user-supplied Unification callback, if any.
			/// </summary>
			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			public override void TryMatchEdge(IParseChartEdge ce)
			{
				// if the candidate edge doesn't fit, don't bother unifying
				if (ce.ChartSpan.StartIndex < i_arg)
					return;

				if (m_chart.QuickCheck(daughter, ce.Self) == UnificationParseChart<T>.QuickCheckResult.Failed)
					return;

				bool f_last = i_arg == 0;

				if (f_last && f_span_only && ce.ChartSpan.StartIndex > 0)
					return;

#if ASYNC_UNIFY
				m_chart.UnifyPartialAsync(mother, daughter, ce.Contents, f_last).ContinueWith(t =>
					{
						if (!t.Result.Equals(default(T)))
							ProcessMatchedEdge(ce, t.Result);
					},
					m_chart.CancelTok,
					TaskContinuationOptions.AttachedToParent,
					TaskScheduler.Default);
#else
				T result;
				if (m_chart.UnifyPartial(mother, daughter, ce.Self, out result, f_last))
					ProcessMatchedEdge(ce, result);
#endif
			}

			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// 
			/// </summary>
			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			void ProcessMatchedEdge(IParseChartEdge ce, T result)
			{
				IParseChartEdge[] rgce_new = rgce.Prepend(ce).ToArray();
				if (i_arg > 0)
				{
					IGrammarRule gr_result = m_chart.IncorporatePart(mother, result);
					m_chart.SubscribeLeftOf(ce.ChartSpan.StartIndex, new ActiveLeftEdge(m_chart, gr_result, rgce_new, f_span_only));
				}
				else
					FinishMatchedEdge(result, new Span(ce.ChartSpan.StartIndex, rgce_new[rgce_new.Length - 1].ChartSpan.EndIndex), rgce_new);
			}
		};

	};
}