//#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 : TaskCancellationChart
	{
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// Active chart edge: binds a sequence interlock to a passive edge notification closure
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public abstract class ActiveEdge : AtomicSequenceObject, IDisposable
		{
			public ActiveEdge(UnificationParseChart c, IGrammarRule m, IParseChartEdge[] rgce)
				: base(c.AtomicStamp)
			{
				this.m_chart = c;
				this.mother = m;
				this.rgce = rgce;
				this.f_span_only = m.IsSpanOnly;
			}

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

			public abstract int TryMatchEdge(IParseChartEdge ce);

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

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

			public ActiveRightEdge(UnificationParseChart c, IGrammarRule m, IParseChartEdge[] rgce)
				: base(c, m, rgce)
			{
				IList<TfsEdge> rd = mother.RuleDaughters;
				this.i_arg = rgce.Length;
				this.i_chart_limit = m_chart.ColumnCount - (rd.Count - i_arg);
				this.daughter = rd[i_arg];
				this.f_last = i_arg == mother.RuleDaughters.Count - 1;
			}

			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <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 int TryMatchEdge(IParseChartEdge ce)
			{
				// if the candidate edge doesn't fit, don't bother unifying
				if (ce.ChartSpan.EndIndex > i_chart_limit)
					return 0;

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

				if (ce.License is Rule && !mother.CheckDaughterCompatibility(ce.License, i_arg))
					return 0;

				if (m_chart.QuickCheck(daughter, ce.Contents))
					return 0;

#if ASYNC_UNIFY
				m_chart.UnifyPartialAsync(mother, daughter, ce.Contents, f_last).ContinueWith(t =>
					{
						if (!t.Result.Equals(default(TfsEdge)))
							ProcessMatchedEdge(ce, t.Result);
					},
					m_chart.CancelTok,
					TaskContinuationOptions.AttachedToParent,
					TaskScheduler.Default);
#else
				int cu = 1;
				TfsEdge result;
				if (m_chart.UnifyPartial(mother, daughter, ce.Contents, out result, f_last))
				{
					IParseChartEdge[] rgce_new = rgce.Append(ce).ToArray();

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


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

			public ActiveLeftEdge(UnificationParseChart c, IGrammarRule m, IParseChartEdge[] rgce)
				: base(c, m, rgce)
			{
				IList<TfsEdge> rd = m.RuleDaughters;
				this.i_arg = rd.Count - 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 int TryMatchEdge(IParseChartEdge ce)
			{
				// if the candidate edge doesn't fit, don't bother unifying
				if (ce.ChartSpan.StartIndex < i_arg)
					return 0;

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

				if (ce.License is Rule && !mother.CheckDaughterCompatibility(ce.License, i_arg))
					return 0;

				if (m_chart.QuickCheck(daughter, ce.Contents))
					return 0;

#if ASYNC_UNIFY
				m_chart.UnifyPartialAsync(mother, daughter, ce.Contents, i_arg == 0).ContinueWith(t =>
					{
						if (!t.Result.Equals(default(TfsEdge)))
							ProcessMatchedEdge(ce, t.Result);
					},
					m_chart.CancelTok,
					TaskContinuationOptions.AttachedToParent,
					TaskScheduler.Default);
#else
				int cu = 1;
				TfsEdge result;
				if (m_chart.UnifyPartial(mother, daughter, ce.Contents, out result, i_arg == 0))
				{
					IParseChartEdge[] rgce_new = rgce.Prepend(ce).ToArray();
					if (i_arg > 0)
					{
						IGrammarRule gr_result = m_chart.IncorporatePart(mother, result);
						cu += m_chart.SubscribeLeftOf(ce.ChartSpan.StartIndex, new ActiveLeftEdge(m_chart, gr_result, rgce_new));
					}
					else
						cu += m_chart.FinishMatchedEdge(mother.License, result, new Span(ce.ChartSpan.StartIndex, rgce_new[rgce_new.Length - 1].ChartSpan.EndIndex), rgce_new);
				}
				return cu;
#endif
			}
		};

	};
}