using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.ComponentModel;

using glue.Extensions.Enumerable;
using glue.XDag;

namespace glue.WpfUtil
{
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// 
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public partial class DagLayoutPanel : Panel
	{
		internal partial class LayoutDag : Dag<LayoutDag.Node>
		{
			[DebuggerDisplay("{ToString(),nq}")]
			internal new class Node : Dag<LayoutDag.Node>.Node
			{
				static Size infinite_size = new Size(double.PositiveInfinity, double.PositiveInfinity);

				[DebuggerBrowsable(DebuggerBrowsableState.Never)]
				Point top_handle;

				[DebuggerBrowsable(DebuggerBrowsableState.Never)]
				Point bot_handle;

				[DebuggerBrowsable(DebuggerBrowsableState.Never)]
				readonly public FrameworkElement fe;

				public int order;

				static int i_next = 0;

				public Rail.SingleNodePos railpos;

				Size _desired = infinite_size;

				public Node(LayoutDag ld, FrameworkElement fe)
					: base(ld)
				{
					this.order = i_next++;
					this.fe = fe;
				}

				public bool ChangeParents(IEnumerable<Node> iefe)
				{
					bool f_any = false;
					foreach (Node n in Parents.Except(iefe).ToArray())
					{
						base.RemoveParent(n);
						f_any = true;
					}

					foreach (Node n in iefe.Except(Parents).ToArray())
					{
						base.AddParent(n);
						f_any = true;
					}
					return f_any;
				}

				public new LayoutDag Dag { get { return (LayoutDag)base.Dag; } }

				public FrameworkElement Item { get { return fe; } }

				IEnumerable<Node> OrderedChildren
				{
					get { return Children.OrderBy(n => n.order); }
				}
				IEnumerable<Node> OrderedParents
				{
					get { return Parents.OrderBy(n => n.order); }
				}
				int GetChildIndex(Node child)
				{
					return OrderedChildren.IndexOfFirst(n => n == child);
				}
				int GetParentIndex(Node parent)
				{
					return OrderedParents.IndexOfFirst(n => n == parent);
				}

				[DebuggerBrowsable(DebuggerBrowsableState.Never)]
				public Size DesiredSize
				{
					get
					{
						if (_desired == infinite_size)
						{
							fe.Measure(infinite_size);
							_desired = fe.DesiredSize;
						}
						return _desired;
					}
				}

				public bool ReMeasure()
				{
					fe.Measure(infinite_size);
					Size sz = fe.DesiredSize;
					if (sz.Equals(_desired))
						return false;
					_desired = sz;
					return true;
				}

				[DebuggerBrowsable(DebuggerBrowsableState.Never)]
				public Rect Final
				{
					get
					{
						if (railpos == null)
							return default(Rect);

						double ypos = Dag.panel.rails.Take(Level).Sum(r => r.MaxHeight + 30);

						Rect r_final = new Rect(new Point(railpos.RailCalcs().Left.Value, ypos), DesiredSize);

						top_handle = r_final.TopLeft;
						top_handle.X += r_final.Width / 2;

						bot_handle = r_final.BottomLeft;
						bot_handle.X += r_final.Width / 2;

						return r_final;
					}
				}

				public void DoRender(DrawingContext dc, Pen pen, Pen dashpen)
				{
					foreach (var cn in Children)
					{
						if (!cn.top_handle.Equals(default(Point)))
							dc.DrawLine(pen, bot_handle, cn.top_handle);
					}
				}

				public override string ToString()
				{
					double d1 = railpos == null ? 0 : railpos.RailCalcs().Left.Value;
					double d2 = railpos == null ? 0 : railpos.RailCalcs().Right.Value;
					return String.Format("{0:N2}-{1:N2}  {2}  parents: {3}/{4}  children: {5}/{6}  {7}",
						d1,
						d2,
						base.ToString(),
						Parents.Count,
						AllAncestors.Count,
						Children.Count,
						AllDescendants.Count,
						fe.Name);
				}

				public IEnumerable<FrameworkElement> FrameworkElementParents
				{
					get
					{
						IList rgp = GetDagParents(this.Item);
						return rgp == null ? Enumerable.Empty<FrameworkElement>() : rgp.OfType<FrameworkElement>();
					}
					set
					{
						SetDagParents(this.Item, value.ToArray());
					}
				}
			};
		};
	};
}