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

using miew.Debugging;
using miew.Enumerable;
using miew.Tokenization;
using miew.String;
using miew.String.Builder;
using miew.Concurrency;

namespace agree
{
	public abstract partial class PassiveEdge : ArrayTfs, IParseObj
	{
#if DEBUG
		public partial class Derived : PassiveEdge
		{
			public void DumpDerivations(TextWriter tw = null)
			{
				tw = tw ?? Console.Out;
				System.Text.StringBuilder sb = null;
				if (_packed_edges == null)
					return;

				sb = new System.Text.StringBuilder();
				sb.AppendFormatLine("================== {0} ================", this.ToString());

				sb.AppendFormatLine("daughters:");
				IDerivation[][] rgrg = new IDerivation[rgo.Count][];
				for (int j = 0; j < rgo.Count; j++)
				{
					rgrg[j] = rgo[j].Derivations.ToArray();

					sb.AppendFormatLine("    #{0} derivations: {1}", j, rgrg[j].Length);
					for (int k = 0; k < rgrg[j].Length; k++)
					{
						sb.AppendFormatLine("        {0}", rgrg[j][k].ToString());
					}
				}

				sb.AppendLine();

				sb.AppendFormatLine("packed: {0} edges", _packed_edges.Count);
				int i = 0;
				foreach (Derived dpe in _packed_edges)
				{
					sb.AppendFormatLine("    packed edge #{0} {1} derivations: {2}", i++, dpe.ToString(), dpe.Derivations.Count);
					foreach (IDerivation dv in dpe.Derivations)
					{
						sb.AppendFormatLine("    === dv #{0} {1} ===", i++, dv.Source.ToString());
						//sb.AppendFormatLine("{0}", dv.TreeDisplay().ToString());
						if (dv is ParseTree)
							sb.AppendLine(((ParseTree)dv).Select(d => d.ToString()).StringJoin(Environment.NewLine).Indent(8));
						else
							sb.AppendLine("        " + ((LexicalAnalysis.AnalysisStack)dv).ToString());
						sb.AppendLine();

						//sb.AppendFormatLine("        {0}", dpe.rgo[j].ToString());
						//for (int j = 0; j < rgo.Count; j++)
						//{
						//    sb.AppendFormatLine("        {0}", dpe.rgo[j].ToString());
						//}
					}
				}
				sb.AppendLine();

				sb.AppendFormatLine("cross-product: {0}", _parse_trees.Length);
				i = 0;
				foreach (var dv in _parse_trees)
				{
					sb.AppendFormatLine("    === dv #{0} {1} ===", i++, dv.Source.ToString());
					//sb.AppendFormatLine("{0}", dv.TreeDisplay().ToString());
					if (dv is ParseTree)
						sb.AppendLine(((ParseTree)dv).Select(d => d.ToString()).StringJoin(Environment.NewLine).Indent(8));
					else
						sb.AppendLine("        " + ((LexicalAnalysis.AnalysisStack)dv).ToString());
					sb.AppendLine();
				}
				sb.AppendLine();

				tw.WriteLine(sb.ToString());
			}
		};

		[DebuggerDisplay("{ToString(),nq}")]
		public partial class ParseTree : IDerivation, IList<IDerivation>
		{
			static int next_id = 0;

			[DebuggerBrowsable(DebuggerBrowsableState.Never)]
			public readonly int id;

			[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
			public ParseTree[] _TREE
			{
				get
				{
					ParseTree[] rg = new ParseTree[daughters.Length];
					for (int i = 0; i < daughters.Length; i++)
						rg[i] = daughters[i] as ParseTree ??
								new LexEntryPlaceholder(ctrl, (LexicalAnalysis.AnalysisStack)daughters[i]);
					return rg;
				}
			}

			[DebuggerDisplay("{ToString(),nq}")]
			public class LexEntryPlaceholder : ParseTree
			{
				LexicalAnalysis.AnalysisStack stk;
				public LexEntryPlaceholder(ParseControl ctrl, LexicalAnalysis.AnalysisStack stk)
					: base(ctrl, stk, null)
				{
					this.stk = stk;
				}
				public override string ToString()
				{
					return stk.ToString();
				}
			}

			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// 
			/// </summary>
			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			public override string ToString()
			{
				int p = 0;
				String pp = "";
				if (mother is PassiveEdge)
				{
					PassiveEdge pt = (PassiveEdge)mother;
					if (pt._packed_edges != null)
					{
						p = pt._packed_edges.Count;
						pp = " [" + pt._packed_edges.Select(po => String.Format("#{0:X}", po.Tfs.id)).StringJoin(", ") + "]";
					}
				}

				String md = "";
				if (mother is Derived)
				{
					Derived dpe = (Derived)mother;
					md = dpe.ChartDaughters.Select(po => String.Format("#{0:X}", po.Tfs.id)).StringJoin(", ");
				}

				return String.Format("{0:X} #{1:X} [{2}] peers: {3}{4}  dtrs: {5} [{6}]",
					GetHashCode() & 0xFFFF,
					mother.Tfs.id,
					md,
					p,
					pp,
					daughters.Length,
					daughters.Select(d => String.Format("{0:X}", d.GetHashCode() & 0xFFFF)).StringJoin(", "));
			}


		};
#endif
	};


	abstract public unsafe partial class ChartBase : ISysObj, IEnumerable<IParseObj>
	{
#if DEBUG
		public void CheckDuplicateDerivations()
		{
			ConcurrentHashSet<ulong> d_hashes = new ConcurrentHashSet<ulong>();

			int c_derivations = 0;
			foreach (var drv in ((ParseChart)this).AllDerivations)
			{
				d_hashes.Add(drv.DerivationHash);
				c_derivations++;
			}

			if (c_derivations != d_hashes.Count)
			{
				//throw new Exception(String.Format("{0} duplicate derivations", c_derivations - d_hashes.Count));
				var grps = ((ParseChart)this).AllDerivations.GroupBy(drv => drv.DerivationHash).Where(grp => grp.Count() > 1);
				Console.WriteLine(String.Format("{0} duplicate derivations in {1} groups", c_derivations - d_hashes.Count, grps.Count()));

				List<String> l0 = new List<String>(), l1 = new List<String>();
				foreach (var grp in grps)
				{
					l0.Add(grp.ElementAt(0).TreeDisplay());
					l1.Add(grp.ElementAt(1).TreeDisplay());
					System.IO.File.WriteAllLines(String.Format("000.tfs-dump"), l0);
					System.IO.File.WriteAllLines(String.Format("001.tfs-dump"), l1);

					Console.WriteLine("=========================");
					foreach (var drv in grp)
					{
						Console.WriteLine("{0}", drv.TreeDisplay());
						Console.WriteLine();
					}
				}
			}
			//if (rgdrv != null)
			//{
			//    if (rgdrv.Count != hsdrv.Count)
			//        report.AppendLine(String.Format("derivations: {0} --- distinct: {1}    (extra {2})", rgdrv.Count, hsdrv.Count, rgdrv.Count - hsdrv.Count));
			//}

		}

		public IEnumerable<PassiveEdge.Derived> AllDerivedEdges()
		{
			var e = new ChartCell._enum_all_edges(this);
			while (e.MoveNext())
			{
				PassiveEdge.Derived dpe = e.Current as PassiveEdge.Derived;
				if (dpe != null)
					yield return dpe;
			}
		}

		internal IEnumerable<PassiveEdge.Derived> OrphanEdges()
		{
			var ade = AllDerivedEdges();
			return ade.Except(ade.SelectMany(dce => dce.ChartDaughters.OfType<PassiveEdge.Derived>()));
		}

		public IEnumerable<PassiveEdge.Derived> DerivationTopEdges()
		{
			return OrphanEdges().OfType<PassiveEdge.Derived>();
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// DEBUG CODE FOLLOWS
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

		public void ChartEdgeReport(IParseObj ce)
		{
			String span_color = ce.TokenSpan == EntireSpan ? "$yellow" : "$darkyellow";
			String sis = SequenceControl.ToString(ce);
			PassiveEdge.Derived dce = ce as PassiveEdge.Derived;
			TextWriter debug_output = dce.ctrl.config.system.tw_debug;
			if (dce == null)
			{
				debug_output.WriteLineColor("$darkcyan {0,-18} $green E{2,-4} " + span_color + " {3,-7} $green {4}",
					ce.GetType().Name,
					null,
					sis,
					ce.TokenSpan,
					ce.Tfs);
			}
			else
			{
				String fedge = dce.Tfs.ToString();
				String medge = fedge.Replace("@", "$green @").Replace("…", "…$darkgreen ");
				debug_output.WriteLineColor(new String(' ', 25) + span_color + " {0,-7}       $darkgreen {1}$ added by $darkgreen {2}$ is now $darkcyan {3}$ $green E{4,-4}",
					dce.TokenSpan,
					medge.PadRight(35 + (medge.Length - fedge.Length)),
					dce.ChartDaughters.Select(de => String.Format("{0}", de.ToString())).StringJoin("$ + $darkgreen ").PadLeft(9 + (dce.ChartDaughters.Count() - 1) * 12),
					dce.GetType().Name,
					sis);
			}
		}


		public void DumpChart(String html_out_file)
		{

			var derivations = DerivationTopEdges()
				.Select((de, ix) => new { top = de, ix, de.DescendantsAndSelf });

			var edge_lookup = derivations
								.SelectMany(dv => dv.DescendantsAndSelf.Select(e => new { top = dv, dv.ix, e }))
								.ToLookup(de => de.e, de => new { de.ix, de.top });

			StringBuilder sb1 = new StringBuilder();
			foreach (var derivation in derivations)
			{
				sb1.AppendFormat(@"
.dv{0}
{{
}}", derivation.ix);
			}

			StringBuilder sb = new StringBuilder();
			sb.AppendFormat(@"
<html>
<head>
<style>
body
{{
font-family:Arial;
}}
span
{{
cursor: pointer;
}}
.fill
{{
background-color:#f0f0f0;
}}
table tr td
{{
vertical-align:top;
font-size:smaller;
}}
{0}
</style>
</head>
<body>
", sb1.ToString());
			int c = ColumnCount;
			double grid_cells_x = 120;// Enumerable.Range(1, c - 1).LeastCommonMultiple();

			sb.Append("<table border=1><tr>");
			int cx_total = 0;
			for (int j = 0; j < c; j++)
			{
				int cx = (int)Math.Round(grid_cells_x * (j + 1) / c) - cx_total;
				cx_total += cx;
				sb.AppendFormat("<th colspan={0}>{1}</th>\r\n", cx, j);
			}
			sb.Append("</tr>\r\n\r\n");

			var dict = this._GroupBy(e => e.TokenSpan.Length)
							.ToDictionary(g => g.Key, g => g.ToLookup(e => e.TokenSpan.StartIndex));

			for (int span = c; span > 0; span--)
			{
				sb.Append("<tr>\r\n");

				ILookup<int, IParseObj> length_starts_map;
				if (dict.TryGetValue(span, out length_starts_map))
				{
					cx_total = 0;
					int items = c - span + 1;
					for (int k = 0; k < items; k++)
					{
						int cx = (int)Math.Round(grid_cells_x * (k + 1) / items) - cx_total;
						cx_total += cx;

						IEnumerable<IParseObj> edges = length_starts_map[k];

						String s_cont;
						if (edges != null && edges.Any())
						{
							StringBuilder content = new StringBuilder();
							foreach (IParseObj pe in edges)
							{
								String kk = edge_lookup[pe].Select(a => String.Format("dv{0}", a.ix)).StringJoin(" ");
								String b = pe is PassiveEdge.Completed ? "style='font-weight:bold;' " : String.Empty;
								content.AppendFormat("\t<span {0}class='{1}'>{2}</span><br />\r\n", b, kk, pe.ToString().Replace("-", "&#8209;"));
							}
							s_cont = content.ToString();
						}
						else
							s_cont = "&nbsp;";

						sb.AppendFormat("\t<td colspan='{0}'>\r\n{1}</td>\r\n", cx, s_cont);
					}
				}
				sb.Append("</tr>\r\n");
			}

			sb.AppendFormat("</table>");
			sb.Append(@"
<script type='text/javascript'>
	function getCSSRule(ruleName) {
		ruleName = ruleName.toLowerCase();
		if (document.styleSheets) {
			for (var i = 0; i < document.styleSheets.length; i++) {
				var styleSheet = document.styleSheets[i];
				var ii = 0;
				var cssRule = null;
				do {
					if (styleSheet.cssRules) {
						cssRule = styleSheet.cssRules[ii];
					} else {
						cssRule = styleSheet.rules[ii];
					}
					if (cssRule) {
						if (cssRule.selectorText.toLowerCase() == ruleName) {
							return cssRule;
						}
					}
					ii++;
				} while (cssRule)
			}
		}
		return null;
	}

	function ms_in(e) {
		var x = e.className.split(' ');
		for (var z = 0; z < x.length; z++) {
			var r = getCSSRule('.' + x[z]);
			if (r != null)
				r.style.color = 'red';
		}
	}

	function ms_out(e) {
		var x = e.className.split(' ');
		for (var z = 0; z < x.length; z++) {
			var r = getCSSRule('.' + x[z]);
			if (r != null)
				r.style.color = 'black';
		}
	}

	var obj = document.getElementsByTagName('span'), o, i = 0;
	while (o = obj[i++]) {
		o.onmouseover = function() { ms_in(this); };
		o.onmouseout = function() { ms_out(this); };
	}
</script>
");

			sb.AppendFormat("</body></html>");
			File.WriteAllText(html_out_file, sb.ToString());
		}
#else
		public void ChartEdgeReport(IParseObj ce) { }
		public void DumpChart(String html_out_file) { }
#endif
	};
}