using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using ActiproSoftware.Windows.Controls.Docking;
using miew.Enumerable;
using miew.String;
using miew.Lambda;
using agree;
using agree.Wpf.Util;

#pragma warning disable 0649

namespace agree.Wpf
{
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	///
	///
	///
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public abstract class AgreeDocument : DocumentWindow
	{
		public AgreeDocument()
			: base(MyDockSite.g_docksite)
		{
			this.CanRaft = true;
		}

		public virtual void Print(PrintDialog pd) { }
	}

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	///
	///
	///
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	abstract public class AgreeGrammarDocument : AgreeDocument
	{
		public AgreeGrammarDocument(Grammar g)
		{
			this.g = g;
		}
		protected Grammar g;
	}

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	///
	///
	///
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	class ExpandedWindow : AgreeGrammarDocument
	{
		readonly agree.ISysObj so;

		public agree.ISysObj Inst { get { return so; } }

		public ExpandedWindow(agree.Instance t)
			: base(t.tm.g)
		{
			this.so = t;
			uc_tfs stg = new uc_tfs();
			stg.TfsControl._TfsEdge = t.Expanded;

			Entry ex = t as Entry;
			if (ex != null)
			{
				stg.entry_name.Text = t.Name;
				stg.entry_name.Visibility = System.Windows.Visibility.Visible;
				stg.split_line.Visibility = System.Windows.Visibility.Visible;
			}

			this.Title = String.Format("{0}: expanded", t.Name);
			this.Description = "Expanded typed feature structure for ";
			this.Content = stg;

			//		stg.TfsControl.SizeChanged += new SizeChangedEventHandler(TfsControlSizeChanged);

			this.KeyDown += (sender, e) =>
				{
					if (e.Key == Key.Escape)
						this.Close();
				};
		}

		public ExpandedWindow(Tfs tfs)
			: base(tfs.tm.g)
		{
			this.so = tfs;
			uc_tfs stg = new uc_tfs();
			stg.TfsControl._TfsEdge = tfs;

			this.Title = String.Format("{0}", tfs.ToString());
			this.Description = "typed feature structure";
			this.Content = stg;

			//		stg.TfsControl.SizeChanged += new SizeChangedEventHandler(TfsControlSizeChanged);

			this.KeyDown += (sender, e) =>
			{
				if (e.Key == Key.Escape)
					this.Close();
			};
		}


		public ExpandedWindow(Tfs tfs, Tfs tfs_highlight)
			: base(tfs.tm.g)
		{
			this.so = tfs;
			uc_tfs stg = new uc_tfs(tfs, tfs_highlight);

			this.Title = String.Format("{0}", tfs.ToString());
			this.Description = "typed feature structure";
			this.Content = stg;

			//		stg.TfsControl.SizeChanged += new SizeChangedEventHandler(TfsControlSizeChanged);

			this.KeyDown += (sender, e) =>
			{
				if (e.Key == Key.Escape)
					this.Close();
			};
		}

		/// <summary>
		/// Set the initial floating window size for when it's dragged out of docking position
		/// </summary>
		void TfsControlSizeChanged(object sender, SizeChangedEventArgs e)
		{
			if (State == DockingWindowState.Document)
			{
				uc_tfs stg = this.Content as uc_tfs;

				TfsControl tfsc = stg.TfsControl;
				Size sz = tfsc.ContentSize;
				sz.Width += tfsc.Margin.Left + tfsc.Margin.Right;
				sz.Height += tfsc.Margin.Top + tfsc.Margin.Bottom;

				ScrollViewer sv = (ScrollViewer)stg.FindName("tfsc_sv");
				sz.Width += sv.ComputedVerticalScrollBarVisibility == System.Windows.Visibility.Collapsed ? 4 : 19;
				sz.Height += sv.ComputedHorizontalScrollBarVisibility == System.Windows.Visibility.Collapsed ? 21 : 20;

				Size ds = new Size(DockSite.Workspace.ActualWidth, DockSite.Workspace.ActualHeight);
				sz.Width = Math.Min(sz.Width, ds.Width - 40);
				sz.Height = Math.Min(sz.Height, ds.Height - 40);

				this.SetValue(DockSite.ControlSizeProperty, sz);
			}
		}

		public override void Print(PrintDialog pd)
		{
			(this.Content as uc_tfs).Print(pd);
		}

#if false
		protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
		{
			String n = (this.t != null) ? t.Name : "??";
			Debug.WriteLine("{0:X8} {1} {2} {3} -> {4}", this.GetHashCode(), n, e.Property, e.OldValue, e.NewValue);
			base.OnPropertyChanged(e);
		}
#endif
	};


	//public class TypeHierarchyWindow : AgreeGrammarDocument
	//{
	//    DagLayoutPanel dag_layout_panel;

	//    public TypeHierarchyWindow(ParseControl ctrl, IParseObj pce)
	//        : base(ctrl.g)
	//    {
	//        this.Title = pce.Tfs.Name;

	//        dag_layout_panel = new DagLayoutPanel();

	//        foreach (agree.Type t in g.tm.AllTypes)
	//        {
	//            t.Expanded;


	//        }

	//        this.Content = dag_layout_panel;
	//    }

	//};


	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	///
	///
	///
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	class TypeHierarchyWindow : AgreeGrammarDocument
	{
		readonly agree.ISysObj so;

		public agree.ISysObj Inst { get { return so; } }

		public TypeHierarchyWindow(Grammar g)
			: base(g)
		{
			this.so = g;

			this.Title = String.Format("all types");
			this.Description = "all types";

			Grid bar = new Grid();
			this.Content = bar;

			DagLayoutPanel foo = new DagLayoutPanel();
			foo.Margin = new Thickness(10, 10, 10, 10);
			Slider sl = new Slider();
			sl.Minimum = .3;
			sl.Maximum = 5;
			sl.Value = 1.0;
			sl.Width = 80;
			sl.HorizontalAlignment = System.Windows.HorizontalAlignment.Right;
			sl.VerticalAlignment = System.Windows.VerticalAlignment.Bottom;
			bar.Children.Add(foo);
			bar.Children.Add(sl);

			ScaleTransform st = new ScaleTransform();
			BindingOperations.SetBinding(st, ScaleTransform.ScaleXProperty, Anon<Binding>.New(b =>
			{
				b.Source = sl;
				b.Path = new PropertyPath("Value");
			}));
			BindingOperations.SetBinding(st, ScaleTransform.ScaleYProperty, Anon<Binding>.New(b =>
			{
				b.Source = sl;
				b.Path = new PropertyPath("Value");
			}));
			foo.Stroke = Brushes.DarkGray;
			BindingOperations.SetBinding(foo, Shape.StrokeThicknessProperty, Anon<Binding>.New(b =>
			{
				b.Source = sl;
				b.Path = new PropertyPath("Value");
			}));


			Dictionary<agree.Type, TfsControl> ctrlx = new Dictionary<agree.Type, TfsControl>();
			foreach (agree.Type t in g.tm.AllTypes)
			{
				if (t.Name == "⊤" || t.Name == "*top*" || t.IsGlb ||
					(t.Name != "string" && t.Name != "diff-list" && !t.Name.Contains('*')))
				{
					var tc = new TfsControl();
					tc._TfsEdge = t.Expanded;
					tc.Margin = new Thickness(5);
					tc.Name = t.Name.Replace("⊤", "top").Replace("*", "").Replace("-","_");
					//if (t.Name == "d")
					//{
					//    tc.Padding = new Thickness(5, 5, 200, 5);

					//}

					tc.LayoutTransform = st;

					ctrlx.Add(t, tc);
					foo.Children.Add(tc);
					tc.Background = new SolidColorBrush(Color.FromArgb(0xA0, 0xFF, 0xFF, 0xFF));
						//Brushes.White;
				}
			}
			foreach (var tc in ctrlx)
			{
				tc.Value.SetValue(DagLayoutPanel.DagParentsProperty,
					tc.Key.Parents.Select(tx => ctrlx[tx]).ToArray());
			}

			foo.FOO();

			//		stg.TfsControl.SizeChanged += new SizeChangedEventHandler(TfsControlSizeChanged);

			this.KeyDown += (sender, e) =>
			{
				if (e.Key == Key.Escape)
					this.Close();
			};
		}

		public override void Print(PrintDialog pd)
		{
			(this.Content as uc_tfs).Print(pd);
		}

#if false
		protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
		{
			String n = (this.t != null) ? t.Name : "??";
			Debug.WriteLine("{0:X8} {1} {2} {3} -> {4}", this.GetHashCode(), n, e.Property, e.OldValue, e.NewValue);
			base.OnPropertyChanged(e);
		}
#endif

	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	///
	///
	///
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	class AllTypesWindow : AgreeGrammarDocument
	{
		readonly agree.ISysObj so;

		public agree.ISysObj Inst { get { return so; } }

		public AllTypesWindow(Grammar g)
			: base(g)
		{
			this.so = g;

			this.Title = String.Format("all types");
			this.Description = "all types";

			Grid bar = new Grid();
			this.Content = bar;

			Foo foo = new Foo();
			Slider sl = new Slider();
			sl.Minimum = .3;
			sl.Maximum = 5;
			sl.Value = 1.0;
			sl.Width = 80;
			sl.HorizontalAlignment = System.Windows.HorizontalAlignment.Right;
			sl.VerticalAlignment = System.Windows.VerticalAlignment.Bottom;
			bar.Children.Add(foo);
			bar.Children.Add(sl);

			foreach (agree.Type t in g.tm.AllTypes)
			{
				if ((t.Name != "⊤" && t.Name != "string" && !t.Name.Contains('*')) || t.IsGlb)
				{
					var tc = new TfsControl();
					tc._TfsEdge = t.Expanded;
					tc.Margin = new Thickness(10);

					ScaleTransform st = new ScaleTransform();
					tc.LayoutTransform = st;

					BindingOperations.SetBinding(st, ScaleTransform.ScaleXProperty, Anon<Binding>.New(b =>
					{
						b.Source = sl;
						b.Path = new PropertyPath("Value");
					}));
					BindingOperations.SetBinding(st, ScaleTransform.ScaleYProperty, Anon<Binding>.New(b =>
					{
						b.Source = sl;
						b.Path = new PropertyPath("Value");
					}));





					foo.Children.Add(tc);

				}
			}



			//		stg.TfsControl.SizeChanged += new SizeChangedEventHandler(TfsControlSizeChanged);

			this.KeyDown += (sender, e) =>
			{
				if (e.Key == Key.Escape)
					this.Close();
			};
		}

		public override void Print(PrintDialog pd)
		{
			(this.Content as uc_tfs).Print(pd);
		}

#if false
		protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
		{
			String n = (this.t != null) ? t.Name : "??";
			Debug.WriteLine("{0:X8} {1} {2} {3} -> {4}", this.GetHashCode(), n, e.Property, e.OldValue, e.NewValue);
			base.OnPropertyChanged(e);
		}
#endif
		class Foo : WrapPanel
		{
			public ScaleTransform st;
			public Foo()
			{
				this.Orientation = System.Windows.Controls.Orientation.Horizontal;
				st = new ScaleTransform();
				this.RenderTransform = st;
			}


		}
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	///
	///
	///
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#if false
	class EdgeWindow : AgreeGrammarDocument
	{
		TfsEdge edge;

		public TfsEdge Edge { get { return edge; } }

		public EdgeWindow(TfsEdge e, String title)
			: base(e.Type.mgr.g)
		{
			this.edge = e;
			uc_tfs stg = new uc_tfs();
			stg.TfsControl._TfsEdge = edge;

			this.Title = title ?? String.Format("Feature Structure - {0}", edge.Type.Name);
			this.Description = "typed feature structure";
			this.Content = stg;

			//		stg.TfsControl.SizeChanged += new SizeChangedEventHandler(TfsControlSizeChanged);
		}

		/// <summary>
		/// Set the initial floating window size for when it's dragged out of docking position
		/// </summary>
		void TfsControlSizeChanged(object sender, SizeChangedEventArgs e)
		{
			if (State == DockingWindowState.Document)
			{
				uc_tfs stg = this.Content as uc_tfs;

				TfsControl tfsc = stg.TfsControl;
				Size sz = tfsc.ContentSize;
				sz.Width += tfsc.Margin.Left + tfsc.Margin.Right;
				sz.Height += tfsc.Margin.Top + tfsc.Margin.Bottom;

				ScrollViewer sv = (ScrollViewer)stg.FindName("tfsc_sv");
				sz.Width += sv.ComputedVerticalScrollBarVisibility == System.Windows.Visibility.Collapsed ? 4 : 19;
				sz.Height += sv.ComputedHorizontalScrollBarVisibility == System.Windows.Visibility.Collapsed ? 21 : 20;

				Size ds = new Size(DockSite.Workspace.ActualWidth, DockSite.Workspace.ActualHeight);
				sz.Width = Math.Min(sz.Width, ds.Width - 40);
				sz.Height = Math.Min(sz.Height, ds.Height - 40);

				this.SetValue(DockSite.ControlSizeProperty, sz);
			}
		}

		public override void Print(PrintDialog pd)
		{
			(this.Content as uc_tfs).Print(pd);
		}

#if false
		protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
		{
			String n = (this.t != null) ? t.Name : "??";
			Debug.WriteLine("{0:X8} {1} {2} {3} -> {4}", this.GetHashCode(), n, e.Property, e.OldValue, e.NewValue);
			base.OnPropertyChanged(e);
		}
#endif
	};
#endif

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	///
	///
	///
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public class InteractiveWindow : AgreeDocument
	{
		Stopwatch stopw;
		public TextBlock display;
		TextBox input;
		Label prompt;
		Button execute;

		ISysObj current_obj;

		MainWindow w;

		List<String> history = new List<String>();
		int i_history = 0;

		public InteractiveWindow(MainWindow w, ISysObj def)
		{
			this.w = w;
			this.FontSize = 15;

			Grid gd = new Grid();

			gd.ColumnDefinitions.Add(new ColumnDefinition());

			// scrolling message area
			RowDefinition rd = new RowDefinition();
			gd.RowDefinitions.Add(rd);
			// splitter bar
			rd = new RowDefinition();
			rd.Height = GridLength.Auto;
			gd.RowDefinitions.Add(rd);
			// input area
			rd = new RowDefinition();
			rd.Height = GridLength.Auto;
			gd.RowDefinitions.Add(rd);

			display = new TextBlock();
			display.Margin = new Thickness(4);
			ScrollViewer sv = new ScrollViewer();
			sv.SetValue(Grid.ColumnProperty, 0);
			sv.SetValue(Grid.RowProperty, 0);
			sv.Content = display;
			gd.Children.Add(sv);

			GridSplitter spl = new GridSplitter();
			spl.HorizontalAlignment = System.Windows.HorizontalAlignment.Stretch;
			spl.VerticalAlignment = System.Windows.VerticalAlignment.Center;
			spl.BorderThickness = new Thickness(0, 1, 0, 1);
			spl.BorderBrush = new SolidColorBrush(Color.FromRgb(0xe0, 0xe0, 0xe0));
			spl.Style = (Style)this.FindResource("gridSplitterStyle");
			spl.Height = 7;
			spl.SetValue(Grid.ColumnProperty, 0);
			spl.SetValue(Grid.RowProperty, 1);
			gd.Children.Add(spl);

			// input area
			{
				DockPanel isp = new DockPanel();
				//isp.HorizontalAlignment = System.Windows.HorizontalAlignment.Stretch;
				isp.Width = double.NaN;

				prompt = new Label();
				prompt.Width = 50;
				PromptText = "sys";
				prompt.Margin = new Thickness(0, 3, 0, 0);
				prompt.SetValue(DockPanel.DockProperty, System.Windows.Controls.Dock.Left);
				isp.Children.Add(prompt);

				current_obj = def;

				// this must be attached to the dock next (before the central input area)
				execute = new Button();
				execute.Margin = new Thickness(0, 5, 5, 0);
				execute.Content = "execute";
				execute.SetValue(DockPanel.DockProperty, System.Windows.Controls.Dock.Right);
				execute.VerticalAlignment = System.Windows.VerticalAlignment.Top;
				execute.Click += new RoutedEventHandler((s, e) =>
					{
						SubmitCommand(input.Text);
					});
				isp.Children.Add(execute);

				input = new TextBox();
				input.Margin = new Thickness(5);
				input.PreviewKeyDown += (s, e) =>
					{
						if (e.Key == Key.Up && i_history > 0)
						{
							input.Text = history[--i_history];
						}
						else if (e.Key == Key.Down && i_history < history.Count - 1)
						{
							input.Text = history[++i_history];
						}
						else
							return;
						e.Handled = true;
					};
				input.KeyDown += (s, e) =>
				{
					if (e.Key == Key.Escape)
						input.Text = String.Empty;
					if (e.Key == System.Windows.Input.Key.Enter)
						SubmitCommand(input.Text);
					else if (e.Key == System.Windows.Input.Key.L && Keyboard.Modifiers == ModifierKeys.Control)
						SubmitCommand("cls");
				};
				isp.Children.Add(input);

				isp.SetValue(Grid.ColumnProperty, 0);
				isp.SetValue(Grid.RowProperty, 2);
				gd.Children.Add(isp);

				this.GotFocus += new RoutedEventHandler((o, e) => { input.Focus(); });
			}

			this.Content = gd;
			this.stopw = Stopwatch.StartNew();
			this.Title = "Interactive";
		}

		public void Clear()
		{
			display.Text = String.Empty;
		}

		public CommandResultResponder CurrentHandler
		{
			get
			{
				return w.cmd_responders[current_obj];
			}
			set
			{
				current_obj = value.so;
				PromptText = value.so.SysObjName;
			}
		}

		string prompt_text;
		String PromptText
		{
			get
			{
				return prompt_text; ;
			}
			set
			{
				prompt_text = value;
				prompt.Content = prompt_text + " >";
			}
		}

		void SubmitCommand(String s)
		{
			if (s == "tsdb hike")
			{
				MyDockSite mds = MyDockSite.g_docksite;
				ItsdbDocument idoc = new ItsdbDocument(current_obj);
				mds.wksp.tmc.Items.Add(idoc);
				idoc.Activate();
				return;
			}
			else if (s == "all types")
			{
				MyDockSite mds = MyDockSite.g_docksite;
				AllTypesWindow idoc = new AllTypesWindow(current_obj as Grammar);
				mds.wksp.tmc.Items.Add(idoc);
				idoc.Activate();
				return;
			}
			else if (s == "type hierarchy")
			{
				MyDockSite mds = MyDockSite.g_docksite;
				TypeHierarchyWindow idoc = new TypeHierarchyWindow(current_obj as Grammar);
				mds.wksp.tmc.Items.Add(idoc);
				idoc.Activate();
				return;
			}
			//else if (s == "a ⊓ d")
			//{
			//    MyDockSite mds = MyDockSite.g_docksite;
			//    Grammar g = current_obj as Grammar;
			//    Unification uh = new Unification(g.tm, 99);
			//    TargetTfs tt = uh.Unify(g.tm.type_dict["a"].Expanded, g.tm.type_dict["d"].Expanded);
			//    Tfs te = tt.ToArrayTfs();

			//    ExpandedWindow idoc = new ExpandedWindow(te);
			//    idoc.Title = "a ⊓ d";
			//    mds.wksp.tmc.Items.Add(idoc);
			//    idoc.Activate();
			//    return;
			//}


			history.Add(s);
			i_history = history.Count;
			input.Clear();
			WriteLine("<span style='color:#808080;'>{0}</span> > <span style='color:#800000;'>{1}</span>", current_obj.SysObjName, s);

			CurrentHandler.QueueResultContinuation(new SysCommands.CmdTokInteractive(current_obj, s));
		}

		public void Write(String s, params Object[] args)
		{
			//(this.Content as TextBox).Text += String.Format(s,args);
		}
		public void WriteLine(CommandToken x, String s, params Object[] args)
		{
			String ss;
			SysCommands.CmdTokDisplayMessage xm = x as SysCommands.CmdTokDisplayMessage;
			if (xm != null)
			{
				if (!xm.IsComplete)
					throw new Exception();
				ss = xm.Task.Result;
			}
			else
			{
				//ss = String.Format("<span style='color:#808080;'>{0,8:d\\:hh\\:mm\\:ss}</span> ", stopw.Elapsed);
				ss = String.Empty;

				if (x != null)
					ss += String.Format("<span style='color:#bb9d13;'>[{0}]</span> ", (x.Context ?? x.SystemInstance).SysObjName);

				ss += String.Format(s, args) + Environment.NewLine;
			}

			//display.Inlines.AddRange(ParseFormatting(ss));
			display.Inlines.Add(new HTMLtoWPF().ParseFormatting(ss));
			((this.Content as Grid).Children[0] as ScrollViewer).ScrollToBottom();
		}
		public void WriteLine(String s, params Object[] args)
		{
			WriteLine(null, s, args);
		}

		public IEnumerable<Run> ParseFormatting(String s)
		{
			Stack<Brush> stk_b = new Stack<Brush>();
			Brush b_cur = Brushes.Black;

			String[] parts = s.Split(new Char[] { '\ue099' });
			int i = 0;
			while (i < parts.Length)
			{
				String part = parts[i++];
				if (i > 0)
				{
					if (part.Length >= 7 && part[0] == '-')
					{
						int i_color;
						if (int.TryParse(part.Substring(1, 6), NumberStyles.HexNumber, null, out i_color))
						{
							stk_b.Push(b_cur);
							b_cur = new SolidColorBrush(Color.FromRgb((byte)(i_color >> 16), (byte)(i_color >> 8), (byte)(i_color)));
							part = part.Substring(7);
						}
					}
					else if (stk_b.Count > 0)
					{
						b_cur = stk_b.Pop();
					}
				}
				Run r = new Run(part);
				r.Foreground = b_cur;
				yield return r;
			}
		}


	}


	class HTMLtoWPF
	{
		Dictionary<String, Func<Span, Dictionary<String, String>, Dictionary<String, String>, Span>> tag_dict;

		struct StackEntry
		{
			public StackEntry(String tag, Span obj)
			{
				this.tag = tag;
				this.obj = obj;
			}
			public String tag;
			public Span obj;
		};


		public HTMLtoWPF()
		{
			tag_dict = new Dictionary<String, Func<Span, Dictionary<String, String>, Dictionary<String, String>, Span>>
				{
					{ "a",  (cur,avd,sd) =>
								{
									if (avd == null)
										return null;
									Hyperlink h = new Hyperlink();
									foreach (var kvp in avd)
									{
										if (kvp.Key=="href")
										{
											h.NavigateUri = new Uri(kvp.Value);
										}
									}
									return h;
								}
					},
					{ "i",  (cur,avd,sd) =>
								{
									Span sp = new Span();
									sp.FontStyle = FontStyles.Italic;
									return sp;
								}
					},
					{ "b",  (cur,avd,sd) =>
								{
									Span sp = new Span();
									sp.FontWeight = FontWeights.Bold;
									return sp;
								}
					},
					{ "u",  (cur,avd,sd) =>
								{
									Span sp = new Span();
									sp.TextDecorations.Add(TextDecorations.Underline);
									return sp;
								}
					},
					{ "br",  (cur,avd,sd) =>
								{
									cur.Inlines.Add(new LineBreak());
									return null;
								}
					},
					{ "span",  (cur,avd,sd) =>
								{
									if (sd == null)
										return null;
									Span sp = new Span();
									foreach (var kvp in sd)
									{
										if (kvp.Key=="color")
										{
											try {
												Color z = (Color)ColorConverter.ConvertFromString(kvp.Value);
												sp.Foreground = new SolidColorBrush(z);
											}
											catch
											{
											}
										}
										else if (kvp.Key=="font-weight" && kvp.Value=="bold")
										{
											sp.FontWeight = FontWeights.Bold;
										}
									}
									return sp;
								}
					},


				};
		}

		enum TagType { None = 0, Open, Close, AutoClose };

		Stack<StackEntry> stk = new Stack<StackEntry>();

		static Char[] tag_term = { ' ', '\t', '\r', '\n' };
		static Char[] style_split = { ':', ';' };

		public Span ParseFormatting(String s)
		{
			Span sp_base = new Span();
			Span sp_cur = sp_base;

			/// remove control characters
			s = s.Select(ch => ch < ' ' ? ' ' : ch).NewString();

			/// condense spaces
			int ix;
			while ((ix = s.IndexOf("  ")) != -1)
				s = s.Remove(ix, 1);

			/// find HTML tags
			String[] parts = s.Split(new Char[] { '<' });

			for (int i = 0; i < parts.Length; i++)
			{
				String part = parts[i];
				if (i > 0)
				{
					ix = part.IndexOf('>');
					if (ix < 1)
					{
						sp_cur.Inlines.Add(new Run("HTML parse error, missing closing tag '>' or empty HTML tag"));
						break;
					}
					/// Get the HTML tag type
					String s_tag = part.Remove(ix).Trim().ToLower();
					part = part.Substring(ix + 1);

					/// is it a closing tag?
					TagType found_tag_type = TagType.Open;
					if (s_tag[0] == '/')
					{
						found_tag_type = TagType.Close;
						s_tag = s_tag.Substring(1);
					}
					else if (s_tag.EndsWith("/"))
					{
						found_tag_type = TagType.AutoClose;
						s_tag = s_tag.Remove(s_tag.Length - 1).Trim();
					}

					String s_av = null;
					ix = s_tag.IndexOfAny(tag_term);
					if (ix != -1)
					{
						s_av = s_tag.Substring(ix + 1).Trim();
						s_tag = s_tag.Remove(ix).Trim();
					}

					if (found_tag_type == TagType.Open || found_tag_type == TagType.AutoClose)
					{
						/// get attribute-value pairs if any
						Dictionary<String, String> attribute_values = null;
						Dictionary<String, String> styles = null;
						if (s_av != null)
						{
							var rgav = s_av.Split('=');
							if ((rgav.Length & 1) > 0)
								throw new Exception();

							attribute_values = rgav.PairOff().ToDictionary(av => av.Key, av => av.Value.Trim('\'', '\"'));

							String sp;
							if (attribute_values.TryGetValue("style", out sp))
							{

								var rgsp = sp.Split(style_split, StringSplitOptions.RemoveEmptyEntries);
								if ((rgsp.Length & 1) > 0)
									throw new Exception();

								styles = rgsp.PairOff().ToDictionary(av => av.Key.Trim(), av => av.Value.Trim());
							}
						}

						Func<Span, Dictionary<String, String>, Dictionary<String, String>, Span> open_func;
						if (!tag_dict.TryGetValue(s_tag, out open_func))
						{
							sp_cur.Inlines.Add(new Run(String.Format("HTML parse error, unrecognized tag '{0}'", s_tag)));
							break;
						}

						Span ns = open_func(sp_cur, attribute_values, styles);
						if (ns != null && found_tag_type != TagType.AutoClose)
						{
							stk.Push(new StackEntry(s_tag, sp_cur));
							sp_cur.Inlines.Add(ns);

							sp_cur = ns;
						}
					}
					else if (found_tag_type == TagType.Close)
					{
						if (stk.Count == 0)
						{
							sp_cur.Inlines.Add(new Run(String.Format("HTML parse error, can't close tag '{0}' because there are no tags open", s_tag)));
							break;
						}
						if (stk.Peek().tag != s_tag)
						{
							sp_cur.Inlines.Add(new Run(String.Format("HTML parse error, can't close tag '{0}' because it is not the pending tag", s_tag)));
							break;
						}
						sp_cur = stk.Pop().obj;
					}
				}

				/// Add the unformatted text
				if (part.Length > 0)
					sp_cur.Inlines.Add(new Run(part));
			}
			sp_cur.Inlines.Add(new LineBreak());

			if (stk.Count != 0)
				throw new Exception();

			return sp_base;
		}

	};


	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	///
	///
	///
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	class T3dWindow : AgreeGrammarDocument
	{
		readonly agree.Type t;

		public agree.Type Type { get { return t; } }

		public T3dWindow(agree.Type t)
			: base(t.tm.g)
		{
			this.t = t;
			Zoom3D stg = new Zoom3D(g, t.Expanded, MainWindow.w);
			stg.ClipToBounds = true;

			this.Title = String.Format("{0}: 3d", t.Name);
			this.Description = "Expanded typed feature structure for ";
			this.Content = stg;
		}

	};


	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	///
	///
	///
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	class ParseChartWindow : AgreeGrammarDocument
	{
		readonly ParseControl ctrl;
		readonly ParseChart chart;

		public ParseChart ParseChart { get { return chart; } }

		public ParseChartWindow(ParseControl ctrl, IEnumerable<IDerivation> use_edges)
			: base(ctrl.g)
		{
			this.ctrl = ctrl;
			this.chart = ctrl.chart;

			String title = String.Format("{0}", ctrl.SysObjName);
			//PassiveEdge.Completed cp = null;
			//if (use_edges != null && use_edges.OfType<PassiveEdge.Completed>().Count() == 1)
			//{
			//    cp = use_edges.OfType<PassiveEdge.Completed>().First();
			//    title += "-" + cp.ToString();
			//}
			//this.Title = title;

			this.Description = ctrl.SysObjDescription;

			ScrollViewer sv = new ScrollViewer();
			sv.VerticalScrollBarVisibility = ScrollBarVisibility.Auto;
			sv.HorizontalScrollBarVisibility = ScrollBarVisibility.Disabled;

			VizParseChart vpc = new VizParseChart(chart, use_edges);
			vpc.Margin = new Thickness(10);
			//if (cp == null)
			//{
			//    sv.Content = vpc;
			//}
			//else
			//{
			//    sv.Content = Anon<StackPanel>.New(sp =>
			//        {
			//            sp.Orientation = Orientation.Vertical;
			//            sp.Children.Add(vpc);
			//            sp.Children.Add(new VizParseTree(ctrl, cp));
			//        });
			//}

			this.Content = sv;

			//		stg.TfsControl.SizeChanged += new SizeChangedEventHandler(TfsControlSizeChanged);

			this.KeyDown += (sender, e) =>
			{
				if (e.Key == Key.Escape)
					this.Close();
			};
		}

		//public override void Print(PrintDialog pd)
		//{
		//    (this.Content as uc_tfs).Print(pd);
		//}

		class VizParseChart : Grid
		{
			public VizParseChart(ParseChart pc, IEnumerable<IDerivation> use_edges)
			{
				this.SetValue(RoundedRectangle.ContainerParentProperty, true);

				Border bthis = new Border();
				this.Children.Add(bthis);

				bthis.BorderBrush = Brushes.Black;
				bthis.BorderThickness = new Thickness(0, 0, 1, 1);
				//				bthis.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;
				bthis.VerticalAlignment = System.Windows.VerticalAlignment.Top;
				bthis.SetValue(TextBlock.FontSizeProperty, 11.8);

				StackPanel rows_stack = new StackPanel();
				rows_stack.Orientation = Orientation.Vertical;
				//				rows_stack.VerticalAlignment = System.Windows.VerticalAlignment.Top;
				//			rows_stack.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;
				//rows_stack.MaxWidth = 1200;

				if (use_edges == null)
					use_edges = pc.AllDerivations;

				var lu0 = use_edges.OfType<PassiveEdge.Completed>().ToLookup(pe => pe.TokenSpan);
				var lu1 = use_edges.Select(pe=>pe.Source)
									.Where(pe => pe.GetType() != typeof(LexicalAnalysis.AnalysisStack) &&
												pe.GetType() != typeof(PassiveEdge.Completed))
									.ToLookup(pe => pe.TokenSpan);
				var lu2 = use_edges.OfType<LexicalAnalysis.AnalysisStack>().ToLookup(pe => pe.TokenSpan);
				var lu3 = pc.ctrl.chart_tokens.Distinct(tok => tok.TokenSpan).ToDictionary(tok => tok.TokenSpan);

				for (int i = 0; i < pc.ColumnCount; i++)
				{
					uint span = (uint)(pc.ColumnCount - i);

					bool f_spantext = i < pc.ColumnCount - 1;

					int i_subrows = 0;
					int b_subrows = 0;

					if (f_spantext)
					{
						i_subrows++;
						b_subrows |= 0x01;
					}
					if (lu0.Any(g => g.Key.Length == (int)span))
					{
						i_subrows++;
						b_subrows |= 0x02;
					}
					if (lu1.Any(g => g.Key.Length == (int)span))
					{
						i_subrows++;
						b_subrows |= 0x04;
					}
					if (lu2.Any(g => g.Key.Length == (int)span))
					{
						i_subrows++;
						b_subrows |= 0x08;
					}
					if (lu3.Any(g => g.Key.Length == (int)span))
					{
						i_subrows++;
						b_subrows |= 0x10;
					}

					if (i_subrows > 0)
					{
						Grid g_row = new Grid();
						for (int j = 0; j < i_subrows; j++)
							g_row.RowDefinitions.Add(new RowDefinition());

						for (int j = 0; j <= i; j++)
						{
							ColumnDefinition cd = new ColumnDefinition();
							g_row.ColumnDefinitions.Add(cd);

							miew.Tokenization.Span tsp = new miew.Tokenization.Span(j, span);
							String stxt = pc.ctrl.ts_input.Source.MinimalSpanText(tsp);

							int i_subrow = 0;

							if ((b_subrows & 0x01) > 0)
							{
								Border cc = new Border();
								cc.SetValue(Grid.ColumnProperty, j);
								cc.SetValue(Grid.RowProperty, i_subrow++);
								cc.BorderBrush = Brushes.Black;
								cc.BorderThickness = new Thickness(1, 1, 0, 0);
								TextBlock tb = new TextBlock(new Run(stxt));
								tb.FontSize = 14;
								tb.Margin = new Thickness(5, 5, 5, 0);
								cc.Child = tb;
								g_row.Children.Add(cc);
							}

							if ((b_subrows & 0x02) > 0)
							{
								ChartCell cc = new ChartCell();
								if (f_spantext && i_subrow == 1)
									cc.BorderThickness = new Thickness(1, 0, 0, 0);
								cc.SetValue(Grid.ColumnProperty, j);
								cc.SetValue(Grid.RowProperty, i_subrow++);

								PassiveEdge.Completed[] rgpe = lu0[new miew.Tokenization.Span(j, span)].ToArray();
								if (rgpe.Length > 0)
								{
									cc.Padding = new Thickness(2.5);

									foreach (PassiveEdge.Completed pe in rgpe)
									{
										Tfs te = pe;
										RoundedRectangle rr = new LocalRoundedRect(Brushes.Honeydew, pe, String.Format("{0:X6}\r\n{1}", te.DisplayId, te.Type.Name));
										cc.AddItem(rr);
									}
								}
								g_row.Children.Add(cc);
							}

							if ((b_subrows & 0x04) > 0)
							{
								ChartCell cc = new ChartCell();
								if (f_spantext && i_subrow == 1)
									cc.BorderThickness = new Thickness(1, 0, 0, 0);
								cc.SetValue(Grid.ColumnProperty, j);
								cc.SetValue(Grid.RowProperty, i_subrow++);

								IParseObj[] rgpe = lu1[new miew.Tokenization.Span(j, span)].ToArray();
								if (rgpe.Length > 0)
								{
									cc.Padding = new Thickness(2.5);

									foreach (IParseObj pe in rgpe)
									{
										Tfs te = pe.Tfs;
										RoundedRectangle rr = new LocalRoundedRect(Brushes.Beige, pe, String.Format("{0:X6}\r\n{1}", te.DisplayId, te.Type.Name));
										cc.AddItem(rr);

										//Binding sss0 = new Binding();
										//sss0.Source = rr;
										//sss0.Path = new PropertyPath("Center");

										//Binding sss1 = new Binding();
										//sss1.Source = rr;
										//sss1.Path = new PropertyPath("Top");

										//Line zz = new Line();
										//zz.X1 = 0;
										//zz.Y1 = 0;
										//zz.SetBinding(Line.X2Property, sss0);
										//zz.SetBinding(Line.Y2Property, sss1);
										//zz.Stroke = Brushes.Black;
										//zz.StrokeThickness = 1;
										//this.Children.Add(zz);
									}
								}
								g_row.Children.Add(cc);
							}

							if ((b_subrows & 0x08) > 0)
							{
								ChartCell cc = new ChartCell();
								if (f_spantext && i_subrow == 1)
									cc.BorderThickness = new Thickness(1, 0, 0, 0);
								cc.SetValue(Grid.ColumnProperty, j);
								cc.SetValue(Grid.RowProperty, i_subrow++);

								LexicalAnalysis.AnalysisStack[] rgpe = lu2[new miew.Tokenization.Span(j, span)].ToArray();
								if (rgpe.Length > 0)
								{
									cc.Padding = new Thickness(2.5);

									foreach (LexicalAnalysis.AnalysisStack stk in rgpe)
									{
										RoundedRectangle rr = new LocalRoundedRect(Brushes.MistyRose, stk);

										rr.InnerChild = Anon<StackPanel>.New(sp =>
										{
											sp.Orientation = Orientation.Vertical;
											sp.Children.Add(new TextBlock(new Run(String.Format("{0:X6}", stk.Tfs.DisplayId))));

											foreach (var la in ((IList<LexicalAnalysis.LexicalTransform>)stk).Reverse())
											{
												sp.Children.Add(Anon<TextBlock>.New(x =>
												{
													x.Padding = new Thickness(1.5);
													x.Text = la.license.Name;

												}));
											}
										});
										cc.AddItem(rr);
									}
								}
								g_row.Children.Add(cc);
							}

							if ((b_subrows & 0x10) > 0)
							{
								ChartCell cc = new ChartCell();
								if (f_spantext && i_subrow == 1)
									cc.BorderThickness = new Thickness(1, 0, 0, 0);
								cc.SetValue(Grid.ColumnProperty, j);
								cc.SetValue(Grid.RowProperty, i_subrow++);

								IParseObj tok;
								if (lu3.TryGetValue(tsp, out tok))
								{
									cc.Padding = new Thickness(2.5);

									RoundedRectangle rr = new RoundedRectangle(tok.Text);
									((TextBlock)rr.InnerChild).FontSize = 14;
									rr.CornerRadius = new System.Windows.CornerRadius(2);
									rr.Background = Brushes.AntiqueWhite;
									rr.Margin = new Thickness(2.5);
									rr.Padding = new Thickness(8, 0, 8, 0);
									rr.BorderBrush = Brushes.Gray;
									((WrapPanel)((Border)cc).Child).HorizontalAlignment = System.Windows.HorizontalAlignment.Center;
									cc.AddItem(rr);
								}
								g_row.Children.Add(cc);
							}
						}
						rows_stack.Children.Add(g_row);
					}
				}
				bthis.Child = rows_stack;
			}
		};

		class LocalRoundedRect : RoundedRectangle
		{
			public LocalRoundedRect(Brush br, IParseObj pce, String txt)
				: base(txt)
			{
				Init(br, pce);
			}
			public LocalRoundedRect(Brush br, IParseObj pce)
			{
				Init(br, pce);
			}
			void Init(Brush br, IParseObj pce)
			{
				CornerRadius = new System.Windows.CornerRadius(2);
				Margin = new Thickness(2.5);
				BorderBrush = Brushes.Gray;
				VerticalAlignment = System.Windows.VerticalAlignment.Top;
				Background = br;

				ContextMenu = Anon<ContextMenu>.New(cm =>
					{
						cm.Items.Add(Anon<MenuItem>.New(mi =>
						{
							mi.Header = "TFS";
							mi.Click += (o, e) =>
							{
								MyDockSite mds = MyDockSite.g_docksite;
								var etw = new ExpandedWindow(pce.Tfs);
								mds.wksp.tmc.Items.Add(etw);
								etw.Activate();
							};
						}));
						PassiveEdge.Derived dpe = pce as PassiveEdge.Derived;
						if (dpe != null && dpe.ChartDaughters.Count > 0)
						{
							MenuItem mix = new MenuItem();
							mix.Header = "Derivation daughters";
							cm.Items.Add(mix);

							foreach (var d in dpe.ChartDaughters)
							{
								mix.Items.Add(Anon<MenuItem>.New(xmi =>
								{
									xmi.Header = String.Format("{0}", d.Tfs.SysObjName);
									xmi.Click += (o, e) =>
									{
										MyDockSite mds = MyDockSite.g_docksite;
										var etw = new ExpandedWindow(d.Tfs);
										mds.wksp.tmc.Items.Add(etw);
										etw.Activate();
									};
								}));
							}
						}
						PassiveEdge.Completed cp = pce as PassiveEdge.Completed;
						if (cp != null)
						{
							MenuItem mix = new MenuItem();
							mix.Header = "Start symbols";
							cm.Items.Add(mix);

							foreach (var ss in cp.MatchedStartSymbols)
							{
								mix.Items.Add(Anon<MenuItem>.New(xmi =>
								{
									xmi.Header = String.Format("Start symbol: {0}", ss.Name.Replace("_", "__"));
									xmi.Click += (o, e) =>
									{
										MyDockSite mds = MyDockSite.g_docksite;
										var etw = new ExpandedWindow(ss);
										mds.wksp.tmc.Items.Add(etw);
										etw.Activate();
									};
								}));
							}
						}
					});
			}
		}


		class ChartCell : Border
		{
			WrapPanel wp;

			public ChartCell()
			{
				wp = new WrapPanel();

				this.Child = wp;
				this.BorderBrush = Brushes.Black;
				this.BorderThickness = new Thickness(1, 1, 0, 0);
			}

			public void AddItem(UIElement el)
			{
				wp.Children.Add(el);
			}


			//class XWrapPanel : WrapPanel
			//{
			//    protected override Size ArrangeOverride(Size finalSize)
			//    {
			//        Size sz = base.ArrangeOverride(finalSize);

			//        foreach (var ch in InternalChildren.OfType<FrameworkElement>())
			//        {
			//            Vector v = VisualTreeHelper.GetOffset(ch);
			//            Point pt = new Point(v.X,v.Y);

			//            pt = ch.TransformToAncestor(this).Transform(pt);
			//            Debug.Print("{0} {1} {2}",ch.GetType().Name, pt.X, pt.Y);
			//        }
			//        return sz;
			//    }
			//};


		};
	};

	public class ParseTreeWindow : AgreeGrammarDocument
	{
		VizParseTree tree_layout_panel;

		public ParseTreeWindow(ParseControl ctrl, IDerivation pce)
			: base(ctrl.g)
		{
			this.Title = pce.UnpackedTfs.Name;

			tree_layout_panel = new VizParseTree(ctrl, pce);

			this.Content = tree_layout_panel;
		}

		public IDerivation ParseTree { get { return tree_layout_panel.ParseTree; } }
	};



	public class MultiParseTreeWindow : AgreeGrammarDocument
	{
		WrapPanel wp;
		ParseControl ctrl;
		public MultiParseTreeWindow(ParseControl ctrl)
			: base(ctrl.g)
		{
			this.ctrl = ctrl;
			this.Title = ctrl.SysObjName;

			wp = new WrapPanel();
			wp.Orientation = Orientation.Horizontal;
			this.Content = wp;

			foreach (IDerivation cp in ctrl.chart.AllDerivations)
			{
				wp.Children.Add(new VizParseTree(ctrl, cp));
			}

			//Loaded += new RoutedEventHandler(MultiParseTreeWindow_Loaded);
		}

		public ParseChart ParseChart { get { return ctrl.chart; } }

		void MultiParseTreeWindow_Loaded(object sender, RoutedEventArgs e)
		{
			foreach (FrameworkElement fe in wp.Children)
			{
				//	SaveToPng(fe);
			}
		}



	};

}