using System;
using System.Drawing;
using System.Collections;
namespace DevComponents.Tree.Layout
{
	/// 
	/// Represents the class that performs node map layout.
	/// 
	internal class NodeMapLayout:NodeLayout
	{
		#region Private Variables
		private eMapFlow m_MapFlow=eMapFlow.Spread;
		#endregion
		public NodeMapLayout(TreeGX treeControl, Rectangle clientArea):base(treeControl,clientArea)
		{
		}
		public override void PerformLayout()
		{
			this.PrepareStyles();
			// Get default Columns
			//ArrayList defaultColInfoList=this.GetDefaultColumnInfo();
			if(m_Tree.Nodes.Count==0)
				return;
			Graphics graphics=this.GetGraphics();
			try
			{
				// Loop through each top-level node
				Node[] topLevelNodes=this.GetTopLevelNodes();
				NodeLayoutContextInfo layoutInfo=this.GetDefaultNodeLayoutContextInfo(graphics);
				foreach(Node childNode in topLevelNodes)
				{
					if(!childNode.Visible)
						continue;
					layoutInfo.ContextNode=childNode;
					ProcessRootNode(layoutInfo);
					break;
				}
				EmptyBoundsUnusedNodes(topLevelNodes);
			}
			finally
			{
				if(this.DisposeGraphics)
					graphics.Dispose();
			}
		}
		private void ProcessRootNode(NodeLayoutContextInfo layoutInfo)
		{
			Node node=layoutInfo.ContextNode;
			// Center root node on the screen
			//if(node.SizeChanged)
			//{
				// Calculate size of the node itself...
				LayoutNode(layoutInfo);
				// Calculate size and location of node column header if any
				//				if(node.NodesColumnHeaderVisible)
				//				{
				//					LayoutColumnHeader(layoutInfo);
				//				}
			//}
			
			
			//			if(node.Expanded && node.NodesColumnHeaderVisible && node.NodesColumns.Count>0)
			//				layoutInfo.Top+=node.ColumnHeaderHeight;
			NodesMapPosition positions=NodesMapPosition.Empty;
			bool bNear=false;
			if(node.Expanded)
			{
				if(node.NodesColumns.Count>0)
					layoutInfo.ChildColumns=GetNodeColumnInfo(node);
				foreach(Node childNode in node.Nodes)
				{
					if(!childNode.Visible)
						continue;
					layoutInfo.ContextNode=childNode;
					
					if(bNear || childNode.MapSubRootPosition==eMapPosition.Near)
					{
						layoutInfo.MapPositionNear=true;
						ProcessSubNode(layoutInfo);
						positions.Near.Add(childNode);
						positions.NearHeight+=GetTotalChildNodeHeight(childNode);
						bNear=true;
					}
					else if(childNode.MapSubRootPosition==eMapPosition.Far)
					{
						layoutInfo.MapPositionNear=false;
						ProcessSubNode(layoutInfo);
						positions.Far.Add(childNode);
						positions.FarHeight+=GetTotalChildNodeHeight(childNode);
					}
					else
						positions.Default.Add(childNode);
				}
				// Assign nodes to appropriate sides....
				if(positions.Default.Count>0)
				{
					int toFar=0;
					if(positions.Near.Count==0 && positions.Far.Count==0 && positions.Default.Count<=4)
						toFar=2;
					else
						toFar=(int)Math.Ceiling((double)((positions.Near.Count-positions.Far.Count+positions.Default.Count))/2);
					for(int i=0;i0)
					layoutInfo.ChildColumns=GetNodeColumnInfo(node);
				Rectangle childNodesBounds=Rectangle.Empty;
				foreach(Node childNode in node.Nodes)
				{
					if(!childNode.Visible)
						continue;
					layoutInfo.ContextNode=childNode;
					layoutInfo.ChildColumns=childColumns;
					ProcessSubNode(layoutInfo);
					childNodesBounds.Height+=(Math.Max(childNode.BoundsRelative.Height,childNode.ChildNodesBounds.Height)+this.NodeVerticalSpacing);
					childNodesBounds.Width=Math.Max(childNodesBounds.Width,childNode.BoundsRelative.Width+(childNode.ChildNodesBounds.Width>0?this.NodeHorizontalSpacing+childNode.ChildNodesBounds.Width:0));
				}
				
				layoutInfo.ChildColumns=parentColumns;
				if(childNodesBounds.Height>0)
					childNodesBounds.Height-=this.NodeVerticalSpacing;
				node.ChildNodesBounds=childNodesBounds;
				layoutInfo.ChildColumns=null;
				layoutInfo.ContextNode=node;
			}
			else node.ChildNodesBounds=Rectangle.Empty;
		}
		private void PositionCenterRootSubNodes(Node root, NodesMapPosition positions)
		{
			Rectangle area=Rectangle.Empty;
			area=Rectangle.Union(area,root.BoundsRelative);
			
			// Far Nodes Layout
			//int y=0, x=0;
			int areaHeight=positions.FarHeight;
			int minAreaHeight=this.GetMinimumAreaHeight(positions.Far,root.BoundsRelative.Height);
			if(areaHeightnode.BoundsRelative.Height && node.Expanded && node.AnyVisibleNodes)
					y+=(node.ChildNodesBounds.Height-node.BoundsRelative.Height)/2;
				if(root.BoundsRelative.IntersectsWith(new Rectangle(root.BoundsRelative.X,y,node.BoundsRelative.Width,node.BoundsRelative.Height)))
				{
					if(twoNodesLayout && index==1)
					{
						y = root.BoundsRelative.Bottom + this.NodeVerticalSpacing/2;
						if(node.ChildNodesBounds.Height>node.BoundsRelative.Height && node.Expanded && node.AnyVisibleNodes)
							y+=(node.ChildNodesBounds.Height-node.BoundsRelative.Height)/2;
					}
					else
					{
						// Change X position since Y is already accounted for...
						x+=(root.BoundsRelative.Right-x+this.NodeHorizontalSpacing);
					}
				}
				x+=node.Offset;
				OffsetNodeLocation(node,x,y);
				if(node.Expanded && node.AnyVisibleNodes)
				{
					PositionSubNodes(node,true);
					usedArea=Rectangle.Union(usedArea,node.ChildNodesBounds);
				}
				else
					usedArea=Rectangle.Union(usedArea,node.BoundsRelative);
				if(node.Expanded && node.AnyVisibleNodes)
				{
					top+=(Math.Max(node.ChildNodesBounds.Height,node.BoundsRelative.Height)+this.NodeVerticalSpacing);
				}
				else
				{
					top+=(node.BoundsRelative.Height+this.NodeVerticalSpacing);
				}
			}
		}
		private void PositionNearSubRootNodes(Node root, ArrayList nearNodes, int areaRightMost, int areaTop, int areaHeight, ref Rectangle usedArea)
		{
			int bottom=areaTop+(areaHeight+this.NodeVerticalSpacing*(nearNodes.Count-1));
			bool twoNodesLayout = (nearNodes.Count == 2);
			
			for(int index=0;indexnode.BoundsRelative.Height && node.Expanded && node.AnyVisibleNodes)
					y=bottom-((node.ChildNodesBounds.Height-node.BoundsRelative.Height)/2+node.BoundsRelative.Height);
				else
					y=bottom-node.BoundsRelative.Height;
					
//				Rectangle rNodeArea=new Rectangle(x,y,node.Bounds.Width,node.Bounds.Height);
//				rNodeArea.Inflate(this.NodeHorizontalSpacing,this.NodeVerticalSpacing);
				//if(root.Bounds.IntersectsWith(rNodeArea))
				if(root.BoundsRelative.IntersectsWith(new Rectangle(root.BoundsRelative.X,y,node.BoundsRelative.Width,node.BoundsRelative.Height)))
				{
					if(twoNodesLayout && index==1)
					{
						y = root.BoundsRelative.Top - this.NodeVerticalSpacing/2;
						if(node.ChildNodesBounds.Height>node.BoundsRelative.Height && node.Expanded && node.AnyVisibleNodes)
							y-=((node.ChildNodesBounds.Height-node.BoundsRelative.Height)/2+node.BoundsRelative.Height);
						else
							y-=node.BoundsRelative.Height;
					}
					else
					{
						// Change X position since Y is already accounted for...
						x-=(x+node.BoundsRelative.Width-root.BoundsRelative.X+this.NodeHorizontalSpacing);
					}
				}
				x-=node.Offset;
				OffsetNodeLocation(node,x,y);
				if(node.Expanded && node.AnyVisibleNodes)
				{
					PositionSubNodes(node,false);
                    usedArea = Rectangle.Union(usedArea, node.BoundsRelative);
					usedArea=Rectangle.Union(usedArea,node.ChildNodesBounds);
				}
				else
					usedArea=Rectangle.Union(usedArea,node.BoundsRelative);
				
				if(node.Expanded && node.AnyVisibleNodes)
				{
					bottom-=(Math.Max(node.ChildNodesBounds.Height,node.BoundsRelative.Height)+this.NodeVerticalSpacing);
				}
				else
				{
					bottom-=(node.BoundsRelative.Height+this.NodeVerticalSpacing);
				}
			}
		}
		private void PositionSubNodes(Node parentNode,bool bFar)
		{
			if(parentNode.Nodes.Count==0)
				return;
			int y=parentNode.BoundsRelative.Y-(parentNode.ChildNodesBounds.Height-parentNode.BoundsRelative.Height)/2;
			int x=parentNode.BoundsRelative.Right+this.SubNodeHorizontalSpacing;
			if(!bFar)
			{
				x=parentNode.BoundsRelative.X-this.SubNodeHorizontalSpacing;
			}
			Rectangle rChildBounds=Rectangle.Empty;
			bool bFirst=true;
			if(bFar)
			{
				foreach(Node node in parentNode.Nodes)
				{
					if(!node.Visible)
						continue;
					int top=y;
					bool anyVisibleNodes=node.AnyVisibleNodes;
					if(node.ChildNodesBounds.Height>node.BoundsRelative.Height && node.Expanded && anyVisibleNodes)
						top+=(node.ChildNodesBounds.Height-node.BoundsRelative.Height)/2;
					OffsetNodeLocation(node,x,top);
					if(node.Expanded && anyVisibleNodes)
					{
						PositionSubNodes(node,bFar);
						y+=(Math.Max(node.BoundsRelative.Height,node.ChildNodesBounds.Height)+this.NodeVerticalSpacing);
					}
					else y+=(node.BoundsRelative.Height+this.NodeVerticalSpacing);
					if(bFirst)
					{
						rChildBounds=node.BoundsRelative;
						bFirst=false;
					}
					else
						rChildBounds=Rectangle.Union(rChildBounds,node.BoundsRelative);
					if(!node.ChildNodesBounds.IsEmpty)
						rChildBounds=Rectangle.Union(rChildBounds,node.ChildNodesBounds);
				}
			}
			else
			{
				foreach(Node node in parentNode.Nodes)
				{
					if(!node.Visible)
						continue;
					int left=x-node.BoundsRelative.Width;
					int top=y;
					bool anyVisibleNodes=node.AnyVisibleNodes;
					if(node.ChildNodesBounds.Height>node.BoundsRelative.Height && node.Expanded && anyVisibleNodes)
						top+=(node.ChildNodesBounds.Height-node.BoundsRelative.Height)/2;
					OffsetNodeLocation(node,left,top);
					if(node.Expanded && anyVisibleNodes)
					{
						PositionSubNodes(node,bFar);
						y+=(Math.Max(node.BoundsRelative.Height,node.ChildNodesBounds.Height)+this.NodeVerticalSpacing);
					}
					else y+=(node.BoundsRelative.Height+this.NodeVerticalSpacing);
					if(bFirst)
					{
						rChildBounds=node.BoundsRelative;
						bFirst=false;
					}
					else
						rChildBounds=Rectangle.Union(rChildBounds,node.BoundsRelative);
					if(!node.ChildNodesBounds.IsEmpty)
						rChildBounds=Rectangle.Union(rChildBounds,node.ChildNodesBounds);
				}
			}
			parentNode.ChildNodesBounds=rChildBounds;
		}
		private int GetMinimumAreaHeight(ArrayList nodes, int rootHeight)
		{
			int height=rootHeight;
			if(nodes.Count==1)
				height+=(((Node)nodes[0]).BoundsRelative.Height*2);
			else if(nodes.Count>=2)
				height+=(((Node)nodes[0]).BoundsRelative.Height+((Node)nodes[1]).BoundsRelative.Height);
			return height;
		}
		private int SubNodeHorizontalSpacing
		{
			get {return 24;}
		}
		/// 
		/// Returns true if root node should have expanded part
		/// 
		protected override bool RootHasExpandedPart
		{
			get {return false;}
		}
		private struct NodesMapPosition
		{
			public ArrayList Near;
			public ArrayList Far;
			public ArrayList Default;
			public int NearHeight;
			public int FarHeight;
			public static NodesMapPosition Empty
			{
				get
				{
					NodesMapPosition m;
					m.Default=new ArrayList();
					m.Near=new ArrayList();
					m.Far=new ArrayList();
					m.NearHeight=0;
					m.FarHeight=0;
					return m;
				}
			}			
		}
		/// 
		/// Gets or sets the flow of the sub-root nodes for Map layout.
		/// 
		public eMapFlow MapFlow
		{
			get{return m_MapFlow;}
			set{m_MapFlow=value;}
		}
	}
}