using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
namespace DevComponents.DotNetBar
{
    /// 
    /// Represents simple item container which orders items horizontally and support all ItemAlignment settings.
    /// 
    [ToolboxItem(false)]
    public class SimpleItemContainer : ImageItem, IDesignTimeProvider
    {
        #region Constructor
        /// 
        /// Initializes a new instance of the SimpleItemContainer class.
        /// 
        public SimpleItemContainer()
        {
            // We contain other controls
            m_IsContainer = true;
            this.AccessibleRole = System.Windows.Forms.AccessibleRole.Grouping;
        }
        #endregion
        #region Implementation
        public override void RecalcSize()
        {
            if (_LayoutOrientation == eOrientation.Vertical)
            {
                RecalcSizeVertical();
            }
            else
            {
                List leftItems = new List();
                List centerItems = new List();
                List rightItems = new List();
                int centerWidth = 0;
                Size minSize = SplitItems(leftItems, centerItems, rightItems, out centerWidth);
                Point loc = this.Bounds.Location;
                int bottom = this.Bounds.Bottom;
                loc.X += _PaddingLeft;
                if (this.WidthInternal == 0 || this.WidthInternal <= minSize.Width)
                {
                    BaseItem[] items = new BaseItem[leftItems.Count + centerItems.Count + rightItems.Count];
                    leftItems.CopyTo(items);
                    centerItems.CopyTo(items, leftItems.Count);
                    rightItems.CopyTo(items, leftItems.Count + centerItems.Count);
                    BaseItem previousItem = null;
                    // Just line items up
                    foreach (BaseItem item in items)
                    {
                        if (!item.Visible) continue;
                        item.Displayed = true;
                        loc.X -= GetOverlapSpacing(previousItem, item);
                        item.LeftInternal = loc.X;
                        if (IsVerticallyCentered(item))
                            item.TopInternal = loc.Y + (minSize.Height - item.HeightInternal)/2;
                        else
                            item.TopInternal = loc.Y + (minSize.Height - item.HeightInternal);
                        loc.X += item.WidthInternal + _ItemSpacing;
                        previousItem = item;
                    }
                    m_Rect.Size = minSize;
                }
                else
                {
                    int leftItemsRight = 0;
                    BaseItem previousItem = null;
                    foreach (BaseItem item in leftItems)
                    {
                        item.Displayed = true;
                        if (item.BeginGroup) loc.X += _BeginGroupSpacing;
                        loc.X -= GetOverlapSpacing(previousItem, item);
                        item.LeftInternal = loc.X;
                        if (IsVerticallyCentered(item))
                            item.TopInternal = loc.Y + (minSize.Height - item.HeightInternal)/2;
                        else
                            item.TopInternal = loc.Y + (minSize.Height - item.HeightInternal);
                        loc.X += item.WidthInternal + _ItemSpacing;
                        previousItem = item;
                    }
                    leftItemsRight = loc.X;
                    int rightItemsLeft = 0;
                    loc = new Point(this.Bounds.Right - _PaddingRight, this.Bounds.Y);
                    previousItem = null;
                    for (int i = rightItems.Count - 1; i >= 0; i--)
                    {
                        BaseItem item = rightItems[i];
                        loc.X -= item.WidthInternal;
                        item.Displayed = true;
                        loc.X += GetOverlapSpacing(previousItem, item);
                        item.LeftInternal = loc.X;
                        loc.X -= _ItemSpacing + (item.BeginGroup ? _BeginGroupSpacing : 0);
                        if (IsVerticallyCentered(item))
                            item.TopInternal = loc.Y + (minSize.Height - item.HeightInternal)/2;
                        else
                            item.TopInternal = loc.Y + (minSize.Height - item.HeightInternal);
                        previousItem = item;
                    }
                    rightItemsLeft = loc.X;
                    loc = new Point((this.WidthInternal - centerWidth)/2, this.Bounds.Y);
                    if (loc.X < leftItemsRight || loc.X + centerWidth > rightItemsLeft)
                    {
                        loc.X = leftItemsRight + ((rightItemsLeft - leftItemsRight) - centerWidth)/2;
                    }
                    previousItem = null;
                    foreach (BaseItem item in centerItems)
                    {
                        item.Displayed = true;
                        if (item.BeginGroup) loc.X += _BeginGroupSpacing;
                        loc.X -= GetOverlapSpacing(previousItem, item);
                        item.LeftInternal = loc.X;
                        if (IsVerticallyCentered(item))
                            item.TopInternal = loc.Y + (minSize.Height - item.HeightInternal)/2;
                        else
                            item.TopInternal = loc.Y + (minSize.Height - item.HeightInternal);
                        loc.X += item.WidthInternal + _ItemSpacing;
                        previousItem = item;
                    }
                    m_Rect.Height = minSize.Height;
                }
                if (m_Rect.Width < _MinimumSize.Width)
                    m_Rect.Width = _MinimumSize.Width;
                if (m_Rect.Height < _MinimumSize.Height)
                    m_Rect.Height = _MinimumSize.Height;
            }
            base.RecalcSize();
        }
        protected virtual bool IsVerticallyCentered(BaseItem item)
        {
            return !(item is DevComponents.DotNetBar.Metro.MetroTabItem);
        }
        private Size SplitItems(List leftItems, List centerItems, List rightItems, out int centerWidth)
        {
            Size minSize = new Size();
            
            centerWidth = 0;
            BaseItem previousItem = null;
            foreach (BaseItem item in this.SubItems)
            {
                if (!item.Visible)
                {
                    item.Displayed = false;
                    continue;
                }
                item.RecalcSize();
                
                minSize.Width += item.WidthInternal + _ItemSpacing + (item.BeginGroup ? _BeginGroupSpacing : 0) - GetOverlapSpacing(previousItem, item);
                minSize.Height = Math.Max(item.HeightInternal, minSize.Height);
                if (item.ItemAlignment == eItemAlignment.Near)
                    leftItems.Add(item);
                else if (item.ItemAlignment == eItemAlignment.Center)
                {
                    centerItems.Add(item);
                    centerWidth += item.WidthInternal + _ItemSpacing;
                }
                else if (item.ItemAlignment == eItemAlignment.Far)
                    rightItems.Add(item);
                previousItem = item;
            }
            if (minSize.Width > 0) minSize.Width -= _ItemSpacing;
            if (centerWidth > 0) centerWidth -= _ItemSpacing;
            return minSize;
        }
        private int GetOverlapSpacing(BaseItem previousItem, BaseItem item)
        {
            if (previousItem == null || _OverlapSpacing <= 0 || _OverlapType == null ||
                !(_OverlapType.IsInstanceOfType(previousItem) && _OverlapType.IsInstanceOfType(item)))
                return 0;
            return _OverlapSpacing;
        }
        private bool IsHorizontallyCentered(BaseItem item)
        {
            return !(item is DevComponents.DotNetBar.Metro.MetroTabItem);
        }
        private Size SplitItemsVertical(List topItems, List centerItems, List bottomItems, out int centerHeight)
        {
            Size minSize = new Size();
            centerHeight = 0;
            BaseItem previousItem = null;
            foreach (BaseItem item in this.SubItems)
            {
                if (!item.Visible)
                {
                    item.Displayed = false;
                    continue;
                }
                item.RecalcSize();
                minSize.Height += item.HeightInternal + _ItemSpacing + (item.BeginGroup ? _BeginGroupSpacing : 0) - GetOverlapSpacing(previousItem, item);
                minSize.Width = Math.Max(item.WidthInternal, minSize.Width);
                if (item.ItemAlignment == eItemAlignment.Near)
                    topItems.Add(item);
                else if (item.ItemAlignment == eItemAlignment.Center)
                {
                    centerItems.Add(item);
                    centerHeight += item.HeightInternal + _ItemSpacing;
                }
                else if (item.ItemAlignment == eItemAlignment.Far)
                    bottomItems.Add(item);
                previousItem = item;
            }
            if (minSize.Height > 0) minSize.Height -= _ItemSpacing;
            if (centerHeight > 0) centerHeight -= _ItemSpacing;
            return minSize;
        }
        private void RecalcSizeVertical()
        {
            List leftItems = new List();
            List centerItems = new List();
            List rightItems = new List();
            int centerHeight = 0;
            Size minSize = SplitItemsVertical(leftItems, centerItems, rightItems, out centerHeight);
            Point loc = this.Bounds.Location;
            int bottom = this.Bounds.Bottom;
            if (this.HeightInternal == 0 || this.HeightInternal <= minSize.Height)
            {
                BaseItem[] items = new BaseItem[leftItems.Count + centerItems.Count + rightItems.Count];
                leftItems.CopyTo(items);
                centerItems.CopyTo(items, leftItems.Count);
                rightItems.CopyTo(items, leftItems.Count + centerItems.Count);
                // Just line items up
                foreach (BaseItem item in items)
                {
                    if (!item.Visible) continue;
                    item.Displayed = true;
                    item.TopInternal = loc.Y;
                    item.LeftInternal = loc.X;
                    //if (IsHorizontallyCentered(item))
                    //    item.LeftInternal = loc.X + (minSize.Width - item.WidthInternal) / 2;
                    //else
                    //    item.LeftInternal = loc.X + (minSize.Width - item.WidthInternal);
                    item.WidthInternal = minSize.Width;
                    loc.Y += item.HeightInternal + _ItemSpacing;
                }
                m_Rect.Size = minSize;
            }
            else
            {
                int leftItemsRight = 0;
                foreach (BaseItem item in leftItems)
                {
                    item.Displayed = true;
                    if (item.BeginGroup) loc.Y += _BeginGroupSpacing;
                    item.TopInternal = loc.Y;
                    item.WidthInternal = minSize.Width;
                    if (IsHorizontallyCentered(item))
                        item.LeftInternal = loc.X + (minSize.Width - item.WidthInternal) / 2;
                    else
                        item.LeftInternal = loc.X + (minSize.Width - item.WidthInternal);
                    loc.Y += item.HeightInternal + _ItemSpacing;
                }
                leftItemsRight = loc.Y;
                int rightItemsLeft = 0;
                loc = new Point(this.Bounds.X, this.Bounds.Bottom);
                for (int i = rightItems.Count - 1; i >= 0; i--)
                {
                    BaseItem item = rightItems[i];
                    loc.Y -= item.HeightInternal;
                    item.Displayed = true;
                    item.TopInternal = loc.Y;
                    item.WidthInternal = minSize.Width;
                    loc.Y -= _ItemSpacing + (item.BeginGroup ? _BeginGroupSpacing : 0);
                    if (IsHorizontallyCentered(item))
                        item.LeftInternal = loc.X + (minSize.Width - item.WidthInternal) / 2;
                    else
                        item.LeftInternal = loc.X + (minSize.Width - item.WidthInternal);
                }
                rightItemsLeft = loc.Y;
                loc = new Point(this.Bounds.X, (this.HeightInternal - centerHeight) / 2);
                if (loc.Y < leftItemsRight || loc.Y + centerHeight > rightItemsLeft)
                {
                    loc.Y = leftItemsRight + ((rightItemsLeft - leftItemsRight) - centerHeight) / 2;
                }
                foreach (BaseItem item in centerItems)
                {
                    item.Displayed = true;
                    if (item.BeginGroup) loc.Y += _BeginGroupSpacing;
                    item.TopInternal = loc.Y;
                    item.WidthInternal = minSize.Width;
                    if (IsHorizontallyCentered(item))
                        item.LeftInternal = loc.X + (minSize.Width - item.WidthInternal) / 2;
                    else
                        item.LeftInternal = loc.X + (minSize.Width - item.WidthInternal);
                    loc.Y += item.HeightInternal + _ItemSpacing;
                }
                m_Rect.Width = minSize.Width;
            }
            if (m_Rect.Width < _MinimumSize.Width)
                m_Rect.Width = _MinimumSize.Width;
            if (m_Rect.Height < _MinimumSize.Height)
                m_Rect.Height = _MinimumSize.Height;
        }
        public override void Paint(ItemPaintArgs p)
        {
            foreach (BaseItem item in this.SubItems)
            {
                if (!item.Displayed) continue;
                if (item.BeginGroup)
                {
                    DisplayHelp.DrawLine(p.Graphics, item.LeftInternal -3, this.TopInternal + 4, item.LeftInternal - 3, this.Bounds.Bottom - 8, p.Colors.ItemSeparator, 1);
                }
                item.Paint(p);
            }
        }
        /// 
        /// Returns copy of the item.
        /// 
        public override BaseItem Copy()
        {
            SimpleItemContainer objCopy = new SimpleItemContainer();
            this.CopyToItem(objCopy);
            return objCopy;
        }
        /// 
        /// Copies the item specific properties to new instance of the item.
        /// 
        /// New instance.
        internal void InternalCopyToItem(SimpleItemContainer copy)
        {
            CopyToItem(copy);
        }
        /// 
        /// Copies the item specific properties to new instance of the item.
        /// 
        /// New instance.
        protected override void CopyToItem(BaseItem copy)
        {
            SimpleItemContainer c = copy as SimpleItemContainer;
            base.CopyToItem(c);
        }
        internal void SetSystemFocus()
        {
            if (m_HotSubItem != null || this.SubItems.Count == 0)
                return;
            BaseItem exp = this.ExpandedItem();
            if (exp != null)
            {
                m_HotSubItem = exp;
                m_HotSubItem.InternalMouseEnter();
                m_HotSubItem.InternalMouseMove(new MouseEventArgs(MouseButtons.None, 0, m_HotSubItem.LeftInternal + 2, m_HotSubItem.TopInternal + 2, 0));
                return;
            }
            foreach (BaseItem objItem in this.SubItems)
            {
                if (!objItem.SystemItem && objItem.Displayed && objItem.Visible)
                {
                    m_HotSubItem = objItem;
                    m_HotSubItem.InternalMouseEnter();
                    m_HotSubItem.InternalMouseMove(new MouseEventArgs(MouseButtons.None, 0, m_HotSubItem.LeftInternal + 2, m_HotSubItem.TopInternal + 2, 0));
                    break;
                }
            }
        }
        internal void ReleaseSystemFocus()
        {
            CollapseSubItems(this);
            if (m_HotSubItem != null)
            {
                Control c = this.ContainerControl as Control;
                if (c != null)
                {
                    Point p = c.PointToClient(Control.MousePosition);
                    if (m_HotSubItem.DisplayRectangle.Contains(p))
                        return;
                }
                m_HotSubItem.InternalMouseLeave();
                m_HotSubItem = null;
            }
        }
        private int _ItemSpacing;
        private Size _MinimumSize = Size.Empty;
        /// 
        /// Gets or sets the minimum size of the container. Either Width or Height can be set or both. Default value is 0,0 which means
        /// that size is automatically calculated.
        /// 
        [Browsable(false)]
        public Size MinimumSize
        {
            get { return _MinimumSize; }
            set
            {
                _MinimumSize = value;
                this.NeedRecalcSize = true;
                this.OnAppearanceChanged();
            }
        }
        //private bool ShouldSerializeMinimumSize()
        //{
        //    return !_MinimumSize.IsEmpty;
        //}
        private bool _SystemContainer = false;
        /// 
        /// Returns whether instance of the item container is used as system container internally by DotNetBar.
        /// 
        [Browsable(false)]
        public bool SystemContainer
        {
            get { return _SystemContainer; }
        }
        /// 
        /// Sets whether container is used as system container internally by DotNetBar.
        /// 
        /// true or false to indicate whether container is system container or not.
        internal void SetSystemContainer(bool b)
        {
            _SystemContainer = b;
        }
        /// 
        /// Indicates the spacing between items.
        /// 
        public int ItemSpacing
        {
            get { return _ItemSpacing; }
            set
            {
                if (value != _ItemSpacing)
                {
                    int oldValue = _ItemSpacing;
                    _ItemSpacing = value;
                    OnItemSpacingChanged(oldValue, value);
                }
            }
        }
        /// 
        /// Called when ItemSpacing property has changed.
        /// 
        /// Old property value
        /// New property value
        protected virtual void OnItemSpacingChanged(int oldValue, int newValue)
        {
            this.NeedRecalcSize = true;
            this.OnAppearanceChanged();
            //OnPropertyChanged(new PropertyChangedEventArgs("ItemSpacing"));
            
        }
        private int _BeginGroupSpacing = 6;
        /// 
        /// Indicates additional spacing between item when its BeginGroup property is set.
        /// 
        [DefaultValue(6), Category("Appearance"), Description("Indicates additional spacing between item when its BeginGroup property is set.")]
        public int BeginGroupSpacing
        {
            get { return _BeginGroupSpacing; }
            set { _BeginGroupSpacing = value; }
        }
        private eOrientation _LayoutOrientation = eOrientation.Horizontal;
        /// 
        /// Specifies the layout orientation
        /// 
        [DefaultValue(eOrientation.Horizontal), Category("Layout"), Description("Specifies the layout orientation")]
        public eOrientation LayoutOrientation {
            get { return _LayoutOrientation; }
            set
            {
                if (_LayoutOrientation != value)
                {
                    _LayoutOrientation = value;
                    NeedRecalcSize = true;
                }
            } 
        }
        private bool _EqualizeButtonSize = false;
        /// 
        /// Indicates whether all items are resized to be of the size equal to largest item in the container
        /// 
        [DefaultValue(false), Category("Layout"), Description("Indicates whether all items are resized to be of the size equal to largest item in the container")]
        public bool EqualizeButtonSize
        {
            get { return _EqualizeButtonSize; }
            set
            {
                if (_EqualizeButtonSize != value)
                {
                    _EqualizeButtonSize = value;
                    NeedRecalcSize = true;
                }
            }
        }
        private int _PaddingLeft = 0;
        /// 
        /// Indicates left side padding within container.
        /// 
        public int PaddingLeft
        {
            get { return _PaddingLeft; }
            set { _PaddingLeft = value; }
        }
        private int _PaddingRight;
        /// 
        /// Indicates right side padding within container.
        /// 
        public int PaddingRight
        {
            get { return _PaddingRight; }
            set { _PaddingRight = value; }
        }
        private int _OverlapSpacing = 0;
        /// 
        /// Gets overlap spacing for specific item types specified by OverlapType in the container.
        /// 
        public int OverlapSpacing
        {
            get { return _OverlapSpacing; }
            set { _OverlapSpacing = value; }
        }
        private Type _OverlapType = null;
        /// 
        /// Gets the type of the item that will overlap.
        /// 
        public Type OverlapType
        {
            get { return _OverlapType; }
            set { _OverlapType = value; }
        }
        #endregion
        #region IDesignTimeProvider
        public InsertPosition GetInsertPosition(Point pScreen, BaseItem DragItem)
        {
            return DesignTimeProviderContainer.GetInsertPosition(this, pScreen, DragItem);
        }
        public void DrawReversibleMarker(int iPos, bool Before)
        {
            DesignTimeProviderContainer.DrawReversibleMarker(this, iPos, Before);
            return;
        }
        public void InsertItemAt(BaseItem objItem, int iPos, bool Before)
        {
            DesignTimeProviderContainer.InsertItemAt(this, objItem, iPos, Before);
            return;
        }
        #endregion
    }
}