using System;
using System.Drawing;
using System.Collections;
using DevComponents.DotNetBar;
using System.Drawing.Drawing2D;
using System.Drawing.Text;
using System.Collections.Generic;
namespace DevComponents.AdvTree.Layout
{
	/// 
	/// Summary description for NodeLayout.
	/// 
	internal abstract class NodeLayout
	{
		#region Private Variables
		protected int m_Height=0;
		protected int m_Width=0;
		protected AdvTree m_Tree=null;
		protected Rectangle m_ClientArea;
		//protected int m_ExpandAreaWidth=8;
		protected Size m_ExpandPartSize=new Size(8,8);
	    private Size _CachedExpandPartSize = Size.Empty;
		private int m_CommandAreaWidth=10;
		private int m_TreeLayoutChildNodeIndent = 16;
		private System.Windows.Forms.LeftRightAlignment m_LeftRight=System.Windows.Forms.LeftRightAlignment.Left;
		private int m_NodeVerticalSpacing=3;
		private int m_NodeHorizontalSpacing=0;
		private CellLayout m_CellLayout=null;
		private Graphics m_Graphics=null;
		#endregion
		public NodeLayout(AdvTree treeControl, Rectangle clientArea, LayoutSettings layoutSettings)
		{
			m_Tree=treeControl;
			m_ClientArea=clientArea;
            _LayoutSettings = layoutSettings;
		}
		/// 
		/// Performs layout of the nodes inside of the tree control.
		/// 
		public virtual void PerformLayout()
		{
		}
        public virtual void UpdateTopLevelColumnsWidth()
        {
        }
        private LayoutSettings _LayoutSettings;
        /// 
        /// Gets or sets layout settings.
        /// 
        public LayoutSettings LayoutSettings
        {
            get { return _LayoutSettings; }
            set { _LayoutSettings = value; }
        }
		/// 
		/// Performs layout for single unassigned node. Node does not have to be part of the tree control.
		/// 
		/// Node to perform layout on.
		public virtual void PerformSingleNodeLayout(Node node)
		{
			if(node==null)
				return;
			this.PrepareStyles();
			// Get default Columns
			System.Drawing.Graphics g=this.GetGraphics();
            SmoothingMode sm = g.SmoothingMode;
            TextRenderingHint th = g.TextRenderingHint;
            if (m_Tree.AntiAlias)
            {
                g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
                //g.TextRenderingHint = DisplayHelp.AntiAliasTextRenderingHint;
            }
			try
			{
				NodeLayoutContextInfo layoutInfo=this.GetDefaultNodeLayoutContextInfo(g);
				layoutInfo.ContextNode=node;
			    if (node.IsDragNode)
			        layoutInfo.DefaultColumns = new NodeColumnInfo(new List(), false);
				LayoutNode(layoutInfo);
			}
			finally
			{
                if (m_Tree.AntiAlias)
                {
                    g.SmoothingMode = sm;
                    //g.TextRenderingHint = th;
                }
				g.Dispose();
			}
		}
		public int Width
		{
			get {return m_Width;}
		}
		public int Height
		{
			get {return m_Height;}
		}
		public Rectangle ClientArea
		{
			get {return m_ClientArea;}
			set {m_ClientArea=value;}
		}
		
		public Graphics Graphics
		{
			get { return m_Graphics;}
			set { m_Graphics = value;}
		}
		
		internal bool DisposeGraphics
		{
			get
			{
				return (m_Graphics == null);
			}
		}
		protected virtual System.Drawing.Graphics GetGraphics()
		{
			if(m_Graphics!=null)
				return m_Graphics;
			
			Graphics g=m_Tree.CreateGraphics();
			
			return g;
		}
		/// 
		/// Resizes all styles and prepares them for layout.
		/// 
		protected virtual void PrepareStyles()
		{
			// Resize styles if needed
			foreach(ElementStyle es in m_Tree.Styles)
			{
				if(es.SizeChanged)
					ElementStyleLayout.CalculateStyleSize(es,m_Tree.Font);
			}
            if (_LayoutSettings != null)
                _CachedExpandPartSize = Dpi.Size(_LayoutSettings.ExpandPartSize);
            else
    			_CachedExpandPartSize = Dpi.Size(m_ExpandPartSize);
            
		}
		/// 
		/// Returns default top-level columns for tree control.
		/// 
		/// Returns array list of ColumnInfo objects.
        protected virtual NodeColumnInfo GetDefaultColumnInfo()
		{
            List ci = new List();
            NodeColumnInfo info = new NodeColumnInfo(ci, false);
			ColumnHeaderCollection columns=m_Tree.Columns;
            //int treeWidth = m_Tree.ClientRectangle.Width;
			if(columns!=null)
			{
                for (int i = 0; i < columns.Count; i++)
				{
                    int columnIndex = columns.DisplayIndexMap[i];
                    ColumnHeader h = columns[columnIndex];
                    ColumnInfo columnInfo = new ColumnInfo(h.Bounds.Width, h.Visible, h, columnIndex);
                    ci.Add(columnInfo);
                    info.HasAutoSizeColumn |= columnInfo.AutoSize;
				}
			}
			return info;
		}
		/// 
		/// Returns column information for a given node.
		/// 
		/// Node to return column information for
		/// Returns array list of ColumnInfo objects or null if there are no columns defined.
		protected virtual NodeColumnInfo GetNodeColumnInfo(Node node)
		{
            if (!node.HasColumns)
            {
                return null;
            }
            List ci = new List();
            NodeColumnInfo info = new NodeColumnInfo(ci, false);
            ColumnHeaderCollection columns = node.NodesColumns;
            for (int i = 0; i < columns.Count; i++)
			{
                int columnIndex = columns.DisplayIndexMap[i];
                ColumnHeader h = columns[columnIndex];
                ColumnInfo columnInfo = new ColumnInfo(h.Bounds.Width, h.Visible, h, columnIndex);
                ci.Add(columnInfo);
                info.HasAutoSizeColumn |= columnInfo.AutoSize;
			}
            return info;
		}
        ///// 
        ///// Gets or sets the vertical spacing between nodes in pixels.
        ///// 
        //public virtual int NodeVerticalSpacing
        //{
        //    get {return m_NodeVerticalSpacing;}
        //    set {m_NodeVerticalSpacing=value;}
        //}
        ///// 
        ///// Gets or sets the horizontal spacing between nodes in pixels.
        ///// 
        //public virtual int NodeHorizontalSpacing
        //{
        //    get {return m_NodeHorizontalSpacing;}
        //    set {m_NodeHorizontalSpacing=value;}
        //}
		/// 
		/// Gets or sets the child node indent in pixels.
		/// 
		public virtual int TreeLayoutChildNodeIndent
		{
			get {return m_TreeLayoutChildNodeIndent; }
			set { m_TreeLayoutChildNodeIndent = value; }
		}
		/// 
		/// Returns column header collection for the given column template name.
		/// 
		/// Name of the column template.
		/// Column header collection or null if template name cannot be found.
		public virtual ColumnHeaderCollection GetColumnHeader(string name)
		{
			if(name=="" || name==null)
				return null;
			return m_Tree.Headers.GetByName(name).Columns;
		}
        //private int _ExpandAreaWidth = 24;
        ///// 
        ///// Returns width of the expand button area. Default is 24 pixels.
        ///// 
        //public virtual int ExpandAreaWidth
        //{
        //    get { return _ExpandAreaWidth; }
        //    set
        //    {
        //        _ExpandAreaWidth = value;
        //    }
        //}
        ///// 
        ///// Gets or sets width of command button area. Default is 8 pixels.
        ///// 
        //public virtual int CommandAreaWidth
        //{
        //    get {return m_CommandAreaWidth;}
        //    set {m_CommandAreaWidth=value;}
        //}
		/// 
		/// Sets the position and size of the node command button.
		/// 
		/// Node layout context information
		protected virtual void LayoutCommandPart(NodeLayoutContextInfo layoutInfo, ElementStyle nodeStyle)
		{
			// Command part is right-aligned just before the node border
            Rectangle bounds = new Rectangle(layoutInfo.ContextNode.ContentBounds.Right - this.LayoutSettings.CommandAreaWidth -
				ElementStyleLayout.StyleSpacing(nodeStyle,eSpacePart.Border,eStyleSide.Right),layoutInfo.ContextNode.ContentBounds.Y+
				ElementStyleLayout.StyleSpacing(nodeStyle,eSpacePart.Border,eStyleSide.Top),
                this.LayoutSettings.CommandAreaWidth, layoutInfo.ContextNode.ContentBounds.Height -
				ElementStyleLayout.StyleSpacing(nodeStyle,eSpacePart.Border,eStyleSide.Top)-
				ElementStyleLayout.StyleSpacing(nodeStyle,eSpacePart.Border,eStyleSide.Bottom));
//			Rectangle bounds=new Rectangle(layoutInfo.ContextNode.ContentBounds.Right-this.CommandAreaWidth-
//				ElementStyleLayout.StyleSpacing(nodeStyle,eSpacePart.Border,eStyleSide.Right),layoutInfo.ContextNode.ContentBounds.Y, 
//				this.CommandAreaWidth, layoutInfo.ContextNode.ContentBounds.Height);
			layoutInfo.ContextNode.CommandBoundsRelative=bounds;
		}
		/// 
		/// Determines the rectangle of the +/- part of the tree node that is used to expand node.
		/// 
		/// Node layout context information
		protected virtual void LayoutExpandPart(NodeLayoutContextInfo layoutInfo, bool bLeftNode, int x)
		{
			Node node=layoutInfo.ContextNode;
			Size partSize=GetExpandPartSize();
			Rectangle bounds=new Rectangle(x,0,partSize.Width,partSize.Height);
            if (node.ExpandPartVerticalAlignment == eVerticalAlign.Middle)
                bounds.Y = (node.BoundsRelative.Height - bounds.Height) / 2;
            else if (node.ExpandPartVerticalAlignment == eVerticalAlign.Top)
                bounds.Y = Dpi.Height3;
            else
                bounds.Y = node.BoundsRelative.Height - partSize.Height - Dpi.Height3;
            if (bLeftNode || layoutInfo.ExpandPartAlignedLeft && layoutInfo.LeftToRight)
                bounds.X += (layoutInfo.ExpandAreaWidth - bounds.Width) / 2;
            else
                bounds.X = node.BoundsRelative.Right - layoutInfo.ExpandAreaWidth + (layoutInfo.ExpandAreaWidth - partSize.Width) / 2;
			node.SetExpandPartRectangle(bounds);
		}
		/// 
		/// Returns the size of the node expand part.
		/// 
		/// Size of the expand part, default 8,8.
		protected virtual Size GetExpandPartSize()
		{
            return _CachedExpandPartSize;
            //if (_LayoutSettings != null)
            //    return _LayoutSettings.ExpandPartSize;
            //return m_ExpandPartSize;
		}
        ///// 
        ///// Gets or sets the size of the expand part that is expanding/collapsing the node. Default value is 8,8.
        ///// 
        //public System.Drawing.Size ExpandPartSize
        //{
        //    get {return m_ExpandPartSize;}
        //    set {m_ExpandPartSize=value;}
        //}
		/// 
		/// Provides the layout for single node.
		/// 
		/// Layout information.
		protected virtual void LayoutNode(NodeLayoutContextInfo layoutInfo)
		{
			bool bHasExpandPart=this.HasExpandPart(layoutInfo);
			bool bHasCommandPart=this.HasCommandPart(layoutInfo);
			Node node=layoutInfo.ContextNode;
            Rectangle nodeRect = new Rectangle(layoutInfo.Left, layoutInfo.Top, 0, 0);
			Rectangle nodeContentRect=Rectangle.Empty; // Node content rect excludes expand rect
			int height=0, width=0;
			
			// Left node relative to the main root node...
            bool bLeftNode = layoutInfo.LeftToRight; // (layoutInfo.MapPositionNear && layoutInfo.LeftToRight);
            layoutInfo.LayoutNodeExpandPartWidth = 0;
			if(bLeftNode && bHasExpandPart || this.ReserveExpandPartSpace)
			{
                layoutInfo.LayoutNodeExpandPartWidth = (layoutInfo.ExpandAreaWidth + this.GetCellLayout().CellPartSpacing);
                width += (layoutInfo.ExpandAreaWidth + this.GetCellLayout().CellPartSpacing);
			}
			int x=width;		// relative to 0,0 of the node
			int y=0;			// Relative to 0,0 of the node
			
			// Apply node style
			ElementStyle nodeStyle=null;
			if(node.Expanded && node.StyleExpanded!=null)
				nodeStyle=node.StyleExpanded;
			else if(node.Style!=null)
				nodeStyle=node.Style;
			else
				nodeStyle=layoutInfo.DefaultNodeStyle;
			nodeContentRect.X=x;
			if(nodeStyle!=null)
			{
				x+=ElementStyleLayout.LeftWhiteSpace(nodeStyle);
				y+=ElementStyleLayout.TopWhiteSpace(nodeStyle);
				nodeContentRect.X+=nodeStyle.MarginLeft;
				nodeContentRect.Y+=nodeStyle.MarginTop;
			}
            Size size = this.GetCellLayout().LayoutCells(layoutInfo, x, y);
            node.SetCellsBounds(new Rectangle(x, y, size.Width, size.Height));
			height=size.Height;
			width+=size.Width;
			nodeContentRect.Width=size.Width;
			nodeContentRect.Height=size.Height;
			if(nodeStyle!=null)
			{
				nodeContentRect.Width+=(ElementStyleLayout.StyleSpacing(nodeStyle,eSpacePart.Padding | eSpacePart.Border,eStyleSide.Left)+
					ElementStyleLayout.StyleSpacing(nodeStyle,eSpacePart.Padding | eSpacePart.Border,eStyleSide.Right));
				nodeContentRect.Height+=(ElementStyleLayout.StyleSpacing(nodeStyle,eSpacePart.Padding | eSpacePart.Border,eStyleSide.Top)+
					ElementStyleLayout.StyleSpacing(nodeStyle,eSpacePart.Padding | eSpacePart.Border,eStyleSide.Bottom));
				width+=(ElementStyleLayout.HorizontalStyleWhiteSpace(nodeStyle));
				height+=(ElementStyleLayout.VerticalStyleWhiteSpace(nodeStyle));
			}
            if (!bLeftNode && bHasExpandPart && !this.ReserveExpandPartSpace)
                width += layoutInfo.ExpandAreaWidth;
			if(bHasCommandPart)
			{
                width += this.LayoutSettings.CommandAreaWidth;
                nodeContentRect.Width += this.LayoutSettings.CommandAreaWidth;
			}
			nodeRect.Height=height;
			nodeRect.Width=width;
			node.SetBounds(nodeRect);
			node.SetContentBounds(nodeContentRect);
			if(bHasCommandPart)
				LayoutCommandPart(layoutInfo, nodeStyle);
			else
				node.CommandBoundsRelative=Rectangle.Empty;
            if (bHasExpandPart || this.ReserveExpandPartSpace)
				LayoutExpandPart(layoutInfo,bLeftNode, 0);
			else
				node.SetExpandPartRectangle(Rectangle.Empty);
			node.SizeChanged=false;
			// Calculate size and location of node column header if any
			//if(node.NodesColumnHeaderVisible)
			{
				//layoutInfo.Left+=this.NodeLevelOffset;
				LayoutColumnHeader(layoutInfo);
				//layoutInfo.Left-=this.NodeLevelOffset;
			}
		}
		/// 
		/// Returns true if given node has expand part.
		/// 
		/// Layout context information.
		/// 
		protected virtual bool HasExpandPart(NodeLayoutContextInfo layoutInfo)
		{
			Node node=layoutInfo.ContextNode;
			if(node.ExpandVisibility==eNodeExpandVisibility.Auto)
			{
				if(IsRootNode(node) && !RootHasExpandedPart || !NodeOperations.GetAnyVisibleNodes(node))
					return false;
				return true;
			}
			else
				return (node.ExpandVisibility==eNodeExpandVisibility.Visible);
		}
		/// 
		/// Returns whether given node has command part.
		/// 
		/// Layout context information.
		/// True if command part should be drawn otherwise false.
		protected virtual bool HasCommandPart(NodeLayoutContextInfo layoutInfo)
		{
			return layoutInfo.ContextNode.CommandButton;
		}
		/// 
		/// Returns true if root node should have expanded part
		/// 
		protected virtual bool RootHasExpandedPart
		{
			get {return true;}
		}
		/// 
		/// Returns true if expand part space should be accounted for even if they expand part is not visible or need to be displayed. Default value is false.
		/// 
		protected virtual bool ReserveExpandPartSpace
		{
			get
			{
				return false;
			}
		}
		/// 
		/// Returns class responsible for cell layout.
		/// 
		/// Cell layout class.
		protected internal virtual CellLayout GetCellLayout()
		{
            if (m_CellLayout == null)
                m_CellLayout = new CellLayout(this.LayoutSettings);
			return m_CellLayout;
		}
		/// 
		/// Offsets node location and location of it's child nodes bounds.
		/// 
		/// Node to offset.
		/// Horizontal offset.
		/// Vertical offset.
		protected virtual void OffsetNodeLocation(Node node, int x, int y)
		{
			node.SetBounds(new Rectangle(node.BoundsRelative.X+x,node.BoundsRelative.Y+y,node.BoundsRelative.Width,node.BoundsRelative.Height));
			if(node.Expanded)
				node.ChildNodesBounds=new Rectangle(node.ChildNodesBounds.X+x,node.ChildNodesBounds.Y+y,node.ChildNodesBounds.Width,node.ChildNodesBounds.Height);
		}
		protected virtual NodeLayoutContextInfo GetDefaultNodeLayoutContextInfo(System.Drawing.Graphics graphics)
		{
			NodeLayoutContextInfo layoutInfo=new NodeLayoutContextInfo();
			layoutInfo.ClientRectangle=m_ClientArea;
			layoutInfo.DefaultColumns=this.GetDefaultColumnInfo();
			layoutInfo.ChildColumns=null;
            layoutInfo.Indent = m_Tree.Indent;
			layoutInfo.Left=0;
			layoutInfo.Top=0;
		    layoutInfo.LeftMargin = m_Tree.BackgroundStyle.PaddingLeft;
			layoutInfo.DefaultFont=m_Tree.Font;
			layoutInfo.LeftToRight=(this.LeftRight==System.Windows.Forms.LeftRightAlignment.Left);
			layoutInfo.Graphics=graphics;
			layoutInfo.Styles=m_Tree.Styles;
            layoutInfo.FullRowBackgroundNodes = new ArrayList();
			if(m_Tree.CellLayout!=eCellLayout.Default)
				layoutInfo.CellLayout=m_Tree.CellLayout;
			if(m_Tree.CellPartLayout!=eCellPartLayout.Default)
				layoutInfo.CellPartLayout=m_Tree.CellPartLayout;
				
			if(m_Tree.NodeStyle!=null)
				layoutInfo.DefaultNodeStyle=m_Tree.NodeStyle;
			if(m_Tree.CellStyleDefault!=null)
				layoutInfo.DefaultCellStyle=m_Tree.CellStyleDefault;
			else
				layoutInfo.DefaultCellStyle=ElementStyle.GetDefaultCellStyle(layoutInfo.DefaultNodeStyle);
			// Determine size of the default Column Header
			if(m_Tree.ColumnStyleNormal!=null)
			{
				ElementStyleLayout.CalculateStyleSize(m_Tree.ColumnStyleNormal,layoutInfo.DefaultFont);
				layoutInfo.DefaultHeaderSize=m_Tree.ColumnStyleNormal.Size;
			}
			if(layoutInfo.DefaultHeaderSize.IsEmpty)
				layoutInfo.DefaultHeaderSize.Height=layoutInfo.DefaultFont.Height+4;
            layoutInfo.ExpandPartWidth = Dpi.Width(this.Tree.ExpandWidth);
            layoutInfo.View = this.Tree.View;
            layoutInfo.TileSize = Dpi.Size(this.Tree.TileSize);
            layoutInfo.ColumnStyle = this.Tree.ColumnStyleNormal;
		    layoutInfo.ExpandAreaWidth = Dpi.Width(this.LayoutSettings.ExpandAreaWidth);
			return layoutInfo;
		}
		protected Node[] GetTopLevelNodes()
		{
			if(m_Tree.DisplayRootNode!=null)
				return new Node[] {m_Tree.DisplayRootNode};
			else
			{
				Node[] nodes=new Node[m_Tree.Nodes.Count];
				m_Tree.Nodes.CopyTo(nodes);
				return nodes;
			}
		}
		protected bool IsRootNode(Node node)
		{
			return NodeOperations.IsRootNode(m_Tree,node);
		}
		protected virtual void EmptyBoundsUnusedNodes(Node[] topLevelNodes)
		{
			if(m_Tree.DisplayRootNode!=null)
			{
				Node node=m_Tree.DisplayRootNode.PrevVisibleNode;
				while(node!=null)
				{
					node.SetBounds(Rectangle.Empty);
					node=node.PrevVisibleNode;
				}
				node=m_Tree.DisplayRootNode.NextNode;
				if(node==null)
				{
					node=m_Tree.DisplayRootNode.Parent;
					while(node!=null)
					{
						if(node.NextNode!=null)
						{
							node=node.NextNode;
							break;
						}
						else
							node=node.Parent;
					}
				}
				while(node!=null)
				{
					node.SetBounds(Rectangle.Empty);
					node=node.NextVisibleNode;
				}
			}
			else
			{
				for(int i=1;i
        /// Initializes a new instance of the NodeColumnInfo structure.
        /// 
        /// 
        /// 
        public NodeColumnInfo(List columnInfo, bool hasAutoSizeColumn)
        {
            ColumnInfo = columnInfo;
            HasAutoSizeColumn = hasAutoSizeColumn;
        }
        /// 
        /// Gets or sets the list of column info object for the columns.
        /// 
        public List ColumnInfo;
        /// 
        /// Gets or sets whether columns have auto-size column.
        /// 
        public bool HasAutoSizeColumn;
    }
}