using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.ComponentModel;
using System.Drawing;
namespace DevComponents.DotNetBar.Controls
{
    /// 
    /// Represents the panel which can slide out and into the view.
    /// 
    [ToolboxBitmap(typeof(CircularProgress), "Controls.SlidePanel.ico"), ToolboxItem(true), Designer("DevComponents.DotNetBar.Design.SlidePanelDesigner, DevComponents.DotNetBar.Design, Version=14.1.0.37, Culture=neutral,  PublicKeyToken=90f470f34c89ccaf")]
    public class SlidePanel : UserControl
    {
        internal const int DefaultAnimationTime = 500;
        #region Events
        /// 
        /// Occurs when IsOpen property value has changed, i.e. slide-panel is shown or hidden.
        /// 
        [Description("Occurs when IsOpen property value has changed, i.e. slide-panel is shown or hidden.")]
        public event EventHandler IsOpenChanged;
        /// 
        /// Raises IsOpenChanged event.
        /// 
        /// Provides event arguments.
        protected virtual void OnIsOpenChanged(EventArgs e)
        {
            EventHandler handler = IsOpenChanged;
            if (handler != null)
                handler(this, e);
        }
        #endregion
        #region Constructor
        protected override void Dispose(bool disposing)
        {
            DisposeSlideOutButton();
            base.Dispose(disposing);
        }
        #endregion
        #region Implementation
        private eSlideSide _SlideSide = eSlideSide.Left;
        /// 
        /// Gets or sets side panel slides into.
        /// 
        [DefaultValue(eSlideSide.Left), Category("Behavior"), Description("Indicates side panel slides into.")]
        public eSlideSide SlideSide
        {
            get { return _SlideSide; }
            set
            {
                if (value != _SlideSide)
                {
                    eSlideSide oldValue = _SlideSide;
                    _SlideSide = value;
                    OnSlideSideChanged(oldValue, value);
                }
            }
        }
        /// 
        /// Called when SlideSide property has changed.
        /// 
        /// Old property value
        /// New property value
        protected virtual void OnSlideSideChanged(eSlideSide oldValue, eSlideSide newValue)
        {
            //OnPropertyChanged(new PropertyChangedEventArgs("SlideSide"));
            if (!this.IsOpen)
            {
                WaitForCurrentAnimationFinish();
                this.Bounds = GetSlideOutBounds(this.Parent, _OpenBounds);
                if (_SlideOutButtonVisible)
                    CreateSlideOutButton();
            }
        }
        private bool _IsOpen = true;
        /// 
        /// Gets or sets whether panel is open. When this property is changed panel will slide in or out of the view.
        /// 
        [Browsable(false), DefaultValue(true), Description("Indicates whether panel is open. When this property is changed panel will slide in or out of the view.")]
        public bool IsOpen
        {
            get { return _IsOpen; }
            set
            {
                if (_UsesBlockingAnimation && _CurrentAnimation != null || _IsWaiting) return;
                if (value != _IsOpen)
                {
                    bool oldValue = _IsOpen;
                    _IsOpen = value;
                    OnIsOpenChanged(oldValue, value);
                }
            }
        }
        /// 
        /// Called when IsOpen property has changed.
        /// 
        /// Old property value
        /// New property value
        protected virtual void OnIsOpenChanged(bool oldValue, bool newValue)
        {
            //OnPropertyChanged(new PropertyChangedEventArgs("IsOpen"));
            if (!newValue)
                SlideOutOfView();
            else
                SlideIntoView();
            OnIsOpenChanged(EventArgs.Empty);
        }
        private bool _UsesBlockingAnimation = false;
        /// 
        /// Gets or sets whether panel uses modal animation, meaning when IsOpen property is set the call is not returned until animation is complete.
        /// 
        [Browsable(false)]
        public bool UsesBlockingAnimation
        {
            get { return _UsesBlockingAnimation; }
            set
            {
                _UsesBlockingAnimation = value;
            }
        }
        private Rectangle _OpenBounds = Rectangle.Empty;
        /// 
        /// Gets or sets the open panel bounds. When control IsOpen=false and panel is collapsed its original bounds are stored in OpenBounds property and restored once panel is open.
        /// 
        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public Rectangle OpenBounds
        {
            get { return _OpenBounds; }
            set
            {
                _OpenBounds = value;
                if (!this.Visible && this.Bounds.Size != _OpenBounds.Size)
                    this.Bounds = GetSlideOutBounds(this.Parent, _OpenBounds);
            }
        }
        /// 
        /// Slides panel into the view.
        /// 
        private void SlideIntoView()
        {
            Animation.Animation current = _CurrentAnimation;
            if (current != null)
            {
                current.Stop();
                WaitForCurrentAnimationFinish();
            }
            Rectangle bounds = this.Bounds;
            Rectangle targetBounds = _OpenBounds;
            if (_SlideOutButton != null) _SlideOutButton.Visible = false;
            if (_AnimationTime > 0)
            {
                _IsAnimating = true;
                //BarFunctions.AnimateControl(this, true, _AnimationTime, bounds, targetBounds);
                this.Visible = true;
                Animation.AnimationRectangle anim = new DevComponents.DotNetBar.Animation.AnimationRectangle(
                   new Animation.AnimationRequest(this, "Bounds", bounds, targetBounds),
                   Animation.AnimationEasing.EaseOutExpo, _AnimationTime);
                anim.AnimationCompleted += new EventHandler(AnimationCompleted);
                anim.Start();
                _CurrentAnimation = anim;
            }
            else
                this.Bounds = targetBounds;
            if (_SlideOutButtonVisible && _SlideOutButton != null)
            {
                DisposeSlideOutButton();
            }
        }
        internal void AbortAnimation()
        {
            Animation.Animation anim = _CurrentAnimation;
            _CurrentAnimation = null;
            if (anim != null)
            {
                anim.Stop();
                anim.Dispose();
                //Console.WriteLine("{0} Animation Forcefully Stopped", DateTime.Now);
            }
        }
        private bool _IsWaiting = false;
        /// 
        /// Waits for current slide animation to finish and then returns control.
        /// 
        public void WaitForCurrentAnimationFinish()
        {
            if (_IsWaiting) return;
            _IsWaiting = true;
            try
            {
                if (_CurrentAnimation == null) return;
                DateTime start = DateTime.Now;
                while (_CurrentAnimation != null)
                {
                    Application.DoEvents();
                    if (DateTime.Now.Subtract(start).TotalMilliseconds > _AnimationTime * 1.5f)
                    {
                        AbortAnimation();
                        break;
                    }
                }
            }
            finally
            {
                _IsWaiting = false;
            }
        }
        private void DisposeSlideOutButton()
        {
            if (_SlideOutButton == null) return;
            Control parent = _SlideOutButton.Parent;
            if (parent != null)
            {
                parent.Resize -= ParentResize;
                parent.Controls.Remove(_SlideOutButton);
            }
            _SlideOutButton.Dispose();
            _SlideOutButton = null;
        }
        private Animation.Animation _CurrentAnimation = null;
        /// 
        /// Slides panel out of the view.
        /// 
        private void SlideOutOfView()
        {
            Animation.Animation current = _CurrentAnimation;
            if (current != null)
            {
                current.Stop();
                WaitForCurrentAnimationFinish();
            }
            
            Rectangle bounds = this.Bounds;
            Rectangle targetBounds = GetSlideOutBounds();
            if (_AnimationTime > 0 && this.Visible)
            {
                _IsAnimating = true;
                //BarFunctions.AnimateControl(this, true, _AnimationTime, bounds, targetBounds);
                Animation.AnimationRectangle anim = new DevComponents.DotNetBar.Animation.AnimationRectangle(
                    new Animation.AnimationRequest(this, "Bounds", bounds, targetBounds),
                    Animation.AnimationEasing.EaseOutExpo, _AnimationTime);
                anim.AnimationCompleted += new EventHandler(AnimationCompleted);
                anim.Start();
                _CurrentAnimation = anim;
                //this.Visible = false;
            }
            else
                this.Bounds = targetBounds;
            _OpenBounds = bounds;
            if (_SlideOutButtonVisible)
                CreateSlideOutButton();
        }
        private bool _IsAnimating = false;
        internal bool IsAnimating
        {
            get
            {
                return _IsAnimating;
            }
        }
        private void AnimationCompleted(object sender, EventArgs e)
        {
            //Console.WriteLine("{0} Animation Completed", DateTime.Now);
            Animation.Animation anim = _CurrentAnimation;
            _CurrentAnimation = null;
            if (anim != null)
                anim.Dispose();
            if (!_IsOpen)
            {
                this.Visible = false;
            }
            _IsAnimating = false;
        }
        public void SlideOutOfViewSilent(Control parent)
        {
            _OpenBounds = this.Bounds;
            this.Bounds = GetSlideOutBounds(parent);
            _IsOpen = false;
        }
        private SliderButton _SlideOutButton = null;
        private void CreateSlideOutButton()
        {
            DisposeSlideOutButton();
            Control parent = this.Parent;
            if (parent == null) return;
            _SlideOutButton = new SliderButton(this);
            if (_SlideOutButtonStyle != null)
                _SlideOutButton.Style = _SlideOutButtonStyle;
            else
                _SlideOutButton.Style = new ElementStyle(DevComponents.DotNetBar.Rendering.ElementStyleClassKeys.SlideOutButtonKey);
            Size activeSlideOutSize = Dpi.Size(_SlideOutActiveButtonSize);
            Size slideOutSize = Dpi.Size(_SlideOutButtonSize);
            if (_SlideSide == eSlideSide.Top || _SlideSide == eSlideSide.Bottom) // Flip width/height
            {
                activeSlideOutSize = new Size(activeSlideOutSize.Height, activeSlideOutSize.Width);
                slideOutSize = new Size(slideOutSize.Height, slideOutSize.Width);
            }
            _SlideOutButton.ActiveSliderSize = activeSlideOutSize;
            _SlideOutButton.SliderSize = slideOutSize;
            _SlideOutButton.Symbol = _Symbol;
            _SlideOutButton.SymbolColor = _SymbolColor;
            _SlideOutButton.SymbolSet = _SymbolSet;
            _SlideOutButton.SymbolSize = _SymbolSize;
            parent.Controls.Add(_SlideOutButton);
            parent.Controls.SetChildIndex(_SlideOutButton, 0);
            parent.Resize += ParentResize;
        }
        private void ParentResize(object sender, EventArgs e)
        {
            if (_SlideOutButton != null) _SlideOutButton.UpdatePosition();
        }
        private Rectangle GetSlideOutBounds()
        {
            return GetSlideOutBounds(this.Parent);
        }
        private Rectangle GetSlideOutBounds(Control parent)
        {
            return GetSlideOutBounds(parent, this.Bounds);
        }
        private Rectangle GetSlideOutBounds(Control parent, Rectangle panelBounds)
        {
            if (parent == null) return panelBounds;
            Rectangle r = panelBounds;
            if (_SlideSide == eSlideSide.Left)
            {
                r.X = -r.Width;
            }
            else if (_SlideSide == eSlideSide.Right)
            {
                r.X = parent.Width;
            }
            else if (_SlideSide == eSlideSide.Top)
            {
                r.Y = -r.Height;
            }
            else if (_SlideSide == eSlideSide.Bottom)
            {
                r.Y = parent.Height;
            }
            return r;
        }
        private int _AnimationTime = DefaultAnimationTime;
        /// 
        /// Gets or sets the animation duration time in milliseconds. Setting this property to 0 will disable slide animation.
        /// 
        [DefaultValue(DefaultAnimationTime), Category("Behavior"), Description("Indicates animation duration time in milliseconds. Setting this property to 0 will disable slide 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"));
        }
        private bool _SlideOutButtonVisible = true;
        /// 
        /// Gets or sets whether slide out button is shown when panel is out of the view and which allows panel to be shown.
        /// 
        [DefaultValue(true), Category("Behavior"), Description("Indicates whether slide out button is shown when panel is out of the view and which allows panel to be shown.")]
        public bool SlideOutButtonVisible
        {
            get { return _SlideOutButtonVisible; }
            set
            {
                if (value != _SlideOutButtonVisible)
                {
                    bool oldValue = _SlideOutButtonVisible;
                    _SlideOutButtonVisible = value;
                    OnSlideOutButtonVisibleChanged(oldValue, value);
                }
            }
        }
        /// 
        /// Called when SlideOutButtonVisible property has changed.
        /// 
        /// Old property value
        /// New property value
        protected virtual void OnSlideOutButtonVisibleChanged(bool oldValue, bool newValue)
        {
            //OnPropertyChanged(new PropertyChangedEventArgs("SlideOutButtonVisible"));
            if (_IsOpen) return;
            if (newValue)
                CreateSlideOutButton();
            else
                DisposeSlideOutButton();
        }
        private Size _SlideOutButtonSize = SliderButton.DefaultSliderSize;
        /// 
        /// Gets or sets the slide-out buttton size in default state.
        /// 
        [Category("Appearance"), Description("Indicates slide-out buttton size in default state.")]
        public Size SlideOutButtonSize
        {
            get { return _SlideOutButtonSize; }
            set
            {
                if (value != _SlideOutButtonSize)
                {
                    Size oldValue = _SlideOutButtonSize;
                    _SlideOutButtonSize = value;
                    OnSlideOutButtonSizeChanged(oldValue, value);
                }
            }
        }
        /// 
        /// Called when SlideOutButtonSize property has changed.
        /// 
        /// Old property value
        /// New property value
        protected virtual void OnSlideOutButtonSizeChanged(Size oldValue, Size newValue)
        {
            //OnPropertyChanged(new PropertyChangedEventArgs("SlideOutButtonSize"));
            if (_SlideOutButton != null)
            {
                _SlideOutButton.SliderSize = newValue;
                _SlideOutButton.UpdatePosition();
            }
        }
        /// 
        /// Returns whether property should be serialized.
        /// 
        public bool ShouldSerializeSlideOutButtonSize()
        {
            return _SlideOutButtonSize != SliderButton.DefaultSliderSize;
        }
        /// 
        /// Resets property to default value.
        /// 
        [EditorBrowsable(EditorBrowsableState.Never)]
        public void ResetSlideOutButtonSize()
        {
            SlideOutButtonSize = SliderButton.DefaultSliderSize;
        }
        private Size _SlideOutActiveButtonSize = SliderButton.DefaultActiveSliderSize;
        /// 
        /// Gets or sets active (mouse over) slide out button size.
        /// 
        [Category("Appearance"), Description("Indicates active (mouse over) slide out button size.")]
        public Size SlideOutActiveButtonSize
        {
            get { return _SlideOutActiveButtonSize; }
            set
            {
                if (value != _SlideOutActiveButtonSize)
                {
                    Size oldValue = _SlideOutActiveButtonSize;
                    _SlideOutActiveButtonSize = value;
                    OnSlideOutActiveButtonSizeChanged(oldValue, value);
                }
            }
        }
        /// 
        /// Called when SlideOutActiveButtonSize property has changed.
        /// 
        /// Old property value
        /// New property value
        protected virtual void OnSlideOutActiveButtonSizeChanged(Size oldValue, Size newValue)
        {
            //OnPropertyChanged(new PropertyChangedEventArgs("SlideOutActiveButtonSize"));
            if (_SlideOutButton != null)
            {
                _SlideOutButton.ActiveSliderSize = newValue;
                _SlideOutButton.UpdatePosition();
            }
        }
        /// 
        /// Returns whether property should be serialized.
        /// 
        [EditorBrowsable(EditorBrowsableState.Never)]
        public bool ShouldSerializeSlideOutActiveButtonSize()
        {
            return _SlideOutActiveButtonSize != SliderButton.DefaultActiveSliderSize;
        }
        /// 
        /// Resets property to default value.
        /// 
        [EditorBrowsable(EditorBrowsableState.Never)]
        public void ResetSlideOutActiveButtonSize()
        {
            SlideOutActiveButtonSize = SliderButton.DefaultActiveSliderSize;
        }
        private ElementStyle _SlideOutButtonStyle = null;
        /// 
        /// Gets or sets slide-out button style.
        /// 
        [DefaultValue(null), Category("Appearance"), Description("Indicates slide-out button style.")]
        public ElementStyle SlideOutButtonStyle
        {
            get { return _SlideOutButtonStyle; }
            set
            {
                if (value != _SlideOutButtonStyle)
                {
                    ElementStyle oldValue = _SlideOutButtonStyle;
                    _SlideOutButtonStyle = value;
                    OnSlideOutButtonStyleChanged(oldValue, value);
                }
            }
        }
        /// 
        /// Called when SlideOutButtonStyle property has changed.
        /// 
        /// Old property value
        /// New property value
        protected virtual void OnSlideOutButtonStyleChanged(ElementStyle oldValue, ElementStyle newValue)
        {
            if (newValue != null)
            {
                if (_SlideOutButton != null)
                    _SlideOutButton.Style = newValue;
            }
            //OnPropertyChanged(new PropertyChangedEventArgs("SlideOutButtonStyle"));
        }
        private bool _CenterContent = false;
        /// 
        /// Gets or sets whether panel centers the Controls inside of it. Default value is false.
        /// 
        internal bool CenterContent
        {
            get { return _CenterContent; }
            set
            {
                if (value != _CenterContent)
                {
                    bool oldValue = _CenterContent;
                    _CenterContent = value;
                    OnCenterContentChanged(oldValue, value);
                }
            }
        }
        /// 
        /// Called when CenterContent property has changed.
        /// 
        /// Old property value
        /// New property value
        protected virtual void OnCenterContentChanged(bool oldValue, bool newValue)
        {
            //OnPropertyChanged(new PropertyChangedEventArgs("CenterContent"));
            if (newValue)
                CenterControls();
        }
        protected override void OnResize(EventArgs e)
        {
            if (_CenterContent)
                CenterControls();
            base.OnResize(e);
        }
        protected override void OnControlAdded(ControlEventArgs e)
        {
            if (_CenterContent)
                CenterControl(e.Control);
            base.OnControlAdded(e);
        }
        private void CenterControl(Control item)
        {
            Point loc = new Point(Math.Max(0, (this.Width - item.Width) / 2), Math.Max(0, (this.Height - item.Height) / 2));
            if (item.Location != loc)
                item.Location = loc;
        }
        private void CenterControls()
        {
            foreach (Control item in this.Controls)
            {
                CenterControl(item);
            }
        }
        [Browsable(true), DesignerSerializationVisibility(DesignerSerializationVisibility.Visible), EditorBrowsable(EditorBrowsableState.Always), Bindable(true)]
        public override string Text
        {
            get
            {
                return base.Text;
            }
            set
            {
                base.Text = value;
            }
        }
        private Color _SymbolColor = Color.Empty;
        /// 
        /// Gets or sets the color of the Symbol displayed on slideout button.
        /// 
        [Category("Appearance"), Description("Indicates color of the Symbol displayed on slideout button.")]
        public Color SymbolColor
        {
            get { return _SymbolColor; }
            set { _SymbolColor = value; this.Invalidate(); }
        }
        /// 
        /// Gets whether property should be serialized.
        /// 
        [EditorBrowsable(EditorBrowsableState.Never)]
        public bool ShouldSerializeSymbolColor()
        {
            return !_SymbolColor.IsEmpty;
        }
        /// 
        /// Resets property to its default value.
        /// 
        [EditorBrowsable(EditorBrowsableState.Never)]
        public void ResetSymbolColor()
        {
            this.SymbolColor = Color.Empty;
        }
        /// 
        /// Gets the realized symbol string.
        /// 
        [Browsable(false)]
        public string SymbolRealized
        {
            get { return _SymbolRealized; }
        }
        private string _Symbol = "", _SymbolRealized = "";
        /// 
        /// Indicates the symbol displayed on face of the slideout button.
        /// 
        [DefaultValue(""), Category("Appearance"), Description("Indicates the symbol displayed on face of the slideout button.")]
        [Editor("DevComponents.DotNetBar.Design.SymbolTypeEditor, DevComponents.DotNetBar.Design, Version=14.1.0.37, Culture=neutral,  PublicKeyToken=90f470f34c89ccaf", typeof(System.Drawing.Design.UITypeEditor))]
        public string Symbol
        {
            get { return _Symbol; }
            set
            {
                if (value != _Symbol)
                {
                    string oldValue = _Symbol;
                    _Symbol = value;
                    OnSymbolChanged(oldValue, value);
                }
            }
        }
        /// 
        /// Called when Symbol property has changed.
        /// 
        /// Old property value
        /// New property value
        protected virtual void OnSymbolChanged(string oldValue, string newValue)
        {
            if (string.IsNullOrEmpty(newValue))
                _SymbolRealized = "";
            else
                _SymbolRealized = Symbols.GetSymbol(newValue);
            this.Invalidate();
        }
        private eSymbolSet _SymbolSet = eSymbolSet.Awesome;
        /// 
        /// Gets or sets the symbol set used to represent the Symbol.
        /// 
        [Browsable(false), DefaultValue(eSymbolSet.Awesome)]
        public eSymbolSet SymbolSet
        {
            get { return _SymbolSet; }
            set
            {
                if (_SymbolSet != value)
                {
                    eSymbolSet oldValue = _SymbolSet;
                    _SymbolSet = value;
                    OnSymbolSetChanged(oldValue, value);
                }
            }
        }
        /// 
        /// Called when SymbolSet property value changes.
        /// 
        /// Indciates old value
        /// Indicates new value
        protected virtual void OnSymbolSetChanged(eSymbolSet oldValue, eSymbolSet newValue)
        {
            this.Invalidate();
            this.Refresh();
        }
        private float _SymbolSize = 10f;
        /// 
        /// Indicates the size of the symbol in points.
        /// 
        [DefaultValue(10f), Category("Appearance"), Description("Indicates the size of the symbol in points.")]
        public float SymbolSize
        {
            get { return _SymbolSize; }
            set
            {
                if (value != _SymbolSize)
                {
                    float oldValue = _SymbolSize;
                    _SymbolSize = value;
                    OnSymbolSizeChanged(oldValue, value);
                }
            }
        }
        /// 
        /// Called when SymbolSize property has changed.
        /// 
        /// Old property value
        /// New property value
        protected virtual void OnSymbolSizeChanged(float oldValue, float newValue)
        {
            this.Invalidate();
        }
        #endregion
    }
    /// 
    /// Defines the side SlidePanel slides into.
    /// 
    public enum eSlideSide
    {
        Left,
        Right,
        Top,
        Bottom
    }
}