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

using glue.Collections.XSpinLock;
using glue.Debugging;
using glue.Extensions.Enumerable;
using glue.Tokenization;

namespace agree.Parse
{
	abstract public partial class ChartBase : ISysObj
	{
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// 
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public interface ISelf : IDisposable
		{
			TfsEdge Contents { get; }
			bool SpinCompare(TfsEdge other);
		};

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// for (e.g.) TfsEdge -- doesn't know it's license
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public interface IMotherDaughter : ISelf
		{
			IEnumerable<TfsEdge> RuleDaughters { get; }
		};

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		///
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public interface ILicense : ISelf
		{
			ISysObj License { get; }
		};

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// 
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public interface IParseChartRule : IMotherDaughter, ILicense
		{
			new IList<TfsEdge> RuleDaughters { get; }
			int KeyDaughterIndex { get; }
			bool IsSpanOnly { get; }
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// 
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public interface IGrammarRule : IParseChartRule
		{
			bool CheckDaughterCompatibility(ISysObj daughter, int i_arg);
		};

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// 
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public interface IParseChartEdge : ILicense, IAtomicallySequenceable
		{
			Span ChartSpan { get; }
		};

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// 
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public interface IParseChartToken : IParseChartEdge
		{
			String Text { get; }
		};

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

		public void ChartEdgeReport(IParseChartEdge ce)
		{
			if (debug_output != null)
			{
				String span_color = ce.ChartSpan == EntireSpan ? "$yellow" : "$darkyellow";
				String sis = ce.SequenceId == ast.InertValue ? "--" : ce.SequenceId.ToString();
				DerivedPassiveEdge dce = ce as DerivedPassiveEdge;
				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.ChartSpan,
						ce.Contents);
				}
				else
				{
					String fedge = dce.Contents.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.ChartSpan,
						medge.PadRight(35 + (medge.Length - fedge.Length)),
						dce.Daughters.Select(de => String.Format("{0}", de.ToString())).StringJoin("$ + $darkgreen ").PadLeft(9 + (dce.Daughters.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.AllEdges()
							._GroupBy(e => e.ChartSpan.Length)
							.ToDictionary(g => g.Key, g => g.ToLookup(e => e.ChartSpan.StartIndex));

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

				ILookup<int, IParseChartEdge> 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<IParseChartEdge> edges = length_starts_map[k];

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

		public TextWriter debug_output = null;

		public void SetDebugOutput(TextWriter tw)
		{
			debug_output = tw;
		}
	};
}