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

using glue.Extensions.String;
using glue.Extensions.Enumerable;
using glue.Tasks;
using glue.Tokenization;
using glue.Debugging;

namespace agree.Parse
{
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	///
	/// 
	///
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public abstract class UnificationParseChart : SubscriptionChart
	{
		public UnificationParseChart(
				ParserConfiguration config,
				ISysObj so_owner,
				String source_text,
				IEnumerable<ISysObj> start_symbols,
				int chart_span,
				IEnumerable<IParseChartEdge> initial_tokens)
			: base(config, so_owner, source_text, chart_span)
		{
			this.initial_tokens = initial_tokens;
			this.start_symbols = start_symbols;
		}

		readonly IEnumerable<IParseChartEdge> initial_tokens;
		readonly internal IEnumerable<ISysObj> start_symbols;

		public abstract bool CanUnify(ISysObj t1, TfsEdge t2);
		public abstract bool UnifyPartial(IParseChartRule mother, TfsEdge daughter, TfsEdge candidate, out TfsEdge result, bool f_last);
		public abstract Task<TfsEdge> UnifyPartialAsync(IParseChartRule mother, TfsEdge daughter, TfsEdge candidate, bool f_last);
		public abstract IGrammarRule IncorporatePart(IGrammarRule mother, TfsEdge result);
		public virtual bool QuickCheck(TfsEdge t1, TfsEdge t2) { return false; }

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public int FinishMatchedEdge(ISysObj license, TfsEdge 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");
			//}
			int cu = 0;

			DerivedPassiveEdge nce = null;

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

			nce = nce ?? new DerivedPassiveEdge(this, span, license, result, rgce_new);
			StartAttachedChildTask(AddEdgeInner, nce);
			return cu;
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// Guarantee a parent task context so that nested tasks will be attached as child tasks.
		/// Do not specify AttachedToParent (or rethrown exception will propagate into the parent task, which is not
		/// accessible to this function) and do not specify the chart's CancelTok (or this task's act of cancelling the
		/// parse will put a task-canceled exception into the continuation, and trump/erase the actual exception)
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

		public Task ParseAsync()
		{
			var hs = initial_tokens.UnionMany(tok => tok.ChartSpan.Range);
			if (hs.Count != ColumnCount)
			{
				String m2 = Enumerable.Range(0, ColumnCount).Except(hs).StringJoin(", ");
				throw new ParseException("No tokens were submitted to the parser for the following chart column positions: {0}.", m2);
			}

			timer.Start();
			return StartAttachedChildTask(ParseInner).ContinueWith(
							t =>
							{
								SetComplete();
								ClearSubscribers();

								if (t.Exception != null)
								{
									CancelParse();
									m_status |= TaskCancellationChart.Status.Faulted;
									AggregateException aex = t.Exception.Flatten();
									var rgex = aex.InnerExceptions.OfType<ParseException>().Distinct(e => e.Id).ToArray();
									if (rgex.Length == 1)
										throw rgex[0];
									throw aex;
								}
							},
							CancellationToken.None,
							TaskContinuationOptions.ExecuteSynchronously,
							TaskScheduler.Default);
		}

		void ParseInner()
		{
			foreach (IParseChartEdge tok in initial_tokens)
				StartAttachedChildTask(AddEdgeInner, tok);
		}
	};
}