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

using miew.Tokenization;

namespace agree
{
	using unif_stats = ParseControl.Stats._unification;

	abstract public partial class SubscriptionChart : ChartBase
	{
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// Active chart edge: binds a sequence interlock to a passive edge notification closure
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public abstract class ActiveEdge : IAtomicSequence
		{
			public ActiveEdge(ParseChart c, GrammarRule r, MotherDaughterTfs m, ref PassiveEdge.ParseObjs rgo)
			{
				this.chart = c;
				this.ctrl = c.ctrl;
				this.r = r;
				this.rgo = rgo;
				this.f_restrict = c.ctrl.AnyPacking;
				c.ctrl.stats.Parsing.Chart.ActiveEdge();
			}

			internal ActiveEdge m_next;	/// linked list managed by 'NotificationSource'

			readonly protected ParseChart chart;
			readonly protected ParseControl ctrl;
			readonly protected GrammarRule r;
			protected PassiveEdge.ParseObjs rgo;
			internal TfsSection daughter;
			protected int i_arg;
			protected bool f_restrict;

			public abstract void TryMatchEdge(IParseObj ce, ref unif_stats u_stats);

			public Span CompletedSpan { get { return rgo.Span; } }

			protected int i_seq;
			public void SetSequenceId(int id) { i_seq = id; }
			public int SequenceId { get { return i_seq; } }

			protected int s_state;
			public int State { get { return s_state; } }
			public void SetStateRaw(int s) { s_state = s; }
			public bool BeginTransact() { throw new NotImplementedException(); }
		};

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

			public ActiveRightEdge(ParseChart c, GrammarRule r, MotherDaughterTfs m, ref PassiveEdge.ParseObjs rgo)
				: base(c, r, m, ref rgo)
			{
				this.i_arg = rgo.Count;
				TfsSection[] rd = m.RuleDaughters;
				this.i_chart_limit = chart.ColumnCount - (rd.Length - i_arg);
				this.daughter = rd[i_arg];
				this.f_last = i_arg == rd.Length - 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 void TryMatchEdge(IParseObj ce, ref unif_stats u_stats)
			{
				if (this.IsRemove())
					return;

				if (rgo.AnyInactive)
				{
					/// by setting this, we request to be opportunistically removed from the chart-edge subscription we 
					/// have been subscribed as interested in, during the next subscription posting pass
					s_state = SequenceControl.REMOVE;
					return;
				}

				// if the candidate edge doesn't fit, don't bother unifying
				if (ce.TokenSpan.EndIndex > i_chart_limit)
					return;

				if (f_last && r.IsSpanOnly && ce.TokenSpan.EndIndex < chart.ColumnCount - 1)
					return;

				Rule d_rule = ce.License as Rule;
				if (d_rule != null && !r.CheckDaughterCompatibility(d_rule, i_arg))
					return;

				//var pe = ce as PassiveEdge.Derived;
				//if (pe != null && pe.IsHold())
				//    pe.DirectCancel();

				if (!ce.IsActive())
					return;

				if (ctrl.QuickCheckParse(ce.Tfs, daughter))
				    return;

				MotherDaughterTfs mdtfs = ctrl.UnifySection(
											daughter,
											ce.Tfs,
											f_last, 
											f_restrict,
											ref u_stats);

				if (mdtfs != null)
				{
					PassiveEdge.ParseObjs npo = new PassiveEdge.ParseObjs(rgo, ce);
					if (f_last)
						chart.FinishMatchedEdge(r, mdtfs, ref npo, ref u_stats);
					else
						chart.Subscribe(RIGHT, new ActiveRightEdge(chart, r, mdtfs, ref npo), ref u_stats);
				}
			}
		};


		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// Active left edge: a partial right edge.
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public class ActiveLeftEdge : ActiveEdge
		{
			public ActiveLeftEdge(ParseChart c, GrammarRule r, MotherDaughterTfs m, ref PassiveEdge.ParseObjs rgo)
				: base(c, r, m, ref rgo)
			{
				this.i_arg = (byte)(m.RuleDaughters.Length - 1 - rgo.Count);
				this.daughter = m.RuleDaughters[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(IParseObj ce, ref unif_stats u_stats)
			{
				if (this.IsRemove())
					return;

				if (rgo.AnyInactive)
				{
					/// by setting this, we request to be opportunistically removed from the chart-edge subscription we 
					/// have been subscribed as interested in, during the next subscription posting pass
					s_state = SequenceControl.REMOVE;
					return;
				}

				// if the candidate edge doesn't fit, don't bother unifying
				if (ce.TokenSpan.StartIndex < i_arg)
					return;

				if (i_arg == 0 && r.IsSpanOnly && ce.TokenSpan.StartIndex > 0)
					return;

				Rule d_rule = ce.License as Rule;
				if (d_rule != null && !r.CheckDaughterCompatibility(d_rule, i_arg))
					return;

				//var pe = ce as PassiveEdge.Derived;
				//if (pe != null && pe.IsHold())
				//    pe.DirectCancel();

				if (!ce.IsActive())
					return;

				if (ctrl.QuickCheckParse(ce.Tfs, daughter))
				    return;

				MotherDaughterTfs mdtfs = ctrl.UnifySection(
											daughter,
											ce.Tfs,
											i_arg == 0,
											f_restrict,
											ref u_stats);

				if (mdtfs != null)
				{
					PassiveEdge.ParseObjs npo = new PassiveEdge.ParseObjs(ce, rgo);
					if (i_arg == 0)
						chart.FinishMatchedEdge(r, mdtfs, ref npo, ref u_stats);
					else
						chart.Subscribe(LEFT, new ActiveLeftEdge(chart, r, mdtfs, ref npo), ref u_stats);
				}
			}
		};
	};
}