using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Interop;
using System.Diagnostics;
using System.Globalization;
using System.Windows;
using System.Windows.Documents;
using System.Windows.Controls;
using System.Linq;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.Windows.Threading;
using System.Threading;


namespace agree.Wpf.Util
{
	public static class Util
	{
		public static Size DrawText(this DrawingContext dc, String s, double x, double y, Typeface f, double emSize, Brush b)
		{
			FormattedText ft = new FormattedText(s, CultureInfo.CurrentCulture, FlowDirection.LeftToRight, f, emSize, b);
			dc.DrawText(ft, new Point(x, y));
			return new Size(ft.Width, ft.Height);
		}
		public static Size DrawText(this DrawingContext dc, String s, Point pt, Typeface f, double emSize, Brush b)
		{
			FormattedText ft = new FormattedText(s, CultureInfo.CurrentCulture, FlowDirection.LeftToRight, f, emSize, b);
			dc.DrawText(ft, pt);
			return new Size(ft.Width, ft.Height);
		}
		public static void DrawText(this DrawingContext dc, FormattedText ft, double x, double y)
		{
			dc.DrawText(ft, new Point(x, y));
		}
		public static FormattedText FormattedText(this String s, Typeface f, double emSize, Brush b = null)
		{
			return new FormattedText(s, CultureInfo.CurrentCulture, FlowDirection.LeftToRight, f, emSize, b ?? Brushes.Black);
		}

		static Size infinite_size = new Size(double.PositiveInfinity, double.PositiveInfinity);
		public static void SaveToPng(FrameworkElement fe, System.IO.Stream stream)
		{
			using (var ps = new HwndSource(new HwndSourceParameters()))
			{
				ps.RootVisual = VisualTreeHelper.GetParent(fe) == null ? fe : null;

				// Flush the dispatcher queue   
				//var disp = Dispatcher.CurrentDispatcher;
				//if (disp == null)
				//    throw new Exception();

				//disp.Invoke(DispatcherPriority.SystemIdle, new Action(() => { }));

				fe.Measure(infinite_size);
				Size sz = fe.DesiredSize;

				DrawingVisual dv = new DrawingVisual();
				using (DrawingContext ctx = dv.RenderOpen())
				{
					VisualBrush br = new VisualBrush(fe);
					br.AutoLayoutContent = true;
					ctx.DrawRectangle(br, null, new Rect(0, 0, sz.Width, sz.Height));
				}

				RenderTargetBitmap bitmap = new RenderTargetBitmap((int)sz.Width+2, (int)sz.Height+2, 96, 96, PixelFormats.Pbgra32);
				bitmap.Render(dv);

				//disp.Invoke(DispatcherPriority.SystemIdle, new Action(() => { }));
				PngBitmapEncoder encoder = new PngBitmapEncoder();
				encoder.Frames.Add(BitmapFrame.Create(bitmap));
				encoder.Save(stream);
			}
		}
	};

	/// <summary>
	/// http://thejoyofcode.com/Generating_images_using_WPF_on_the_Server.aspx
	/// </summary>
	public class BackgroundStaDispatcher
	{
		private Dispatcher _dispatcher;
		static int id = 0;

		public BackgroundStaDispatcher(Action<Exception> exception_handler)
		{
			AutoResetEvent are = new AutoResetEvent(false);

			Thread thread = new Thread(() =>
			{
				_dispatcher = Dispatcher.CurrentDispatcher;
				_dispatcher.UnhandledException += (o, e) =>
				{
					exception_handler(e.Exception);
					if (!Debugger.IsAttached)
					{
						e.Handled = true;
					}
				};
				are.Set();
				Dispatcher.Run();
			});

			thread.Name = string.Format("BackgroundStaDispatcher({0})", (id++).ToString());
			thread.SetApartmentState(ApartmentState.STA);
			thread.IsBackground = true;
			thread.Start();

			are.WaitOne();
		}

		public void Invoke(Action action)
		{
			_dispatcher.Invoke(action);
		}
	}
}

namespace agree.Wpf.Util
{
	public class RoundedRectangle : Border, INotifyPropertyChanged
	{
		Rectangle r;
		Border mask;
		Grid g2;

		public RoundedRectangle()
			: this(default(UIElement))
		{
		}

		public RoundedRectangle(String txt, TextWrapping wrap = TextWrapping.NoWrap)
			: this(new TextBlock())
		{
			this.Text = txt;
		}

		public RoundedRectangle(UIElement el)
		{
			r = new Rectangle();

			mask = new Border();
			mask.Background = Brushes.White;

			g2 = new Grid();
			g2.OpacityMask = new VisualBrush(mask);
			g2.Children.Add(r);
			if (el != null)
				g2.Children.Add(el);

			Grid g1 = new Grid();
			g1.Children.Add(mask);
			g1.Children.Add(g2);

			base.BorderThickness = new Thickness(1);
			base.BorderBrush = Brushes.Black;
			base.CornerRadius = new CornerRadius(6);
			base.Child = g1;
		}

		public String Text
		{
			set
			{
				TextBlock tb = new TextBlock();
				InnerChild = tb;
				tb.Text = value;
				tb.Foreground = Brushes.Black;
				tb.Padding = new Thickness(1.5);
				tb.TextWrapping = TextWrapping.NoWrap;
			}
		}

		public UIElement InnerChild
		{
			get
			{
				return g2.Children.Count > 1 ? g2.Children[1] : null;
			}
			set
			{
				while (g2.Children.Count >= 2)
					g2.Children.RemoveAt(1);
				g2.Children.Add(value);
			}
		}

		protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
		{
			base.OnPropertyChanged(e);
			if (e.Property == Border.CornerRadiusProperty)
				mask.CornerRadius = this.CornerRadius;
			else if (e.Property == Border.BackgroundProperty)
				r.Fill = this.Background;
		}

		Point pt_top_center;
		Point pt_bot_center;

		public Point TopCenter { get { return pt_top_center; } }

		public Point BottomCenter { get { return pt_bot_center; } }

		public Double Top { get { return pt_top_center.Y; } }

		public Double Bottom { get { return pt_bot_center.Y; } }

		public Double Center { get { return pt_top_center.X; } }

		public static readonly DependencyProperty ContainerParentProperty =
			DependencyProperty.RegisterAttached("ContainerParent", typeof(Boolean), typeof(RoundedRectangle),
			new FrameworkPropertyMetadata(
				false,
					0,
					null,
					null,
					true));

		public static Boolean GetTreeParent(FrameworkElement e)
		{
			return (Boolean)e.GetValue(ContainerParentProperty);
		}
		public static void SetTreeParent(FrameworkElement e, Boolean v)
		{
			e.SetValue(ContainerParentProperty, v);
		}

		FrameworkElement FindContainerParent()
		{
			FrameworkElement p = this;
			while ((p = p.Parent as FrameworkElement) != null)
			{
				if ((Boolean)p.GetValue(ContainerParentProperty))
					return p;
			}
			return null;
		}

		protected override Size ArrangeOverride(Size finalSize)
		{
			Size sz = base.ArrangeOverride(finalSize);
			FrameworkElement p = FindContainerParent();
			if (p != null)
			{
				Point pt;
				pt = TranslatePoint(new Point(sz.Width / 2, 0), p);
				if (!pt.Equals(pt_top_center))
				{
					pt_top_center = pt;
					NotifyPropertyChanged("TopCenter");
					NotifyPropertyChanged("Top");
					NotifyPropertyChanged("Center");
				}
				pt = TranslatePoint(new Point(sz.Width / 2, sz.Height), p);
				if (!pt.Equals(pt_bot_center))
				{
					pt_bot_center = pt;
					NotifyPropertyChanged("BottomCenter");
					NotifyPropertyChanged("Bottom");
				}
			}
			return sz;
		}

		void NotifyPropertyChanged(String s_field)
		{
			var h = PropertyChanged;
			if (h != null)
				h(this, new PropertyChangedEventArgs(s_field));
		}

		public event PropertyChangedEventHandler PropertyChanged;
	};

	public class VizParseTree : StackPanel
	{
		IDerivation pce;
		TreeLayoutPanel tlp;

		public VizParseTree(ParseControl ctrl, IDerivation pce)
		{
			this.pce = pce;

			this.Orientation = System.Windows.Controls.Orientation.Vertical;

			tlp = new TreeLayoutPanel();
			tlp.VerticalBuffer = 15;
			tlp.HorizontalBuffer = 35;
			tlp.HorizontalBufferSubtree = 35;
			tlp.Margin = new Thickness(10);
			tlp.Stroke = Brushes.Gray;

			//tree_layout_panel.SetValue(TextBlock.FontSizeProperty, 18.0);
			//tree_layout_panel.SetValue(TextBlock.FontFamilyProperty, new FontFamily("Arial"));

			CreateTreeNode(ctrl.g.nl.GetParseTree(ctrl.chart, pce));

			this.Children.Add(tlp);
			var tb = new TextBlock(new Run(pce.UnpackedTfs.Name));
			tb.HorizontalAlignment = System.Windows.HorizontalAlignment.Center;
			this.Children.Add(tb);
		}

		public IDerivation ParseTree { get { return pce; } }

		FrameworkElement CreateTreeNode(GrammarNodeLabeler.ParseTree pt)
		{
			FrameworkElement ltn = pt.Children.Length == 0 ? (FrameworkElement)
										new TextBelowLabelTreeNode(pt) :
										new LabelTreeNode(pt.Label);

			tlp.Children.Add(ltn);

			foreach (var fe in pt.Children.Select(c => CreateTreeNode(c)))
			{
				fe.SetValue(TreeLayoutPanel.TreeParentProperty, ltn);
			}
			return ltn;
		}

		class LabelTreeNode : RoundedRectangle
		{
			public LabelTreeNode(String text)
				: base(text)
			{
				CornerRadius = new CornerRadius(1.5);
				Background = Brushes.Honeydew;
				HorizontalAlignment = System.Windows.HorizontalAlignment.Center;
				((TextBlock)InnerChild).Padding = new Thickness(5, 0, 5, 0);
			}
		};

		class TextBelowLabelTreeNode : StackPanel
		{
			public TextBelowLabelTreeNode(GrammarNodeLabeler.ParseTree pt)
			{
				this.Orientation = System.Windows.Controls.Orientation.Vertical;
				this.SnapsToDevicePixels = true;

				var ltn = new LabelTreeNode(pt.Label);
				ltn.HorizontalAlignment = System.Windows.HorizontalAlignment.Center;
				this.Children.Add(ltn);

				Run r = new Run(pt.SourceText);
				r.FontFamily = new FontFamily("Tekton Pro");
				r.FontSize = 21.0;
				r.FontWeight = FontWeight.FromOpenTypeWeight(100);

				var tb = new TextBlock();
				tb.Margin = new Thickness(0, 4, 0, 0);
				tb.Inlines.Add(r);
				tb.HorizontalAlignment = System.Windows.HorizontalAlignment.Center;
				this.Children.Add(tb);
			}
		};
	};
}