#define GC_MONITORING
#define EXHAUSTIVE_POOL_SEARCH
#define TESTSUITE
//#define TLDB

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

using glue.Collections.BitArray;
using glue.Extensions.String;
using glue.Extensions.Enumerable;
using glue.Debugging;
using glue.Extensions.Math;

using agree;
using agree.Parse;

using tldb;

using glue;
using glue.Tokenization;

#pragma warning disable 0618

class MainClass
{
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// 
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	static void Main(string[] args)
	{
		Console.WriteLine("gcServer={0}", System.Runtime.GCSettings.IsServerGC);
#if GC_MONITORING
		GC.RegisterForFullGCNotification(75, 75);
		bool checkForNotify = true;
		new Thread(() =>
			{
				//using (var q = new ThreadPriorityRegion(ThreadPriority.AboveNormal))
				{
					while (checkForNotify)
					{
						GCNotificationStatus s = GC.WaitForFullGCApproach();
						if (s == GCNotificationStatus.Succeeded)
						{
							ParseChart pc = ParseChart.active_chart;
							if (pc != null)
							    pc.Pause();

				//			Thread.Yield();

							if (_g != null)
							{
								//if (_g.loadtray.c_truncators > 0)
								//    foreach (Thread th in _g.loadtray.truncators)
								//        th.Priority = ThreadPriority.AboveNormal;

								/// even though it's too bad that the truncation is far behind when there's a GC coming up,
								/// it's still wasteful to block any threads over the situation
								//SpinWait.SpinUntil(() => _g.loadtray.mre_truncate_ok.WaitOne(0));
							}

							//ParseChart.gc_idle.Reset();
							//ParseChart.f_pause_for_gc = true;

							/// stop it again now that the idle event is blocking
							//pc = ParseChart.active_chart;
							//if (pc != null)
							//    pc.Pause();

							//Console.Write(Environment.NewLine + "GC Collect {0}/{1}... ", GC.CollectionCount(2), GC.CollectionCount(3));

							//Console.Write("collect... ");
							GC.Collect();

							//Thread.Yield();

							//GC.Collect();

							if (pc != null)
								pc.manual_gcs++;

							while (true)
							{
								s = GC.WaitForFullGCComplete(1000);
								if (s == GCNotificationStatus.Succeeded)
								{
									//Console.Write("complete {0}/{1} ", GC.CollectionCount(2), GC.CollectionCount(3));

									//Console.Write("finalizers... ");
									//GC.WaitForPendingFinalizers();

									//Console.Write("pause... ");
									//Thread.Sleep(500);

									//Console.WriteLine("done.");

									//ParseChart.f_pause_for_gc = false;
									//ParseChart.gc_idle.Set();
									//pc = ParseChart.active_chart;
									if (pc != null)
									    pc.Resume();
									break;
								}
								else
									Console.Write("GC Wait ({0}) {1}/{2}", s, GC.CollectionCount(2), GC.CollectionCount(3));

							}
						}
						else
							Console.WriteLine("GC Approach ({0}, ignoring)", s);
					}
				}
				Console.WriteLine("exit GC monitoring");
			}).Start();
#endif

		new MainClass()._Main(args);

#if GC_MONITORING
		checkForNotify = false;
		GC.CancelFullGCNotification();
#endif

		if (Debugger.IsAttached)
		{
			Console.WriteLine("done! press <enter> to exit");
			Console.ReadLine();
		}
	}

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// 
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public void ParseG6Morph(Grammar g)
	{
		int c_parses;
		using (TimingReport tr = new TimingReport(Console.Out, "Parsing..."))
		{
			c_parses = 0;
			try
			{
				using (ParseChart pc = DoParse(g, "give that cat to the dog"))
				{
					if (pc != null)
					{
						c_parses = pc.CompletedParses.Count;
						Console.WriteLine(c_parses);
					}
				}
			}
			catch (AggregateException)
			{
			}
		}
	}


	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// 
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public void ParseThai(Grammar g)
	{
		int c_parses;

		Tray.MonospaceFormatter mf = null;
		if (f_tfs)
			mf = new Tray.MonospaceFormatter(g.loadtray);

		using (TimingReport tr = new TimingReport(Console.Out, "Parsing..."))
		{
			//String s_sent = "เขา ไป ซื้อ ดอกไม้ ที่ ตลาด ไป เยี่ยม เพื่อน และ";
			if (sentences_to_parse.Count > 0)
			{
				int i = 0;
				foreach (String l in sentences_to_parse)
				{
					c_parses = 0;
					try
					{
						TimingReport tr2 = null;
						if (f_trees || f_tfs || f_parse_info)
							tr2 = new TimingReport(Console.Out, l);
						using (ParseChart pc = DoParse(g, l))
						{
							if (pc != null)
							{
								c_parses = pc.CompletedParses.Count;

								if (f_chart_dump)
									pc.DumpChart(String.Format("chart_dump-{0}.html", i));

								if (f_trees || f_tfs)
								{
									foreach (var px in pc.CompletedParses)
									{
										if (f_trees)
											Console.WriteLine(g.nl.GetParseTree(pc, px));
										if (f_tfs)
										{
											Console.WriteLine(mf.FormatEdge(px.Contents.Edge));
										}
									}
								}
							}
							if (f_trees)
								tr2.Dispose();
						}
					}
					catch (ParseException)
					{
					}
					catch (AggregateException)
					{
					}
					Console.WriteLine("{0} parses", c_parses);
					i++;
				}
			}
			else
			{
				foreach (String l in File.ReadAllLines("grammars/thai/testsuite.txt").Skip(skipitems).Take(takeitems))
				{
					String[] rgs = l.Split('\t');
					int id = int.Parse(rgs[0]);
					c_parses = 0;
					try
					{
						TimingReport tr2 = null;
						if (f_trees || f_tfs || f_item_info || f_parse_info)
							tr2 = new TimingReport(Console.Out, String.Format("{0}", id));
						using (ParseChart pc = DoParse(g, rgs[1]))
						{
							if (pc != null)
							{
								c_parses = pc.CompletedParses.Count;

								if (f_chart_dump)
									pc.DumpChart(String.Format("chart_dump-{0}.html", id));

								if (f_trees || f_tfs)
								{
									foreach (var px in pc.CompletedParses)
									{
										if (f_trees)
											Console.WriteLine(g.nl.GetParseTree(pc, px));
										if (f_tfs)
										{
											Console.WriteLine(mf.FormatEdge(px.Contents.Edge));
										}
									}
								}
							}
						}
						if (f_trees || f_tfs || f_item_info || f_parse_info)
							tr2.Dispose();
					}
					catch (ParseException)
					{
					}
					catch (AggregateException)
					{
					}
					int c_parses_exp = int.Parse(rgs[2]);
					if (c_parses != c_parses_exp)
					{
						String msg = String.Format("parser regression test failed for [{0}]\n{1} parses, expected {2}", rgs[1], c_parses, c_parses_exp);
						//throw new Exception(msg);
						Console.WriteLine(msg);
					}

#if FCTC_STATS
					g.tm.TypePatternReport();
#endif

					g.lex.ReleaseAll();
					//foreach (GrammarRule r in g._grammar_rules)
					//    r.ReleaseExpanded();

					if (f_garbage)
					{
						rch1 = g.Reachable();
						Console.WriteLine(g.GarbageReport(rch1));
					}
				}
			}
#if false
			Console.WriteLine("processing {0} test items", igts.Count);
			foreach (var testitem in igts)
			{
				Phrase phr = testitem.phr;
				//phr = db.GetPhrase(219755);
				String[] sent = phr.Parts.Select(e => e.FullDop.Thai(default(DbObj.LayoutOpts))).ToArray();
				String s_sent = sent.StringJoin(" ");
				Console.WriteLine();
				Console.WriteLine("Parsing: P{0} {1}", phr.Id, s_sent);
				try
				{
					using (ParseChart pc = DoParse(sent))
					{
						c_parses = pc.CompletedParses.Count;
					
				}
				catch (AggregateException ex)
				{
					c_parses = 0;
					//Console.WriteLine(ex.Flatten().InnerException);
				}
				//Console.WriteLine();
				//Console.WriteLine(g.GarbageReport());

				Console.WriteLine("P{0} {1} Completed Parses: {2}", phr.Id, s_sent, c_parses);
				//Console.ReadLine();
				sw_parsecounts.WriteLine("{0}\t{1}\t{2}", phr.Id, s_sent, c_parses);
			}
#endif
		}

		g.lex.ReleaseAll();
		//foreach (GrammarRule r in g._grammar_rules)
		//    r.ReleaseExpanded();

		if (!f_no_regress && f_garbage)
		{
			rch1 = g.Reachable();
			Console.WriteLine(g.GarbageReport(rch1));
#if DEBUG && EXHAUSTIVE_POOL_SEARCH
			Tray.RootAnalysis ra = new Tray.RootAnalysis(g.loadtray);
			foreach (Edge ge in g.loadtray.TreeTops(rch1.Except(rch0)))
			{
				Console.WriteLine("{0}\t\t{1}", ge.ToString(), ra.PathInfo(ge.Mark));
			}
#endif
		}
	}

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// 
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public void ParseErg(Grammar g)
	{
		int c_unif;
		int c_unif_tot = 0;
		int c_parses;
		double sec_tot = 0;

		Tray.MonospaceFormatter mf = null;
		if (f_tfs)
			mf = new Tray.MonospaceFormatter(g.loadtray);

		String fdate = String.Format("/programming/analytical-grammar/hike/{0:yyyyMMdd\\-hhmmss}-hike-results.txt", DateTime.Now);
		File.Delete(fdate);

		var hike_regress = File.ReadAllLines("/programming/analytical-grammar/hike/agree-regress.txt").Select(oo => oo.Split('\t')).Select(vv =>
			new { id = int.Parse(vv[0]), readings = vv[1] == "" ? 0 : int.Parse(vv[1]), cunif = vv[2] == "" ? 0 : int.Parse(vv[2]) }).ToDictionary(a => a.id);

		var original_lines = System.IO.File.ReadAllLines(@"/programming/analytical-grammar/hike/hike-results-use.txt", Encoding.UTF8)
			.ToArray();
		var rg_items = original_lines
			.Select(vvv =>
			{
				var wxyz = vvv.Split('\t');
				var rsr = wxyz[0];
				bool sk = rsr[0] == '#';
				rsr = rsr.Trim('#');
				return new
				{
					id = int.Parse(rsr),
					skip = sk,
					text = wxyz[1].Trim('\"'),
					/*		sec = (Double)int.Parse(wxyz[2]) / 1000,
							readings = int.Parse(wxyz[3])  */
				};
			}).ToArray();

		String[] rg_sent =
			rg_items.Skip(skipitems).Take(takeitems).Select(vsr => vsr.text).ToArray();
		//{ "The most important preparation for a mountain tour is to keep in reasonably good shape." };
		//{ "If you come with the morning boat, you can start the trip the same day." };
		//{ "The most important preparation for a tour is to work hard." };
		//{ "The preparation for a tour is to work hard." };
		//{ "Be considerate of game, farm animals and other hikers." };
		//{ "The cats are cute." };
		//{ "The cute cat is fat." };
		//{ "In the city mountains you can rest, set up camp and spend the night." };
		//{"Both Voss Utferdslag and Bergen Turlag are very concerned about the military's expansion plans."};

		if (sentences_to_parse.Count == 0)
			sentences_to_parse.AddRange(rg_sent);

		using (TimingReport tr = new TimingReport(Console.Out, "Parsing..."))
		{
			Double u_s = 0.0, us_tot = 0.0;
			int item_ix = 0;
			Console.Out.WriteLine("sec.  unif.    u/s  avg.    GB  GC rsz".PadLeft(133));
			foreach (String sent in sentences_to_parse)
			{
				double mem = 0;

				String disp_sent = sent.SubstringOrLess(0, 79);

				c_parses = -1;
				c_unif = -1;
				String result_entry = "";
				int ix;
				int num;
				Double sec = -1;
				if ((ix = Array.FindIndex(rg_items, oo => oo.text == sent)) != -1)
				{
					result_entry = original_lines[ix] + "\t";
					num = rg_items[ix].id;
					if (rg_items[ix].skip)
						goto skipit;
				}
				else
				{
					ix = num = item_ix;
				}
				sec = double.NaN;

				Console.Out.WriteColorSync("{0,3:##0} {1,3:##0} $yellow {2} ", ix, num, disp_sent);
				try
				{
					int rz = 0;
					if (g.loadtray is ConcurrentTray)
						rz = ((ConcurrentTray)g.loadtray).c_resizes;

					g.loadtray.mre_truncate_ok.WaitOne();

					int gen = GC.CollectionCount(2);
					using (ParseChart pc = DoParse(g, sent))
					{
						if (f_quick_exit)
							Environment.Exit(0);
						if (pc == null)
						{
							sec = 0;
							Console.Out.WriteLineColorSync("$red null chart");
						}
						else
						{
							gen = GC.CollectionCount(2) - gen;
							c_parses = pc.CompletedParses.Count;

							c_unif = pc.UnificationCount;
							c_unif_tot += c_unif;

							sec = pc.ParseTime.TotalMilliseconds / 1000;
							sec_tot += sec;
							u_s = c_unif / sec;
							us_tot = c_unif_tot / sec_tot;
							mem = (double)GC.GetTotalMemory(false) / (1024 * 1024 * 1024);

							String gcinfo = "";
							if (gen > 0 || pc.manual_gcs > 0)
								gcinfo = String.Format("{0}-{1}", gen, pc.manual_gcs);

							String rsz = "";
							if (g.loadtray is ConcurrentTray)
							{
								rz = ((ConcurrentTray)g.loadtray).c_resizes - rz;
								((ConcurrentTray)g.loadtray).c_resizes = 0;
								if (rz > 0)
									rsz = rz.ToString();
							}
							String pp = String.Format("{0:#0.000} {1,6:0} {2,6:0} {3,5:0} {4,5:#0.00} {5,3} {6,3}",
								sec,
								c_unif,
								u_s,
								us_tot,
								mem,
								gcinfo,
								rsz);
							Console.Out.WriteLineColorSync("$magenta {0}$green {1}{2}",
								c_parses,
								new String(' ', Math.Max(0, 124 - c_parses.ToString().Length - disp_sent.Length - pp.Length)),
								pp);

							var a = hike_regress.FirstOrDefault(kvp => kvp.Key == num).Value;
							if (a.id == num)
							{
								if (a.readings != c_parses)
									Console.WriteLine("error: parses {0}, expected {1}", c_parses, a.readings);
								if (a.cunif != c_unif)
									Console.WriteLine("error: unif {0}, expected {1}", c_unif, a.cunif);
							}

							if (f_chart_dump)
								pc.DumpChart(String.Format("chart_dump-{0}.html", item_ix));

							if (f_trees || f_tfs)
							{
								foreach (var px in pc.CompletedParses)
								{
									if (f_trees)
										Console.WriteLine(g.nl.GetParseTree(pc, px));

									if (f_tfs)
										Console.WriteLine(mf.FormatEdge(px.Contents.Edge));
								}
							}
						}
						//((ConcurrentTray)g.loadtray).Report();
#if false
						Console.WriteLine("pm-tot: {0}  load: min {1} ({2}) max {3} ({4}) avg {5:0}",
							g.loadtray.PoolMarkCount,
							g.loadtray.Pools.Min(cp => cp.Count),
							g.loadtray.Pools.ArgMin(cp => cp.Count).Feature,
							g.loadtray.Pools.Max(cp => cp.Count),
							g.loadtray.Pools.ArgMax(cp => cp.Count).Feature,
							g.loadtray.Pools.Average(cp => cp.Count));
						Console.WriteLine(g.tm.GlbCacheInfo());
#endif
					}
					/// wait for chart dispose
					//Console.Write("chart dispose...");
					g.loadtray.mre_truncate_ok.WaitOne();
					//Console.WriteLine("done.");

				}
				catch (ParseException ex)
				{
					Console.Out.WriteLineColorSync("$red {0} {1}", ex.GetType().Name, ex.Message);
					c_parses = 0;

					Console.WriteLine("cleaning garbage");
					g.loadtray.CleanGarbage(g.Reachable());
				}
				catch (AggregateException ex)
				{
					Exception rex = ex.Flatten().InnerExceptions.FirstOrDefault(x => !(x is AggregateException));
					rex = rex ?? ex;

					if (!(rex is ParseTimeoutException))
						Console.WriteLine();
					Console.Out.WriteLineColorSync("$red {0} {1}", rex.GetType().Name, rex.Message);
					if (!(rex is ParseTimeoutException))
						Console.WriteLine(rex.StackTrace);
					c_parses = 0;

					Console.WriteLine("cleaning garbage");
					g.loadtray.CleanGarbage(g.Reachable());
				}

#if FCTC_STATS
				g.tm.TypePatternReport();
#endif
			skipit:
				//foreach (ProcessThread thr in Process.GetCurrentProcess().Threads)
				//{
				//    thr.PriorityLevel = ThreadPriorityLevel.Normal;
				//    if (thr.Id != gc_monitor_native_thread_id)
				//        thr.PriorityLevel = ThreadPriorityLevel.Normal;
				//}

				if (sec == -1)
					result_entry += "\"\"\td.n.a.\t\"\"\t\"\"\t\"\"";
				else if (double.IsNaN(sec))
					result_entry += String.Format("{0}\t>{1:0}s.\t{2}\t{3}\t{4}", String.Empty, timeout, String.Empty, c_unif, us_tot);
				else
					result_entry += String.Format("{0}\t{1}\t{2}\t{3}\t{4}", c_parses, String.Empty, sec, c_unif, us_tot);
				using (var xxxx = File.AppendText(fdate))
					xxxx.WriteLine(result_entry);

				g.lex.ReleaseAll();

				//foreach (GrammarRule r in g._grammar_rules)
				//    r.ReleaseExpanded();

				if (f_garbage)
				{
					rch1 = g.Reachable();
					Console.WriteLine(g.GarbageReport(rch1));
				}

				g.loadtray.ResetMark();
				//Console.WriteLine("{0:X8}", g.loadtray.next_mark);

				item_ix++;

				//Console.WriteLine("glb-cache: {0}  pm-tot: {1}  load: min {2} ({3}) max {4} ({5}) avg {6}",
				//    g.tm.glb_cache.Count,
				//    g.loadtray.PoolMarkCount,
				//    g.loadtray.Pools.Min(cp => cp.Count),
				//    g.loadtray.Pools.ArgMin(cp => cp.Count).Feature,
				//    g.loadtray.Pools.Max(cp => cp.Count),
				//    g.loadtray.Pools.ArgMax(cp => cp.Count).Feature,
				//    g.loadtray.Pools.Average(cp => cp.Count));

				if (f_profile && item_ix % 5 == 1)
				{
					Console.Out.WriteLineColorSync("$red pausing...");
					Thread.Sleep(20000);
				}

#if false
				if (u_s < 10000)
				{
					for (int j = 0; j < 5; j++)
					{
						Console.Write("GC... ");
						GC.Collect();
						GC.WaitForFullGCComplete();
						Console.Write("finalizers... ");
						GC.WaitForPendingFinalizers();
						//Console.Write("pause... ");
						//Thread.Sleep(500);
						Console.WriteLine("done.");
					}
				}
#endif
#if true
				if (mem > 4.0)
				{
					Console.Write("GC ");
					g.loadtray.mre_truncate_ok.WaitOne();

					GC.Collect();
					GC.WaitForFullGCComplete();
					Console.Write("{0:#0.00} ", (double)GC.GetTotalMemory(false) / (1024 * 1024 * 1024));

					Console.Write("f ");
					GC.WaitForPendingFinalizers();

					Console.Write("pause ");
					Thread.Sleep(500);

					Console.WriteLine(" done.");
				}
#endif
			}
		}
	}

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// 
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

	bool f_parse_info = false;
	bool f_no_regress = false;
	bool f_trees = false;
	bool f_garbage = false;
	bool f_tfs = false;
	bool f_chart_dump = false;
	bool f_quick_exit = false;
	bool f_profile = false;
	bool f_no_parse = false;
	bool f_item_info = false;
	int skipitems = 0;
	int takeitems = int.MaxValue;
	int max_truncators = int.MaxValue;
	List<String> sentences_to_parse = new List<String>();
	double timeout = double.NaN;
	HashSet<PoolMark> rch1, rch0 = null;

	static Grammar _g;


	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// 
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	void _Main(String[] args)
	{
		Console.OutputEncoding = Encoding.UTF8;
		Console.ForegroundColor = (ConsoleColor)7;

		for (int i = 0; i < args.Length; i++)
			if (args[i].StartsWith("--"))
				args[i] = args[i].Substring(1);

		bool f_verbose = args.Contains("-v");
		if (f_verbose)
			args = args.Where(e => e != "-v").ToArray();

		if (args.Contains("-help") || args.Contains("-?"))
		{
			Console.WriteLine(helpmsg);
			return;
		}

		int arg_ix;
		if ((arg_ix = Array.IndexOf<String>(args, "-singlethreaded")) != -1)
		{
			Console.WriteLine("disabling multi-threading");
			glue.Debugging.Nop.single_threaded = true;
			ParseChart.c_tasks_max = -1;
			args = args.Where((e, x) => x != arg_ix).ToArray();
		}

		if ((arg_ix = Array.IndexOf<String>(args, "-timeout")) != -1 && ++arg_ix < args.Length)
		{
			if (!double.TryParse(args[arg_ix], out timeout))
				timeout = double.NaN;
			args = args.Where((e, x) => x < arg_ix - 1 || x > arg_ix).ToArray();
		}

		if ((arg_ix = Array.IndexOf<String>(args, "-parseinfo")) != -1)
		{
			f_parse_info = true;
			args = args.Where((e, x) => x != arg_ix).ToArray();
		}

		if ((arg_ix = Array.IndexOf<String>(args, "-iteminfo")) != -1)
		{
			f_item_info = true;
			args = args.Where((e, x) => x != arg_ix).ToArray();
		}

		if ((arg_ix = Array.IndexOf<String>(args, "-noregress")) != -1)
		{
			Console.WriteLine("disabling regression test");
			f_no_regress = true;
			args = args.Where((e, x) => x != arg_ix).ToArray();
		}

		if ((arg_ix = Array.IndexOf<String>(args, "-trees")) != -1)
		{
			f_trees = true;
			args = args.Where((e, x) => x != arg_ix).ToArray();
		}

		if ((arg_ix = Array.IndexOf<String>(args, "-garbage")) != -1)
		{
			f_garbage = true;
			args = args.Where((e, x) => x != arg_ix).ToArray();
		}

		if ((arg_ix = Array.IndexOf<String>(args, "-tfs")) != -1)
		{
			f_tfs = true;
			args = args.Where((e, x) => x != arg_ix).ToArray();
		}

		if ((arg_ix = Array.IndexOf<String>(args, "-parsechart")) != -1)
		{
			f_chart_dump = true;
			args = args.Where((e, x) => x != arg_ix).ToArray();
		}

		if ((arg_ix = Array.IndexOf<String>(args, "-quickexit")) != -1)
		{
			f_quick_exit = true;
			args = args.Where((e, x) => x != arg_ix).ToArray();
		}

		if ((arg_ix = Array.IndexOf<String>(args, "-warmboot")) != -1)
			return;

		if ((arg_ix = Array.IndexOf<String>(args, "-profile")) != -1)
		{
			f_profile = true;
			args = args.Where((e, x) => x != arg_ix).ToArray();
		}

		if ((arg_ix = Array.IndexOf<String>(args, "-noparse")) != -1)
		{
			f_no_parse = true;
			args = args.Where((e, x) => x != arg_ix).ToArray();
		}

		arg_ix = 0;
		while ((arg_ix = Array.IndexOf<String>(args, "-parse", arg_ix)) != -1 && arg_ix + 1 < args.Length)
		{
			sentences_to_parse.Add(args[arg_ix + 1]);
			args = args.Where((e, x) => x < arg_ix || x > arg_ix + 1).ToArray();
			arg_ix--;
		}

		if ((arg_ix = Array.IndexOf<String>(args, "-maxtruncators")) != -1 && arg_ix + 1 < args.Length)
		{
			int.TryParse(args[arg_ix + 1], out max_truncators);
			args = args.Where((e, x) => x < arg_ix || x > arg_ix + 1).ToArray();
		}

		if ((arg_ix = Array.IndexOf<String>(args, "-skipitems")) != -1 && arg_ix + 1 < args.Length)
		{
			int.TryParse(args[arg_ix + 1], out skipitems);
			args = args.Where((e, x) => x < arg_ix || x > arg_ix + 1).ToArray();
		}

		if ((arg_ix = Array.IndexOf<String>(args, "-takeitems")) != -1 && arg_ix + 1 < args.Length)
		{
			int.TryParse(args[arg_ix + 1], out takeitems);
			args = args.Where((e, x) => x < arg_ix || x > arg_ix + 1).ToArray();
		}

		if ((arg_ix = Array.IndexOf<String>(args, "-ctaskmax")) != -1 && arg_ix + 1 < args.Length)
		{
			int.TryParse(args[arg_ix + 1], out ParseChart.c_tasks_max);
			Console.WriteLine("parser max tasks: {0}", ParseChart.c_tasks_max);
			args = args.Where((e, x) => x < arg_ix || x > arg_ix + 1).ToArray();
		}

		String filename = args.FirstOrDefault(e => e[0] != '-');
		if (filename == null)
		{
			Console.WriteLine(helpmsg);
			return;
		}

		String base_file = Path.GetFileNameWithoutExtension(filename).ToLower();
		String ext = Path.GetExtension(filename).ToLower();

		Thread.CurrentThread.Priority = ThreadPriority.AboveNormal;

		Grammar g;
		using (TimingReport tr = new TimingReport(Console.Out, "Loading grammar file"))
			g = new SysCommands.CmdTokLoadGrammar(SysObj.Instance, filename, base_file).Task.Result;
		_g = g;

		if (max_truncators != int.MaxValue)
		{
			Console.WriteLine("max truncators: {0}", max_truncators);
			g.loadtray.max_truncators = max_truncators;
		}

		if (!double.IsNaN(timeout))
		{
			Console.WriteLine("parser timeout: {0}", timeout);
			g.tm.config.ParserConfiguration.ItemTimeoutSeconds = timeout;
		}

		if (f_parse_info)
			g.Parser.SetDebugOutput(Console.Out);

		if (!f_no_regress)
		{
			g.tm.RegressionTest(Console.Out, base_file);
		}

		if (f_garbage)
		{
			rch0 = g.Reachable();
			Console.WriteLine(g.GarbageReport(rch0));
		}

		if (!f_no_parse)
		{
			//if (g.loadtray is ConcurrentTray)
			//{
			//    Console.Write("shrink tray");
			//    ((ConcurrentTray)g.loadtray).Shrink();
			//}

			if (base_file == "thai")
			{
				ParseThai(g);
			}
			else if (base_file == "erg")
			{
				ParseErg(g);
			}
			else if (base_file == "g6morph")
			{
				ParseG6Morph(g);
			}
		}
		Console.WriteLine();
	}

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// 
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	ParseChart DoParse(Grammar g, String sent)
	{
		var t = new SysCommands.CmdTokParse(g, sent).Task;
		if (t == null)
			return null;
		return t.Result;
	}

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// 
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

	static String helpmsg =
@"usage:

example:
    agree [options] grammar_script_file

Options:
    -noregress			disable regression test
    -timeout ss			set parser timeout per sentence (sec.)
    -trees				show ASCII parse tree
    -tfs				show ASCII tfs
    -garbage			garbage edge report
    -parseinfo			verbose parsing output
    -singlethreaded		disable multi-threading
    -skipitems nn		skip over 'n' testsuite items
    -takeitems nn		process 'n' testsuite items
    -ctaskmax nn		maximum OS tasks allowed for parser
    -parsechart			dump HTML parse chart to 'chart_dump.html'
    -parse ""Sentence to parse."" [-parse ""Another sentence"" ...]
";
}