using System;
using System.Collections;
using System.Drawing;
namespace DevComponents.Tree.Layout
{
	/// 
	/// Represents the class that performs node diagram layout.
	/// 
	internal class NodeDiagramLayout:NodeLayout
	{
		private eDiagramFlow m_DiagramFlow=eDiagramFlow.LeftToRight;
		public NodeDiagramLayout(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;
			System.Drawing.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)
		{
			if(m_DiagramFlow==eDiagramFlow.RightToLeft || m_DiagramFlow==eDiagramFlow.BottomToTop)
				layoutInfo.MapPositionNear=true;
			ProcessSubNode(layoutInfo);
			PositionRootNode(layoutInfo);
		}
		private void PositionRootNode(NodeLayoutContextInfo layoutInfo)
		{
			Point location=Point.Empty;
			Node root=layoutInfo.ContextNode;
			switch(m_DiagramFlow)
			{
				case eDiagramFlow.LeftToRight:
				{
					location.Y=(root.ChildNodesBounds.Height-root.BoundsRelative.Height)/2;
					break;
				}
				case eDiagramFlow.RightToLeft:
				{
					location.X=root.ChildNodesBounds.Width+this.NodeHorizontalSpacing;
					location.Y=(root.ChildNodesBounds.Height-root.BoundsRelative.Height)/2;
					break;
				}
				case eDiagramFlow.TopToBottom:
				{
					location.X=0; //(root.ChildNodesBounds.Width-root.Bounds.Width)/2;
					break;
				}
				case eDiagramFlow.BottomToTop:
				{
					location.X=root.ChildNodesBounds.Width; //-root.Bounds.Width; //-root.Bounds.Width)/2;
					location.Y=root.ChildNodesBounds.Height+this.NodeVerticalSpacing; //-root.Bounds.Height; //+this.NodeVerticalSpacing;
					break;
				}
			}
			OffsetNodeLocation(root,location.X,location.Y);
			Rectangle area=Rectangle.Empty;
			area=Rectangle.Union(area,root.BoundsRelative);
			PositionSubNodes(root);
			
			area=Rectangle.Union(area,root.ChildNodesBounds);
			root.ChildNodesBounds=area;
			m_Width=area.Width;
			m_Height=area.Height;
		}
//		private int GetMaxChildNodeWidth(Node parent)
//		{
//			int width=0;
//			foreach(Node node in parent.Nodes)
//			{
//				if(node.Bounds.Width>width)
//					width=node.Bounds.Width;
//			}
//			return width;
//		}
		private void PositionSubNodes(Node parentNode)
		{
			if(parentNode.Nodes.Count==0)
				return;
			Rectangle rChildBounds=Rectangle.Empty;
			bool bFirst=true;
			bool rootNode=IsRootNode(parentNode);
			if(m_DiagramFlow==eDiagramFlow.LeftToRight || !rootNode && m_DiagramFlow==eDiagramFlow.TopToBottom)
			{
				int y=parentNode.BoundsRelative.Y-(parentNode.ChildNodesBounds.Height-parentNode.BoundsRelative.Height)/2;
				int x=0;
//				if(parentNode.Parent!=null)
//					x=parentNode.Bounds.X+GetMaxChildNodeWidth(parentNode.Parent)+this.NodeHorizontalSpacing;
//				else
					x=parentNode.BoundsRelative.Right+this.NodeHorizontalSpacing;
				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);
						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 if(m_DiagramFlow==eDiagramFlow.RightToLeft || !rootNode && m_DiagramFlow==eDiagramFlow.BottomToTop)
			{
				int y=parentNode.BoundsRelative.Y-(parentNode.ChildNodesBounds.Height-parentNode.BoundsRelative.Height)/2;
				int x=parentNode.BoundsRelative.X-this.NodeHorizontalSpacing;
				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);
						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 if(m_DiagramFlow==eDiagramFlow.TopToBottom)
			{
				int y=parentNode.BoundsRelative.Bottom+this.NodeVerticalSpacing;
				
				foreach(Node node in parentNode.Nodes)
				{
					if(!node.Visible)
						continue;
					int left=parentNode.BoundsRelative.Right+this.NodeHorizontalSpacing;
					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);
						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 if(m_DiagramFlow==eDiagramFlow.BottomToTop)
			{
				int y=parentNode.BoundsRelative.Top-this.NodeVerticalSpacing;
				
				foreach(Node node in parentNode.Nodes)
				{
					if(!node.Visible)
						continue;
					int left=parentNode.BoundsRelative.Left-this.NodeHorizontalSpacing-node.BoundsRelative.Width;
					int top=y-node.BoundsRelative.Height;
					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);
						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 void ProcessSubNode(NodeLayoutContextInfo layoutInfo)
		{
			Node node=layoutInfo.ContextNode;
			bool bHorizontalFlow=true;//(m_DiagramFlow==eDiagramFlow.LeftToRight || m_DiagramFlow==eDiagramFlow.RightToLeft);
			if(node.SizeChanged)
			{
				// Calculate size of the node itself...
				LayoutNode(layoutInfo);
			}
			else
				node.SetBounds(new Rectangle(Point.Empty,node.BoundsRelative.Size));
			
			if(node.Expanded)
			{
				ArrayList parentColumns=layoutInfo.ChildColumns;
				ArrayList 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);
					if(bHorizontalFlow)
					{
						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));
					}
					else
					{
						childNodesBounds.Width+=(Math.Max(childNode.BoundsRelative.Width,childNode.ChildNodesBounds.Width)+this.NodeHorizontalSpacing);
						childNodesBounds.Height=Math.Max(childNodesBounds.Height,childNode.BoundsRelative.Height+(childNode.ChildNodesBounds.Height>0?this.NodeVerticalSpacing+childNode.ChildNodesBounds.Height:0));
					}
				}
				layoutInfo.ChildColumns=parentColumns;
				if(bHorizontalFlow)
				{
					if(childNodesBounds.Height>0)
						childNodesBounds.Height-=this.NodeVerticalSpacing;
				}
				else
				{
					if(childNodesBounds.Width>0)
						childNodesBounds.Width-=this.NodeHorizontalSpacing;
				}
				node.ChildNodesBounds=childNodesBounds;
				layoutInfo.ChildColumns=null;
				layoutInfo.ContextNode=node;
			}
			else node.ChildNodesBounds=Rectangle.Empty;
		}
		/// 
		/// Returns true if root node should have expanded part
		/// 
		protected override bool RootHasExpandedPart
		{
			get {return false;}
		}
		/// 
		/// Indicates the layout flow for the nodes.
		/// 
		public eDiagramFlow DiagramFlow
		{
			get {return m_DiagramFlow;}
			set
			{
				m_DiagramFlow=value;
			}
		}
	}
}