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

using glue.Collections.XSpinLock;
using glue.Extensions.String;
using glue.Debugging;
using glue.Tokenization;
using agree.Parse;
using glue.Collections.ReadOnly;

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// <summary>
/// 
/// </summary>
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
partial class Program
{
	static Program()
	{
		foreach (var qq in tests)
		{
			qq.warr = qq.input.ToLower().Split(' ');
		}
	}
	public static void ConsoleMsg(ConsoleColor color, String fmt, params String[] args)
	{
		lock (Console.Out)
		{
			Console.ForegroundColor = color;
			Console.WriteLine(fmt, args);
			Console.ResetColor();
		}
	}

	public static bool f_unif_delay;
	bool f_sonly;

	ILookup<String,TestLexicalEntry> lex_index;

	class SO : agree.ISysObj
	{
		public string SysObjName
		{
			get { return "TestSysObj"; }
		}

		public IReadOnlyDictionary<string, agree.ISysObj> SysObjChildren
		{
			get { return ReadOnlyDictionary<String, agree.ISysObj>.Empty; }
		}

		public agree.ISysObj SysObjParent
		{
			get { return this; }
		}

		public string SysObjDescription
		{
			get { return "TestSysObj description"; }
		}

	};
	SO so = new SO();

	async Task<TestChart> Parse(bool print_enable, TokenizedString ts, TestGrammarRule[] rules)
	{
		//var zzz = ts.Select(tok => 
		//    {
		//        TestEdge te = lex_index[tok.Text].Self;
				//new TestEdge { s = //{   lex_index[tok.Text]);
		TestChart chart = new TestChart(so, ts, rules, null);
		chart.SetDebugOutput(Console.Out);

		await chart.ParseAsync();
		//await chart.ParseB();
		return chart;
	}

#if false
	Task<TestChart> ParseDetach(bool print_enable, String[] warr, TestGrammarRule[] rules)
	{
		return TaskEx.Run<Task<TestChart>>(() => Parse(print_enable, warr, rules)).Unwrap();
	}
#endif

	bool f_print, f_loop, f_show_chart, f_show_hash, f_show_iter, f_detach, f_each, f_nohash;
	int iter, i_testitem;
	int printing;

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// 
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	static List<Task> lt;

	void ProgramMain(String[] args)
	{
		TaskScheduler.UnobservedTaskException += new EventHandler<UnobservedTaskExceptionEventArgs>(TaskScheduler_UnobservedTaskException);
		f_print = args.Contains("-output");

		int c_tasks_desired = 1;
		String arg = args.FirstOrDefault(a => a.StartsWith("-tasks:"));
		if (arg != null)
			c_tasks_desired = Math.Max(int.Parse(arg.Substring(7)), 1);

		i_testitem = -1;
		arg = args.FirstOrDefault(a => a.StartsWith("-testitem:"));
		if (arg != null)
			i_testitem = int.Parse(arg.Substring(10));
		if (i_testitem < 0 || i_testitem >= tests.Length)
			i_testitem = -1;

		f_loop = c_tasks_desired > 1 || args.Contains("-loop");

		f_show_chart = args.Contains("-showchart");

		f_show_hash = args.Contains("-showhash");

		f_show_iter = args.Contains("-showiter");

		f_sonly = args.Contains("-sonly");

		f_unif_delay = args.Contains("-unifdelay");

		f_detach = args.Contains("-detach");

		f_nohash = args.Contains("-nohash");

		f_each = args.Contains("-each");
		if (f_each)
			c_tasks_desired = tests.Length;

		iter = 0;

		printing = 0;

		lex_index = lex.SelectMany(x => x.Value.Select(pos =>
			{
				return new TestLexicalEntry(x.Key, new TestEdge(null, pos));
			}))
			.ToLookup(le => le.Lemmata[0]);

		/*List<Task>*/ lt = new List<Task>();
		for (int i=0; i < c_tasks_desired; i++)
		{
			int test_no = f_each ? i : i_testitem;
			Console.WriteLine("{0}: test #{1}", i, test_no);
			lt.Add(DoOneParserAsyncDetach(i, test_no));
		}
		Thread.Sleep(100);

		if (!f_loop)
		{
			try
			{
				TaskEx.WhenAll(lt).Wait();
			}
			catch (Exception ex)
			{
				Console.WriteLine(ex);
			}
		}

		if (f_loop || Debugger.IsAttached)
		{
			Console.ReadKey();
			Console.ResetColor();
		}
		Environment.Exit(0);
	}

	void TaskScheduler_UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e)
	{
		Debugger.Break();
	}

	Task<TestChart> DoOneParserAsyncDetach(int i, int test_no)
	{
		return Task.Factory.StartNew<Task<TestChart>>(async () =>
			{
				bool print = f_print && (Interlocked.CompareExchange(ref printing, 1, 0) == 0);
				TestChart q = null;
				do
				{
					q = await DoOneParserAsync(print, test_no);
					if (f_print)
						Console.Out.WriteLineColor("$magenta {0}: test #{1} done", i, test_no);
				}
				while (f_loop);
				return q;
			}, CancellationToken.None, TaskCreationOptions.AttachedToParent, TaskScheduler.Default).Unwrap();
	}

	async Task<TestChart> DoOneParserAsync(bool print, int test_no)
	{
		if (test_no == -1)
			test_no = rnd.Next(tests.Length);
		TestItem tst = tests[test_no];

		int iter_local = Interlocked.Increment(ref iter);

		if (print)
			Console.Out.WriteLineColor(CancellationToken.None, "$magenta Test #{0}, using TestItem {1}", iter_local, test_no);

		TestChart c = await Parse(print, new TokenizedString(tst.warr," "), tst.rules);

		if (f_show_iter && (iter % 100 == 0))
			Console.WriteLine(iter);

		int oh=0;
		if (!f_nohash)
		{
			oh = OverallHash(c);
			if (tst.hash != 0 && oh != tst.hash)
				throw new Exception(String.Format("error: hash {0:X8} expected {1:X8}", oh, tst.hash));
		}

		// part that we don't need to wait on
		if (print) await TaskEx.Run(() =>
		{
			if (f_show_chart) lock (Console.Out)
				{
					PrintChart(tst.warr, c);
				}

			if (f_show_hash && !f_nohash)
				Console.WriteLine(Environment.NewLine + "overall hash: {0:X8}", oh);
		});

		return c;
	}

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// 
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	int GetTreeHashCode(TestChart.DerivedPassiveEdge e)
	{
		int h = 0;
		Action<TestChart.DerivedPassiveEdge> f = null;
		f = (ce) =>
			{
				TestChart.DerivedPassiveEdge dce = ce as TestChart.DerivedPassiveEdge;
				if (dce != null)
					foreach (TestChart.DerivedPassiveEdge cce in dce.Daughters)
						f(cce);
				h ^= ce.Self.s.GetHashCode();
				h ^= ce.ChartSpan.GetHashCode();
			};
		f(e);
		return ((h >> 16) ^ h) & 0xFFFF;
	}

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// 
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	int OverallHash(TestChart chart)
	{
		int overall_hash = 0;
		foreach (TestChart.DerivedPassiveEdge dce in chart.DerivationTopEdges(uint.MaxValue))
		{
			unchecked { overall_hash += GetTreeHashCode(dce); }
		}
		return overall_hash;
	}

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// 
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	void PrintChart(String[] warr, TestChart chart)
	{
		Console.WriteLine();
		int i_d = 0;
		foreach (TestChart.DerivedPassiveEdge dce in chart.DerivationTopEdges(uint.MaxValue)
			.Where(e => !f_sonly || (e.Self.s == "S" && e.ChartSpan==chart.EntireSpan))
			.OrderBy(e => GetTreeHashCode(e))
			)
		{
			Console.WriteLine("... Derivation #{0} {1:X4} ...", i_d++, GetTreeHashCode(dce));
			for (IEnumerable<TestChart.DerivedPassiveEdge> iece = Enumerable.Repeat(dce, 1); iece.Any(); iece = iece.OfType<TestChart.DerivedPassiveEdge>().SelectMany(ee => ee.Daughters.OfType<TestChart.DerivedPassiveEdge>()))
			{
				int cpos = 0;
				foreach (TestChart.DerivedPassiveEdge xce in iece)
				{
					//String k = xce.Contents.s;
					//String k = xce.GetHashCode().ToString("X8");
					String k = xce.Self.s;
#if false
					if (xce is TestChart.NAryDerivedChartEdge)
					{
						uint sid = (xce as TestChart.NAryDerivedChartEdge)._rb_seq_id;
						k += "-" + (sid == uint.MaxValue ? "n/a" : sid.ToString());
					}
#endif
					String s0 = new String(' ', (xce.ChartSpan.StartIndex - cpos) * 11);
					int w = xce.ChartSpan.Length * 11 - 1;
					String s2 = k.PadLeft(w / 2 + k.Length / 2, '=').PadRight(w, '=') + " ";

					Console.Write(s0 + s2);

					cpos = xce.ChartSpan.EndIndex + 1;
				}
				Console.WriteLine();
			}
			Console.Write(new String(' ', dce.ChartSpan.StartIndex * 11));
			for (int i = dce.ChartSpan.StartIndex; i <= dce.ChartSpan.EndIndex; i++)
				Console.Write(warr[i].PadLeft(10) + " ");
			Console.WriteLine();
			Console.WriteLine();
		}

#if false
		Chart.Layout layout = chart.GetLayout;

		for (int j = layout.Max(e => e.Count); j >= 0; j--)
		{
			foreach (List<ChartEdge> ccb in layout)
			{
				if (j < ccb.Count)
				{
					Console.Write(ccb[j].Parent.s.PadLeft(10, '=') + );
				}
				else
					Console.Write(new String(' ', 10));
				Console.Write(' ');
			}
			Console.WriteLine();
		}
		for (int i = 0; i < warr.Length; i++)
		{
			Console.Write(warr[i].PadLeft(10) + " ");
		}
		Console.WriteLine();
#elif false

#if false
		for (int span=1; span <= chart.ColumnCount; span++)
		{
			for (int i=0; i < chart.ColumnCount - (span - 1); i++)
			{
				chart.chart[i][span - 1] = chart.chart[i][span - 1] ?? new List<ChartEdge>();
				chart.chart[i][span - 1].Add(new TestChartEdge(new ChartSpan(i, (uint)span), new TfsEdge(String.Format("{0}-{1}", i, span))));
			}
		}
#endif


#if false
		for (int span=chart.ColumnCount; span > 0; span--)
		{
			//Console.Write("{0}:", span);
			List<ChartEdge> lce;
			List<ChartEdge>[] span_cols = chart.CopiedSpansOfLength(span);

			int mod = chart.ColumnCount - (span - 1);
			bool f_any = false;
			for (int i_col=0; ; i_col += span)
			{
				if (i_col >= mod)
				{
					Console.WriteLine();
					i_col = i_col % mod;
					if (i_col == 0)
					{
						if (!f_any)
							break;
						f_any = false;
					}
					Console.Write(new String(' ', 11 * i_col));
				}

				if ((lce = span_cols[i_col]) != null)
				{
					ChartEdge ce = lce[0];

					Console.Write(ce.Contents.s.PadLeft(10, '=') + new String('=', 11 * (ce.Span.Length - 1)));

					lce.RemoveAt(0);
					if (lce.Count == 0)
						span_cols[i_col] = null;
					f_any = true;
				}
				else
					Console.Write(new String(' ', 11 * span));
				Console.Write(' ');
			}
		}
#endif

#else
		/*
		for (int j = chart.RightwardsEdges(uint.MaxValue).Max(e => e.Count()); j >= 0; j--)
		{
			foreach (ConcurrentList<ChartEdge> ccb in chart)
			{
				if (j < ccb.Count)
				{
					Console.Write(ccb[j].Contents.s.PadLeft(10, '='));
				}
				else
					Console.Write(new String(' ', 10));
				Console.Write(' ');
			}
			Console.WriteLine();
		}
		for (int i = 0; i < warr.Length; i++)
		{
			Console.Write(warr[i].PadLeft(10) + " ");
		}
		Console.WriteLine();
		 * */

#endif
	}

};