using System;
using System.Globalization;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Media;
using System.Windows.Shapes;

using miew.Enumerable;
using miew.Dag;

namespace agree.Wpf.Util
{
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// 
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public partial class DagLayoutPanel : Panel
	{
		//public enum LayoutDirection
		//{
		//    TopToBottom = 0,
		//    BottomToTop = 1,
		//    LeftToRight = 2,
		//    RightToLeft = 3,
		//}


		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// Shape.Stroke - dependency property is adopted by the DagPanel
		/// Shape.StrokeThickness - dependency property is adopted by the DagPanel
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

		static DagLayoutPanel()
		{
			Shape.StrokeProperty.AddOwner(typeof(DagLayoutPanel),
					new FrameworkPropertyMetadata(
						Brushes.Black,
						0,
						(e, o) =>
						{
							DagLayoutPanel p = (DagLayoutPanel)e;
							p.InvalidateVisual();
						},
						null,
						false));

			Shape.StrokeThicknessProperty.AddOwner(typeof(DagLayoutPanel),
					new FrameworkPropertyMetadata(
						1.0,
						0,
						(e, o) =>
						{
							DagLayoutPanel p = (DagLayoutPanel)e;
							p.InvalidateVisual();
						},
						null,
						false));
		}

		public Brush Stroke
		{
			get { return (Brush)GetValue(Shape.StrokeProperty); }
			set { SetValue(Shape.StrokeProperty, value); }
		}

		public Double StrokeThickness
		{
			get { return (Double)GetValue(Shape.StrokeThicknessProperty); }
			set { SetValue(Shape.StrokeThicknessProperty, value); }
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// DagParents attached property - establish the Dag topology by setting an IList of parents (or none) for each 
		/// Dag node (a FrameworkElement)
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

		static FrameworkElement[] rg_fe_empty = new FrameworkElement[0];

		public static readonly DependencyProperty DagParentsProperty =
			DependencyProperty.RegisterAttached("DagParents", typeof(IList), typeof(DagLayoutPanel),
			new FrameworkPropertyMetadata(
					default(IList),
					FrameworkPropertyMetadataOptions.AffectsParentMeasure |
					FrameworkPropertyMetadataOptions.AffectsParentArrange,
					(d, args) =>
					{
						FrameworkElement fe = d as FrameworkElement;
						if (fe != null)
						{
							DagLayoutPanel dlp = fe.Parent as DagLayoutPanel;
							IList new_list = args.NewValue as IList;
							IList old_list = args.NewValue as IList;
							if (dlp != null && new_list != null)
								dlp.ChangeNodeParents(fe, old_list, new_list);
						}
					},
					(d, o) =>
					{
						IList rg = o as IList;
						FrameworkElement fe = d as FrameworkElement;
						if (fe == null || rg == null)
							return rg_fe_empty;
						DagLayoutPanel dlp = fe.Parent as DagLayoutPanel;
						if (dlp == null)
							return rg_fe_empty;

						/// prevent cycles in the dag.
						LayoutDag.Node cur_node = dlp._dag[fe];
						return rg
							.OfType<FrameworkElement>()
							.Where(par => par != fe && !cur_node.AllDescendants.Contains(dlp._dag[par]))
							.ToArray();
					},
					true));

		public static IList GetDagParents(FrameworkElement e)
		{
			return e.GetValue(DagParentsProperty) as IList ?? rg_fe_empty;
		}

		public static void SetDagParents(FrameworkElement e, IList par)
		{
			e.SetValue(DagParentsProperty, par);
		}

		static IEnumerable<FrameworkElement> VisibleDagParents(FrameworkElement fe)
		{
			HashSet<FrameworkElement> hs = new HashSet<FrameworkElement>();
			GetVisibleDagParents(hs, fe);
			return hs;
		}

		static void GetVisibleDagParents(HashSet<FrameworkElement> found, FrameworkElement fe)
		{
			IList parents = fe.GetValue(DagParentsProperty) as IList;
			if (parents == null)
				return;
			foreach (FrameworkElement parent in parents.OfType<FrameworkElement>())
			{
				if (parent.Visibility == Visibility.Hidden)
					GetVisibleDagParents(found, parent);
				else
					found.Add(parent);
			}
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// DagLevel - read-only, attached dependency property indicates the computed level of the node
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

		static readonly DependencyPropertyKey DagLevelPropertyKey =
			DependencyProperty.RegisterAttachedReadOnly(
				"DagLevel",
				typeof(int),
				typeof(DagLayoutPanel),
				new FrameworkPropertyMetadata(0));

		public static readonly DependencyProperty DagLevelProperty = DagLevelPropertyKey.DependencyProperty;
	};

}