using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data.SqlTypes;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Windows.Forms.VisualStyles;
using DevComponents.DotNetBar.Metro;
using DevComponents.DotNetBar.Rendering;
namespace DevComponents.DotNetBar.Controls
{
    /// 
    /// Represents SideNav control to create "hamburger" menus.
    /// 
    [ToolboxBitmap(typeof(SideNav), "Controls.SideNav.ico"), ToolboxItem(true),Designer("DevComponents.DotNetBar.Design.SideNavDesigner, DevComponents.DotNetBar.Design, Version=14.1.0.37, Culture=neutral,  PublicKeyToken=90f470f34c89ccaf"), System.Runtime.InteropServices.ComVisible(false)]
    public class SideNav : System.Windows.Forms.ContainerControl
    {
        #region Constructor
        private SideNavStrip _Strip = null;
        private Bar _TitleBar = null;
        private LabelItem _TitleLabel = null;
        private ButtonItem _CloseButton = null;
        private ButtonItem _MaximizeButton = null;
        private ButtonItem _RestoreButton = null;
        private Control _Splitter = null;
        public SideNav()
        {
            this.SetStyle(ControlStyles.AllPaintingInWmPaint
                  | ControlStyles.ResizeRedraw
                  | DisplayHelp.DoubleBufferFlag
                  | ControlStyles.UserPaint
                  | ControlStyles.Opaque
                  , true);
            this.Padding = new System.Windows.Forms.Padding(1);
            _Splitter = new Control();
            _Splitter.Width = 4;
            _Splitter.Name = "splitter";
            _Splitter.Cursor = Cursors.VSplit;
            _Splitter.Dock = DockStyle.Right;
            _Splitter.MouseMove += SplitterMouseMove;
            _Splitter.MouseDown += SplitterMouseDown;
            this.Controls.Add(_Splitter);
            // Title bar and fold/extend buttons
            Bar titleBar = new Bar();
            titleBar.Name = "titleBar";
            titleBar.AntiAlias = true;
            titleBar.Name = "titleBar";
            titleBar.PaddingBottom = 7;
            titleBar.PaddingTop = 5;
            titleBar.PaddingLeft = 6;
            titleBar.RoundCorners = false;
            titleBar.Dock = DockStyle.Top;
            titleBar.Stretch = true;
            titleBar.Style = DevComponents.DotNetBar.eDotNetBarStyle.StyleManagerControlled;
            titleBar.TabIndex = 0;
            titleBar.TabStop = false;
            titleBar.BarType = eBarType.StatusBar;
            titleBar.ItemsContainer.OverflowEnabled = false;
            LabelItem titleLabel=new LabelItem();
            titleLabel.Name = "titleLabel";
            titleLabel.Text = "Title";
            titleLabel.FontBold = true;
            _TitleLabel = titleLabel;
            titleBar.Items.Add(titleLabel);
            _TitleBar = titleBar;
            _CloseButton = new ButtonItem("openCloseButton");
            _CloseButton.Symbol = "\uf0d9";
            _CloseButton.ImagePaddingHorizontal = 18;
            _CloseButton.ItemAlignment= eItemAlignment.Far;
            _CloseButton.SymbolSize = 12f;
            _CloseButton.Click+=CloseButtonClick;
            titleBar.Items.Add(_CloseButton);
            _MaximizeButton = new ButtonItem("maximizeButton");
            _MaximizeButton.Symbol = "\uf090";
            _MaximizeButton.ItemAlignment = eItemAlignment.Far;
            _MaximizeButton.SymbolSize = 12f;
            _MaximizeButton.Click += MaximizeButtonClick;
            titleBar.Items.Add(_MaximizeButton);
            _RestoreButton = new ButtonItem("restoreButton");
            _RestoreButton.Symbol = "\uf100";
            _RestoreButton.ItemAlignment = eItemAlignment.Far;
            _RestoreButton.SymbolSize = 12f;
            _RestoreButton.Click += RestoreButtonClick;
            _RestoreButton.Visible = false;
            titleBar.Items.Add(_RestoreButton);
            this.Controls.Add(titleBar);
            _Strip = new SideNavStrip();
            _Strip.Dock = DockStyle.Left;
            _Strip.AutoSize = true;
            _Strip.AutoSyncSizeOrientation = eOrientation.Horizontal;
            _Strip.ButtonCheckedChanged += StripButtonCheckedChanged;
            //SideNavItem menuItem=new SideNavItem();
            //menuItem.Name = "menuItem";
            //menuItem.Text = "Menu";
            //menuItem.Symbol = "\uf0c9";
            //menuItem.IsSystemMenu = true;
            //_Strip.Items.Add(menuItem);
            //Separator sep = new Separator();
            //sep.SeparatorOrientation = eDesignMarkerOrientation.Vertical;
            //sep.FixedSize = new Size(3,1);
            //sep.Padding.Left = 6;
            //sep.Padding.Right = 6;
            //_Strip.Items.Add(sep);
            //SideNavItem item = new SideNavItem();
            //item.Text = "Home";
            //item.Symbol = "\uf015";
            //_Strip.Items.Add(item);
            //SideNavPanel panel=new SideNavPanel();
            //panel.Dock = DockStyle.Fill;
            //this.Controls.Add(panel);
            //item.Panel = panel;
            //item = new SideNavItem();
            //item.Text = "Explore";
            //item.Symbol = "\uf002";
            //_Strip.Items.Add(item);
            //ButtonItem button=new ButtonItem();
            //button.Text = "Button";
            //button.Symbol = "\uf003";
            //button.ButtonStyle = eButtonStyle.ImageAndText;
            //_Strip.Items.Add(button);
            
            this.Controls.Add(_Strip);
            _Strip.Width = 48;
            StyleManager.Register(this);
            UpdateColors();
        }
        protected override void Dispose(bool disposing)
        {
            if (_IsMaximized && disposing && this.Parent!=null)
            {
                this.Parent.Resize -= ParentResize;
            }
            base.Dispose(disposing);
        }
        #endregion
        #region Implementation
        /// 
        /// Called by StyleManager to notify control that style on manager has changed and that control should refresh its appearance if
        /// its style is controlled by StyleManager.
        /// 
        /// New active style.
        [EditorBrowsable(EditorBrowsableState.Never)]
        public void StyleManagerStyleChanged(eDotNetBarStyle newStyle)
        {
            UpdateColors();
        }
        /// 
        /// Updates the control colors from the global color table.
        /// 
        public void UpdateColors()
        {
            SideNavColorTable ct = GetColorTable();
            _TitleBar.BackColor = ct.TitleBackColor;
            _TitleBar.BorderColors = ct.TitleBorderColors;
            this.Invalidate(true);
        }
        protected override void OnPaint(PaintEventArgs e)
        {   
            Graphics g = e.Graphics;
            DisplayHelp.FillRectangle(g,this.ClientRectangle, this.BackColor);
            SideNavColorTable table = GetColorTable();
            DisplayHelp.DrawRoundedRectangle(g, this.ClientRectangle, table.BorderColors, 0);
        }
        private SideNavColorTable GetColorTable()
        {
            return ((Office2007Renderer)GlobalManager.Renderer).ColorTable.SideNav;
        }
        [EditorBrowsable(EditorBrowsableState.Never)]
        public void SetDesignMode()
        {
            _Strip.SetDesignMode(true);
        }
        private bool _EnableSplitter = true;
        /// 
        /// Indicates whether splitter that is located on right hand side of open control is visible and enabled.
        /// 
        [DefaultValue(true), Category("Appearance"), Description("Indicates whether splitter that is located on right hand side of open control is visible and enabled.")]
        public bool EnableSplitter
        {
            get { return _EnableSplitter; }
            set
            {
                if (_EnableSplitter != value)
                {
                    bool oldValue = _EnableSplitter;
                    _EnableSplitter = value;
                    OnEnableSplitterChanged(value, oldValue);
                }
            }
        }
        protected virtual void OnEnableSplitterChanged(bool newValue, bool oldValue)
        {
            _Splitter.Visible = newValue;
        }
        private bool _EnableClose = true;
        /// 
        /// Indicates whether button which folds/closes the control is visible.
        /// 
        [DefaultValue(true), Category("Appearance"), Description("Indicates whether button which folds/closes the control is visible.")]
        public bool EnableClose
        {
            get { return _EnableClose; }
            set
            {
                if (_EnableClose != value)
                {
                    bool oldValue = _EnableClose;
                    _EnableClose = value;
                    OnEnableCloseChanged(value, oldValue);
                }
            }
        }
        protected virtual void OnEnableCloseChanged(bool newValue, bool oldValue)
        {
            _CloseButton.Visible = newValue;
            _TitleBar.RecalcLayout();
        }
        private bool _EnableMaximize = true;
        // 
        /// Indicates whether buttons which maximize and restore the control are visible.
        /// 
        [DefaultValue(true), Category("Apeparance"), Description("Indicates whether buttons which maximize and restore the control are visible.")]
        public bool EnableMaximize
        {
            get { return _EnableMaximize; }
            set
            {
                if (_EnableMaximize != value)
                {
                    bool oldValue = _EnableMaximize;
                    _EnableMaximize = value;
                    OnEnableMaximizeChanged(value, oldValue);
                }
            }
        }
        protected virtual void OnEnableMaximizeChanged(bool newValue, bool oldValue)
        {
            if (!newValue)
            {
                _MaximizeButton.Visible = false;
                _RestoreButton.Visible = false;
            }
            else
            {
                if (_IsMaximized)
                    _RestoreButton.Visible = true;
                else
                    _MaximizeButton.Visible = true;
            }
            _TitleBar.RecalcLayout();
        }
        /// 
        /// Returns collection of items on a bar.
        /// 
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content), Browsable(false)]
        public SubItemsCollection Items
        {
            get
            {
                return _Strip.Items;
            }
        }
        private void CloseButtonClick(object sender, EventArgs e)
        {
            Close(eEventSource.Mouse);
        }
        private void MaximizeButtonClick(object sender, EventArgs e)
        {
            Maximize(eEventSource.Mouse);
        }
        private bool _IsMaximized = false;
        private int _RestoredWidth = 0;
        /// 
        /// Maximizes control width so it fills up space to the right of the control.
        /// 
        /// Source of the event.
        public void Maximize(eEventSource source)
        {
            if(_IsMaximized) return;
            
            if (this.Parent == null) 
                return;
            if (this.Bounds.Right >= this.Parent.ClientRectangle.Right)
                return;
            
            CancelSourceEventArgs args=new CancelSourceEventArgs(source);
            OnBeforeMaximize(args);
            if(args.Cancel) return;
            _IsMaximized = true;
            int maxWidth = GetMaxWidth();
            _RestoredWidth = this.Width;
            BarFunctions.AnimateControl(this, true, _AnimationTime, this.Bounds, new Rectangle(this.Location, new Size(maxWidth, this.Height)));
            _RestoreButton.Visible = true;
            _MaximizeButton.Visible = false;
            _TitleBar.RecalcLayout();
            _Splitter.Visible = false;
            this.Invalidate();
            this.Parent.Resize += ParentResize;
        }
        void ParentResize(object sender, EventArgs e)
        {
            if (this.Parent.Width > 0)
                this.Width = GetMaxWidth();
        }
        private int GetMaxWidth()
        {
            int width = this.Width + this.Parent.ClientRectangle.Right - this.Bounds.Right;
            if (this.Parent is MetroAppForm)
            {
                MetroAppForm form = (MetroAppForm) this.Parent;
                if (form.BorderThickness.Right > 0)
                    width -= (int)form.BorderThickness.Right;
                else
                    width -= 3;
            }
            
            return width;
        }
        private void RestoreButtonClick(object sender, EventArgs e)
        {
            Restore(eEventSource.Mouse);
        }
        /// 
        /// Restores the control to previous size if it was maximized before.
        /// 
        /// Source of event.
        public void Restore(eEventSource source)
        {
            if (!_IsMaximized || _RestoredWidth == 0) return;
            _IsMaximized = false;
            
            CancelSourceEventArgs args = new CancelSourceEventArgs(source);
            OnBeforeRestore(args);
            if (args.Cancel) return;
            BarFunctions.AnimateControl(this, true, _AnimationTime, this.Bounds, new Rectangle(this.Location, new Size(_RestoredWidth, this.Height)));
            _RestoredWidth = 0;
            _RestoreButton.Visible = false;
            _MaximizeButton.Visible = true;
            _TitleBar.RecalcLayout();
            _Splitter.Visible = _EnableSplitter;
            this.Invalidate();
            if(this.Parent!=null)
                this.Parent.Resize -= ParentResize;
        }
        private bool _IsOpen = true;
        private int _OpenWidth = 0;
        /// 
        /// Gets or sets whether control is closed, i.e. whether selected item panel is shown or not. When closed
        /// any selected item is unselected and selected panel hidden.
        /// 
        [DefaultValue(false), Browsable(false)]
        public bool IsClosed
        {
            get { return !_IsOpen; }
            set
            {
                if (value)
                {
                    if(_IsOpen)
                        Close(eEventSource.Code);
                }
                else
                {
                    if (!_IsOpen)
                    {
                        SideNavItem item = FirstVisibleSideNavItem??_LastOpenItem;
                        if (item != null)
                            Open(item, eEventSource.Code);
                    }
                }
            }
        }
        private SideNavItem FirstVisibleSideNavItem
        {
            get
            {
                for (int i = 0; i < _Strip.Items.Count; i++)
                {
                    if (_Strip.Items[i] is SideNavItem && _Strip.Items[i].Visible)
                        return (SideNavItem)_Strip.Items[i];
                }
                return null;
            }
        }
        private void StripButtonCheckedChanged(object sender, EventArgs e)
        {
            if (sender is SideNavItem && ((SideNavItem)sender).Checked)
            {
                OnSelectedItemChanged(EventArgs.Empty);
                if (!_IsOpen)
                    Open((SideNavItem)sender, eEventSource.Code);
            }
        }
        /// 
        /// Opens the control, i.e. expands it, selects specified item and shows its associated panel.
        /// 
        /// Item to select.
        /// Source of the event.
        public void Open(SideNavItem item, eEventSource source)
        {
            if (item == null)
                throw new ArgumentException("item must be set to valid item to select");
            if (_IsOpen) return;
            CancelSourceEventArgs args=new CancelSourceEventArgs(source, item);
            OnBeforeOpen(args);
            if(args.Cancel) return;
            if (!item.Checked)
                item.Checked = true;
            BarFunctions.AnimateControl(this, true, _AnimationTime, this.Bounds, new Rectangle(this.Location, new Size(_OpenWidth, this.Height)));
            _IsOpen = true;
            _Splitter.Visible = _EnableSplitter;
            this.Invalidate();
        }
        private SideNavItem _LastOpenItem = null;
        /// 
        /// Closes the control, i.e. unselects any selected item, hide its associated panel and folds the control.
        /// 
        /// Source of the event.
        public void Close(eEventSource source)
        {
            if(!_IsOpen) return;
            CancelSourceEventArgs args = new CancelSourceEventArgs(source);
            OnBeforeClose(args);
            if (args.Cancel) return;
            _OpenWidth = this.Width;
            int closedWidth = _Strip.Width + this.Padding.Horizontal;
            _Splitter.Visible = false;
            BarFunctions.AnimateControl(this, true, _AnimationTime, this.Bounds, new Rectangle(this.Location, new Size(closedWidth, this.Height)));
            _IsOpen = false;
            if (_Strip.SelectedItem != null)
            {
                _LastOpenItem = _Strip.SelectedItem;
                _Strip.SelectedItem.Checked = false;
            }
            else
                _LastOpenItem = null;
            this.Invalidate();
        }
        private bool _IsMenuExpanded = true;
        /// 
        /// Indicates whether side menu is expanded, i.e. shows both image and text. When menu is collapsed only image is shown.
        /// 
        [DefaultValue(true), Category("Appearance"), Description("Indicates whether side menu is expanded, i.e. shows both image and text. When menu is collapsed only image is shown.")]
        public bool IsMenuExpanded
        {
            get { return _IsMenuExpanded; }
            set
            {
                if (_IsMenuExpanded != value)
                {
                    bool oldValue = _IsMenuExpanded;
                    _IsMenuExpanded = value;
                    OnIsMenuExpandedChanged(value, oldValue);
                }
            }
        }
        protected virtual void OnIsMenuExpandedChanged(bool newValue, bool oldValue)
        {
            ExpandMenu(newValue);
            OnIsMenuExpandedChanged(EventArgs.Empty);
        }
        /// 
        /// Occurs when IsMenuExpanded property has changed its value.
        /// 
        [Description("Occurs when IsMenuExpanded property has changed.")]
        public event EventHandler IsMenuExpandedChanged;
        /// 
        /// Raises IsMenuExpandedChanged event.
        /// 
        /// Provides event arguments.
        protected virtual void OnIsMenuExpandedChanged(EventArgs e)
        {
            EventHandler h = IsMenuExpandedChanged;
            if (h != null)
                h(this, e);
        }
        private bool _IsMenuExpandedDelayedSet = false;
        /// 
        /// Expands or collapses the control items menu.
        /// 
        /// 
        private void ExpandMenu(bool expand)
        {
            if (!this.IsHandleCreated)
            {
                _IsMenuExpandedDelayedSet = true;
                return;
            }
            for (int i = 0; i < _Strip.Items.Count; i++)
            {
                ButtonItem button = _Strip.Items[i] as ButtonItem;
                if (button != null)
                {
                    button.ButtonStyle = (expand ? eButtonStyle.ImageAndText : eButtonStyle.Default);
                }
            }
            _Strip.RecalcLayout();
            if (!_IsOpen)
                this.Width = _Strip.Width + this.Padding.Horizontal;
        }
        private int _AnimationTime = 250;
        /// 
        /// Indicates the animation time in milliseconds for operations that perform visual animation of transition. Set to zero to disable animation.
        /// 
        [DefaultValue(250), Category("Behavior"), Description("Indicates the animation time in milliseconds for operations that perform visual animation of transition. Set to zero to disable animation.")]
        public int AnimationTime
        {
            get { return _AnimationTime; }
            set
            {
                if (value != _AnimationTime)
                {
                    int oldValue = _AnimationTime;
                    _AnimationTime = value;
                    OnAnimationTimeChanged(oldValue, value);
                }
            }
        }
        /// 
        /// Called when AnimationTime property has changed.
        /// 
        /// Old property value
        /// New property value
        protected virtual void OnAnimationTimeChanged(int oldValue, int newValue)
        {
            //OnPropertyChanged(new PropertyChangedEventArgs("AnimationTime"));
        }
        /// 
        /// Occurs before the control is maximized and allows you to cancel that.
        /// 
        [Description("Occurs before the control is maximized and allows you to cancel that.")]
        public event CancelSourceEventHandler BeforeMaximize;
        /// 
        /// Raises BeforeMaximize event.
        /// 
        /// Provides event arguments.
        protected virtual void OnBeforeMaximize(CancelSourceEventArgs e)
        {
            CancelSourceEventHandler h = BeforeMaximize;
            if (h != null)
                h(this, e);
        }
        /// 
        /// Occurs before the control is restored and allows you to cancel that.
        /// 
        [Description("Occurs before the control is restored and allows you to cancel that.")]
        public event CancelSourceEventHandler BeforeRestore;
        /// 
        /// Raises BeforeRestore event.
        /// 
        /// Provides event arguments.
        protected virtual void OnBeforeRestore(CancelSourceEventArgs e)
        {
            CancelSourceEventHandler h = BeforeRestore;
            if (h != null)
                h(this, e);
        }
        /// 
        /// Occurs before the control is opened and allows you to cancel that.
        /// 
        [Description("Occurs before the control is opened and allows you to cancel that.")]
        public event CancelSourceEventHandler BeforeOpen;
        /// 
        /// Raises BeforeOpen event.
        /// 
        /// Provides event arguments.
        protected virtual void OnBeforeOpen(CancelSourceEventArgs e)
        {
            CancelSourceEventHandler h = BeforeOpen;
            if (h != null)
                h(this, e);
        }
        /// 
        /// Occurs before the control is closed and allows you to cancel that.
        /// 
        [Description("Occurs before the control is closed and allows you to cancel that.")]
        public event CancelSourceEventHandler BeforeClose;
        /// 
        /// Raises BeforeClose event.
        /// 
        /// Provides event arguments.
        protected virtual void OnBeforeClose(CancelSourceEventArgs e)
        {
            CancelSourceEventHandler h = BeforeClose;
            if (h != null)
                h(this, e);
        }
        protected override void OnHandleCreated(EventArgs e)
        {
            UpdateSelectedItemTitle();
            UpdateColors();
            if (_IsMenuExpandedDelayedSet)
            {
                ExpandMenu(_IsMenuExpanded);
                _IsMenuExpandedDelayedSet = false;
            }
            base.OnHandleCreated(e);
        }
        internal void UpdateSelectedItemTitle()
        {
            SideNavItem item = _Strip.SelectedItem;
            if (item == null) return;
            if (!string.IsNullOrEmpty(item.Title))
                _TitleLabel.Text = item.Title;
            else
                _TitleLabel.Text = item.Text;
        }
        private Point _SplitterMouseDownPoint = Point.Empty;
        private void SplitterMouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
                _SplitterMouseDownPoint = e.Location;
        }
        private void SplitterMouseMove(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                int maxWidth = GetMaxWidth();
                int minWidth = _Strip.Width + 64;
                int newWidth = this.Width + (e.X - _SplitterMouseDownPoint.X);
                if (newWidth > maxWidth)
                    newWidth = maxWidth;
                else if (newWidth < minWidth)
                    newWidth = minWidth;
                this.Width = newWidth;
            }
        }
        /// 
        /// Gets currently selected item. Only items with Panel assigned can be selected.
        /// 
        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public SideNavItem SelectedItem
        {
            get
            {
                foreach (BaseItem item in this.Items)
                {
                    if (item is SideNavItem && ((SideNavItem)item).Checked)
                    {
                        return (SideNavItem)item;
                    }
                }
                return null;
            }
            set
            {
                if (value != null && value.Panel != null)
                    value.Checked = true;
            }
        }
        /// 
        /// Occurs when SelectedItem changes.
        /// 
        [Description("Occurs when SelectedItem changes.")]
        public event EventHandler SelectedItemChanged;
        /// 
        /// Raises SelectedItemChanged event.
        /// 
        /// Provides event arguments.
        protected virtual void OnSelectedItemChanged(EventArgs e)
        {
            EventHandler h = SelectedItemChanged;
            if (h != null)
                h(this, e);
        }
        /// 
        /// Gets reference to internal SideNavStrip control.
        /// 
        [Browsable(false)]
        public SideNavStrip SideNavStrip
        {
            get { return _Strip; }
        }
        #endregion
    }
    /// 
    /// Defines delegate for the CancelSource events.
    /// 
    /// 
    /// 
    public delegate void CancelSourceEventHandler(object sender, CancelSourceEventArgs e);
    /// 
    /// Event arguments for CancelSourceEventHandler
    /// 
    public class CancelSourceEventArgs : CancelEventArgs
    {
        /// 
        /// Gets the source of the event.
        /// 
        public eEventSource Source = eEventSource.Code;
        /// 
        /// Gets any optional data that is associated with the event.
        /// 
        public object Data = null;
        /// 
        /// Creates new instance of the object.
        /// 
        /// Source of event
        public CancelSourceEventArgs(eEventSource source)
        {
            this.Source = source;
        }
        /// 
        /// Creates new instance of the object.
        /// 
        /// Source of event
        /// Optional data associated with the event.
        public CancelSourceEventArgs(eEventSource source, object data)
        {
            this.Source = source;
            this.Data = data;
        }
    }
}