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

using miew.Enumerable;
using miew.String;

namespace agree
{
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// 
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public class GrammarNodeLabeler
	{
		NodeLabelTemplate[] label_templates;
		NodeMetaTemplate[] meta_templates;
		NodeLabelTemplate[] local_label_templates;
		Config.NodeLabels nlc;
		Grammar g;
		TypeMgr tm;

		public GrammarNodeLabeler(Grammar g, Config.NodeLabels nlc, IEnumerable<NodeLabelTemplate> labels, IEnumerable<NodeMetaTemplate> metas)
		{
			this.g = g;
			this.tm = g.tm;
			this.nlc = nlc;
			label_templates = labels.ToArray();
			meta_templates = metas.ToArray();
			local_label_templates = labels.Where(nlt => nlt.effective_local != null).ToArray();
		}

		public String FindLabel(Tfs pe)
		{
			foreach (NodeLabelTemplate nlt in label_templates)
			{
				Tfs tfs;
				if (nlc.LabelFsPath != null && nlc.LabelFsPath.Count > 0)
				{
					TfsSection ts = pe.GetSection(nlc.LabelFsPath);
					if (ts == null)
						continue;
					tfs = ts;
				}
				else
					tfs = pe;

				if (g.tm.da.UnifyCheck(tfs, nlt.effective))
				{
					String s_label = nlt.Label;
					TfsSection te_meta;
					if (meta_templates.Length > 0 && (te_meta = pe.GetSection(nlc.RecursivePath)) != null)
					{
						foreach (NodeMetaTemplate nmt in meta_templates)
						{
							if (g.tm.da.UnifyCheck(nmt.effective, te_meta))
							{
								Tfs tfs_local;
								if (nlc.LocalPath != null && nlc.LocalPath.Count > 0)
								{
									TfsSection ts = tfs.GetSection(nlc.LocalPath);
									tfs_local = ts;
								}
								else
									tfs_local = tfs;

								foreach (NodeLabelTemplate nlt_sub in label_templates)
								{
									/// jikes.
									if (g.tm.da.UnifyCheck(tfs_local, nlt_sub.effective_local))
										return s_label + nmt.Prefix + nlt_sub.Label + nmt.Suffix;
								}
							}
						}
					}
					return s_label;
				}
			}
			return null;
		}
		public ParseTree GetParseTree(ParseChart pc, IDerivation pe)
		{
			return new ParseTree(this, pc, pe.Source);
		}

		public class ParseTree
		{
			static readonly ParseTree[] rg_pt_empty = new ParseTree[0];

			public ParseTree(GrammarNodeLabeler gnl, ParseChart pc, IParseObj pce)
			{
				this.pc = pc;
				this.pce = pce;
				this.label = gnl.FindLabel(pce.Tfs) ?? pce.Tfs.Name;
				PassiveEdge.Derived dpe = pce as PassiveEdge.Derived;
				if (dpe != null)
					children = dpe.ChartDaughters.Select(d => new ParseTree(gnl, pc, d)).ToArray();
				else
					children = rg_pt_empty;
			}


			readonly ParseChart pc;
			readonly IParseObj pce;
			readonly ParseTree[] children;
			readonly String label;

			public ParseTree[] Children { get { return children; } }

			public String Label { get { return label; } }

			//public IParseObj ChartEdge { get { return pce; } }

			public String SourceText
			{
				get
				{
					var tok = pce as IParseObj;
					if (tok != null)
						return tok.Text;
					return String.Empty;
					//return pc.InputTokens.Source.MinimalSpanText(pce.ChartSpan);
				}
			}

			public override String ToString()
			{
				return children.Length == 0 ?
							label :
							String.Format("{0} ({1})", label, children.StringJoin(" "));
			}

			public String MonospaceFormat()
			{
				var ca = children.Select(c => c.MonospaceFormat().Split(Environment.NewLine.ToCharArray(), StringSplitOptions.RemoveEmptyEntries)).ToArray();
				String s = String.Format("{0}", label);
				if (ca.Length > 0)
				{
					int width = ca.Max(c => c.Max(s_lab => s_lab.Length)) + 2;
					int j_max = ca.Max(c => c.Length);

					StringBuilder sb = new StringBuilder();
					sb.AppendLine(s.PadCenter(width * ca.Length, ' '));

					for (int j = 0; j < j_max; j++)
					{
						for (int q = 0; q < ca.Length; q++)
						{
							String s0;
							if (j < ca[q].Length)
								s0 = ca[q][j];
							else
								s0 = "";
							sb.Append(s0.PadCenter(width, ' '));
						}
						sb.AppendLine();
					}
					s = sb.ToString();
				}
				else
					s += Environment.NewLine;
				return s;
			}
		};
	};
}