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

using glue.HotValue;
using glue.Extensions.Array;
using glue.Extensions.Enumerable;

namespace glue.WpfUtil
{
	public partial class DagLayoutPanel : Panel
	{
		internal partial class Rail
		{
			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// 
			/// </summary>
			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			internal abstract class PosBase
			{
				abstract public RailCalcSet RailCalcs(int iRail = -1);
			};


			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// 
			/// </summary>
			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			internal class SingleRailPos : PosBase
			{
				public Rail rail;
				protected RailCalcSet rcs;

				public SingleRailPos(Rail rail, IHvDouble bv_width, IHvDouble bv_height)
				{
					this.rail = rail;
					this.rcs = new RailCalcSet(rail, this, bv_width, bv_height);
				}

				public PosBase PosPrevious
				{
					get
					{
						int ix = ItemIndex;
						return ix == -1 ? null : rail.GetItem(ItemIndex - 1);
					}
				}

				public PosBase PosNext
				{
					get
					{
						int ix = ItemIndex;
						return ix == -1 ? null : rail.GetItem(ItemIndex + 1);
					}
				}

				public int ItemIndex { get { return rail.FindIndex(this); } }

				public void Remove(int ix)
				{
					if (ix == -1 && (ix = ItemIndex) == -1)
						throw new Exception();
					Debug.Assert(ix == ItemIndex);

					SingleRailPos pAfter = (SingleRailPos)rail.GetItem(ix + 1);

					rail.rg.RemoveAt(ix);
					this.rail = null;

					if (pAfter != null)
						pAfter.rcs.RailBase = this.rcs.RailBase;
				}

				public void Insert(Rail r, int ix)
				{
					this.rail = r;
					this.rcs.RailBase = rail.GetRailStart(ix);

					SingleRailPos pAfter = (SingleRailPos)rail.GetItem(ix);

					rail.rg.Insert(ix, this);

					if (pAfter != null)
						pAfter.rcs.RailBase = this.rcs.RightPadded;
				}

				public void MoveTo(Rail r, ref int new_ix)
				{
					int old_ix = ItemIndex;
					if (old_ix == new_ix)
						return;

					Remove(old_ix);
					if (old_ix < new_ix)
						new_ix--;
					Insert(r, new_ix);
				}

				public override RailCalcSet RailCalcs(int iRail = -1)
				{
					Debug.Assert(iRail == -1 || iRail == rail.RailIndex);
					return rcs;
				}
			}

			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// 
			/// </summary>
			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			[DebuggerDisplay("{ToString(),nq}")]
			internal class SingleNodePos : SingleRailPos
			{
				readonly public LayoutDag.Node node;

				public SingleNodePos(Rail rail, IHvDouble width, IHvDouble height, int iPos, LayoutDag.Node node)
					: base(rail, width, height)
				{
					this.node = node;
					Insert(rail, iPos);

					rail.RailSet.RailsLoaded += (o, e) =>
						{
							SetParentProposal();
							SetChildProposal();

							node.PropertyChanged += (o1, e1) =>
							{
								if (e1.PropertyName == "Level")
								{
									if (node.Level != rail.RailIndex)
									{
										RailSet rail_set = rail.rs;
										Remove(ItemIndex);
										rail_set.SetRailCount(node.Dag.MaxLevel + 1);
										Rail new_rail = rail_set[node.Level];
										Insert(new_rail, new_rail.Count);
									}
								}
								else if (e1.PropertyName == "Child.Item")
								{
									SetChildProposal();
								}
								else if (e1.PropertyName == "Parent.Item")
								{
									SetParentProposal();
								}
							};
						};
				}

				void SetParentProposal()
				{
					//Rail.RailCalcSet[] rgrc;
					//rgrc = node.PreviousLevelParents.Select(n => n.railpos.RailCalcs(n.Level)).ToArray();
					var calcs_by_level = node.Parents
						.GroupBy(x => x.Level)
						.Select(g => g.Select(y => y.railpos.RailCalcs(g.Key)).ToArray())
						.ToArray();

					//Debug.WriteLine(this.rcs.MyParentCenter);
					if (calcs_by_level.Length == 0)
						goto clear_proposal;

					if (calcs_by_level.Length == 1)
					{
						var rgrc = calcs_by_level[0];
						if (rgrc.Length == 1)
							goto clear_proposal;

						this.rcs.MyParentCenter =
							new HvdAddHalfSpan(rgrc[0].SelfBase, rgrc[0].Left, rgrc[rgrc.Length - 1].Right);

						//new HvdAddHalfSpan(rgrc[0].SelfBase, rgrc[0].SelfBase, rgrc[rgrc.Length - 1].Right);
						//  new HvdHalfSpan(rgrc[0].SelfBase, rgrc[rgrc.Length - 1].Right);
					}
					else
					{
						var all_calcs = calcs_by_level.SelectMany(g => g).ToArray();
						int i_min = all_calcs.IndexOfMin(x => x.Left.Value);
						int i_max = all_calcs.IndexOfMax(x => x.Right.Value);
						this.rcs.MyParentCenter =
							new HvdAddHalfSpan(all_calcs[i_min].SelfBase, all_calcs[i_min].SelfBase, all_calcs[i_max].Right);
						//new HvdAddHalfSpan(rgrc[0].SelfBase, rgrc[0].SelfBase, rgrc[rgrc.Length - 1].Right);
						//  new HvdHalfSpan(rgrc[0].SelfBase, rgrc[rgrc.Length - 1].Right);
					}
					return;

				clear_proposal:
					this.rcs.MyParentCenter = HvdConst.NaN;
				}

				void SetChildProposal()
				{
					//Rail.RailCalcSet[] rgrc;
					//rgrc = node.PreviousLevelParents.Select(n => n.railpos.RailCalcs(n.Level)).ToArray();
					var calcs_by_level = node.Children
						.GroupBy(x => x.Level)
						.Select(g => g.Select(y => y.railpos.RailCalcs(g.Key)).ToArray())
						.ToArray();

					//Debug.WriteLine(this.rcs.MyParentCenter);
					if (calcs_by_level.Length == 0)
						goto clear_proposal;

					if (calcs_by_level.Length == 1)
					{
						var rgrc = calcs_by_level[0];
						if (rgrc.Length == 1)
						{
							this.rcs.MyChildCenter = rgrc[0].Center;
							return;
						}

						this.rcs.MyChildCenter =
							new HvdAddHalfSpan(rgrc[0].SelfBase, rgrc[0].Left, rgrc[rgrc.Length - 1].Right);

						//new HvdAddHalfSpan(rgrc[0].SelfBase, rgrc[0].SelfBase, rgrc[rgrc.Length - 1].Right);
						//  new HvdHalfSpan(rgrc[0].SelfBase, rgrc[rgrc.Length - 1].Right);
					}
					else
					{
						var all_calcs = calcs_by_level.SelectMany(g => g).ToArray();
						int i_min = all_calcs.IndexOfMin(x => x.Left.Value);
						int i_max = all_calcs.IndexOfMax(x => x.Right.Value);
						this.rcs.MyChildCenter =
							new HvdAddHalfSpan(all_calcs[i_min].SelfBase, all_calcs[i_min].SelfBase, all_calcs[i_max].Right);
						//new HvdAddHalfSpan(rgrc[0].SelfBase, rgrc[0].SelfBase, rgrc[rgrc.Length - 1].Right);
						//  new HvdHalfSpan(rgrc[0].SelfBase, rgrc[rgrc.Length - 1].Right);
					}


					return;





					//Rail.RailCalcSet[] rgrc;
				////rgrc = node.NextLevelChildren.Select(n => n.railpos.RailCalcs(n.Level)).ToArray();
				//rgrc = node.AllDescendants.Select(n => n.railpos.RailCalcs(n.Level)).ToArray();
				//Debug.WriteLine(this.rcs.MyChildCenter);
				//if (rgrc.Length > 1)
				//{
				//    int i_min = rgrc.IndexOfMin(x => x.Left.Value);
				//    int i_max = rgrc.IndexOfMax(x => x.Right.Value);
				//    this.rcs.MyChildCenter = 
				//        new HvdAddHalfSpan(rgrc[i_min].SelfBase, rgrc[i_min].Left, rgrc[i_max].Right);
				//        //	new HvdAddHalfSpan(rgrc[0].SelfBase, rgrc[0].SelfBase, rgrc[rgrc.Length - 1].Right);
				//     //   new HvdHalfSpan(rgrc[0].SelfBase, rgrc[rgrc.Length - 1].Right);
				//}

				clear_proposal:
					this.rcs.MyChildCenter = HvdConst.NaN;

				}

				public override string ToString()
				{
					return String.Format(@"
{0}
Level: {1}
{2}
",
 node.Item.Name,
 node.Level,
 rcs.ToString());
				}


			};

			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// 
			/// </summary>
			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			public class MultiRailPos : PosBase
			{
				MultiRailPos()
				{
				}

				public override RailCalcSet RailCalcs(int iRail = -1)
				{
					throw new NotImplementedException();
				}
			};
		};
	};
}