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 miew.Enumerable;
using miew.Dag;

namespace agree.Wpf.Util
{
	public partial class DagLayoutPanel : Panel
	{
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		internal partial class LayoutDag : Dag<LayoutDag.Node>
		{
			readonly DagLayoutPanel panel;
			readonly Dictionary<FrameworkElement, Node> layouts = new Dictionary<FrameworkElement, Node>();

			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// constructor
			/// </summary>
			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			public LayoutDag(DagLayoutPanel panel)
			{
				this.panel = panel;
			}

			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// map the user specified child nodes, which are FrameworkElements of any type, to the internal node
			/// </summary>
			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			public Node this[FrameworkElement fe]
			{
				get { return layouts[fe]; }
			}

			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			///
			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			public bool Contains(FrameworkElement fe)
			{
				return layouts.ContainsKey(fe);
			}

			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			///
			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			public bool AddChild(FrameworkElement fe)
			{
				if (fe.Visibility != Visibility.Visible)
					return false;

				Node node;
				if (!layouts.TryGetValue(fe, out node))
				{
					layouts.Add(fe, node = new Node(this, fe));
					node.PropertyChanged += level_repeater;
				}

				foreach (FrameworkElement fe_par in VisibleDagParents(fe))
				{
					Node n_par = layouts[fe_par];
					node.AddParent(n_par);
				}

				/// Attach a hook on node visibility.
				fe.IsVisibleChanged += visibility_hook;
				return true;
			}

			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			///
			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			public void RemoveChild(FrameworkElement fe)
			{
				Node n;
				if (!layouts.TryGetValue(fe, out n))
					throw new Exception();

				fe.IsVisibleChanged -= visibility_hook;
				base.Remove(n);

				n.PropertyChanged -= level_repeater;
				layouts.Remove(fe);
			}

			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			///
			/// static hook functions conserve closures
			///
			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			static PropertyChangedEventHandler level_repeater = (sender, e) =>
			{
				if (e.PropertyName == "Level")
				{
					Node n = sender as Node;
					n.Item.SetValue(DagLevelPropertyKey, n.Level);
				}
			};

			static DependencyPropertyChangedEventHandler visibility_hook = (sender, args) =>
			{
				FrameworkElement fe = sender as FrameworkElement;
				if (fe != null)
				{
					DagLayoutPanel dlp = fe.Parent as DagLayoutPanel;
					dlp._dag.ItemVisibilityChanged(fe);
				}
			};

			void ItemVisibilityChanged(FrameworkElement fe)
			{
				if (fe.Visibility == Visibility.Hidden)
					this.RemoveChild(fe);
				else
					this.AddChild(fe);
			}

			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			///
			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			public bool ChangeNodeParents(FrameworkElement fe, IEnumerable<FrameworkElement> iefe)
			{
				Node n;
				if (!layouts.TryGetValue(fe, out n))
					throw new Exception();

				return n.ChangeParents(iefe.Select(fe_par => layouts[fe_par]));
			}

			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			///
			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			public bool ReMeasureChild(FrameworkElement fe)
			{
				Node n;
				if (!layouts.TryGetValue(fe, out n))
					throw new Exception();
				return n.ReMeasure();
			}
		};
	};
}