using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;
using System.Windows.Forms;
using DevComponents.DotNetBar.ScrollBar;
using System.Drawing;
using System.Runtime.InteropServices;
namespace DevComponents.DotNetBar.Controls
{
    /// 
    /// Represents control which handles scroll-bars.
    /// 
    [ToolboxItem(false)]
    public class ScrollbarControl : ControlWithBackgroundStyle
    {
        private const int ScrollPositionUpdateDelay = 400;
        private HScrollBarAdv _HScrollBar = null;
        private VScrollBarAdv _VScrollBar = null;
        private Control _Thumb = null;
        /// 
        /// Initializes a new instance of the ScrollbarControl class.
        /// 
        public ScrollbarControl()
        {
            this.SetStyle(ControlStyles.UserPaint | 
                ControlStyles.AllPaintingInWmPaint |
                ControlStyles.Opaque | 
                ControlStyles.ResizeRedraw |
                DisplayHelp.DoubleBufferFlag, true);
            
            _HScrollBar = new HScrollBarAdv();
            _HScrollBar.Visible = false;
            _HScrollBar.Appearance = _ScrollBarAppearance;
            _HScrollBar.Scroll += HScrollBarScroll;
            _VScrollBar = new VScrollBarAdv();
            _VScrollBar.Visible = false;
            _VScrollBar.Appearance = _ScrollBarAppearance;
            _VScrollBar.Scroll += VScrollBarScroll;
            this.Controls.Add(_HScrollBar);
            this.Controls.Add(_VScrollBar);
            _Thumb = new Control();
            _Thumb.Visible = false;
            _Thumb.BackColor = SystemColors.Window;
            this.Controls.Add(_Thumb);
        }
        private eScrollBarAppearance _ScrollBarAppearance = eScrollBarAppearance.Default;
        /// 
        /// Gets or sets the scroll-bar visual style.
        /// 
        [DefaultValue(eScrollBarAppearance.Default), Category("Appearance"), Description("Gets or sets the scroll-bar visual style.")]
        public eScrollBarAppearance ScrollBarAppearance
        {
            get { return _ScrollBarAppearance; }
            set
            {
                _ScrollBarAppearance = value;
                OnScrollBarAppearanceChanged();
            }
        }
        private void OnScrollBarAppearanceChanged()
        {
            if (_VScrollBar != null) _VScrollBar.Appearance = _ScrollBarAppearance;
            if (_HScrollBar != null) _HScrollBar.Appearance = _ScrollBarAppearance;
        }
        private void UpdateScrollOverrideControlBounds()
        {
            if (_ScrollOverrideControl != null)
                _ScrollOverrideControl.Bounds = GetScrollOverrideControlBounds();
        }
        protected override void OnResize(EventArgs e)
        {
            UpdateScrollOverrideControlBounds();
            UpdateScrollBars();
            base.OnResize(e);
        }
        protected override void OnVisualPropertyChanged()
        {
            base.OnVisualPropertyChanged();
            UpdateScrollOverrideControlBounds();
            UpdateScrollBars();
        }
        protected override void ScaleControl(SizeF factor, BoundsSpecified specified)
        {
            base.ScaleControl(factor, specified);
            UpdateScrollOverrideControlBounds();
        }
        private bool _UpdatingScrollbars = false;
        protected virtual void UpdateScrollBarsDelayed()
        {
            InvokeDelayed(new MethodInvoker(delegate { UpdateScrollBars(); }), ScrollPositionUpdateDelay);
        }
        protected virtual void UpdateScrollBars()
        {
            if (!IsHandleCreated || _UpdatingScrollbars) return;
            _UpdatingScrollbars = true;
            try
            {
                Control scrollOverrideControl = _ScrollOverrideControl;
                if (scrollOverrideControl == null)
                {
                    if (_HScrollBar.Visible) _HScrollBar.Visible = false;
                    if (_VScrollBar.Visible) _VScrollBar.Visible = false;
                    return;
                }
                WinApi.SCROLLBARINFO psbi = new WinApi.SCROLLBARINFO();
                psbi.cbSize = Marshal.SizeOf(psbi);
                WinApi.GetScrollBarInfo(scrollOverrideControl.Handle, (uint)WinApi.eObjectId.OBJID_VSCROLL, ref psbi);
                if (psbi.rgstate[0] != (int)WinApi.eStateFlags.STATE_SYSTEM_INVISIBLE)
                {
                    Rectangle vsBounds = psbi.rcScrollBar.ToRectangle();
                    vsBounds.Location = this.PointToClient(vsBounds.Location);
                    Rectangle scrollCountrolBounds = _ScrollOverrideControl.Bounds;
                    if (!scrollCountrolBounds.Contains(vsBounds))
                    {
                        // We need to guess bounds for best performance and appearance
                        if (vsBounds.Right > scrollCountrolBounds.Right)
                        {
                            vsBounds.X = scrollCountrolBounds.Right - vsBounds.Width;
                        }
                        else if (vsBounds.X < scrollCountrolBounds.X)
                        {
                            vsBounds.X = 0;
                        }
                    }
                    if (_VScrollBar.Bounds != vsBounds)
                    {
                        _VScrollBar.Bounds = vsBounds;
                        UpdateVerticalScrollBarValues();
                    }
                    if (!_VScrollBar.Visible)
                    {
                        _VScrollBar.Visible = true;
                        _VScrollBar.Refresh();
                        InvokeDelayed(new MethodInvoker(delegate { UpdateVerticalScrollBarValues(); }), ScrollPositionUpdateDelay);
                    }
                    if (psbi.rgstate[0] == (int)WinApi.eStateFlags.STATE_SYSTEM_UNAVAILABLE)
                        _VScrollBar.Enabled = false;
                    else if (!_VScrollBar.Enabled)
                        _VScrollBar.Enabled = true;
                    //Console.WriteLine("VscrollBar Bounds detection {0}", vsBounds);
                }
                else if (_VScrollBar.Visible)
                    _VScrollBar.Visible = false;
                psbi = new WinApi.SCROLLBARINFO();
                psbi.cbSize = Marshal.SizeOf(psbi);
                WinApi.GetScrollBarInfo(scrollOverrideControl.Handle, (uint)WinApi.eObjectId.OBJID_HSCROLL, ref psbi);
                if (psbi.rgstate[0] != (int)WinApi.eStateFlags.STATE_SYSTEM_INVISIBLE)
                {
                    Rectangle hsBounds = psbi.rcScrollBar.ToRectangle();
                    hsBounds.Location = this.PointToClient(hsBounds.Location);
                    Rectangle scrollCountrolBounds = _ScrollOverrideControl.Bounds;
                    if (!scrollCountrolBounds.Contains(hsBounds))
                    {
                        // We need to guess bounds for best performance and appearance
                        if (hsBounds.Bottom > scrollCountrolBounds.Bottom)
                        {
                            hsBounds.Y = scrollCountrolBounds.Bottom - hsBounds.Height;
                        }
                    }
                    if (_VScrollBar.Visible && hsBounds.Width == scrollCountrolBounds.Width)
                        hsBounds.Width -= _VScrollBar.Width;
                    if (_HScrollBar.Bounds != hsBounds)
                    {
                        _HScrollBar.Bounds = hsBounds;
                        UpdateHorizontalScrollBarValues();
                    }
                    if (!_HScrollBar.Visible)
                    {
                        _HScrollBar.Visible = true;
                        _HScrollBar.Refresh();
                        InvokeDelayed(new MethodInvoker(delegate { UpdateHorizontalScrollBarValues(); }), ScrollPositionUpdateDelay);
                    }
                    if (psbi.rgstate[0] == (int)WinApi.eStateFlags.STATE_SYSTEM_UNAVAILABLE)
                        _HScrollBar.Enabled = false;
                    else if (!_HScrollBar.Enabled)
                        _HScrollBar.Enabled = true;
                }
                else if (_HScrollBar.Visible)
                    _HScrollBar.Visible = false;
                if (_HScrollBar.Visible && _VScrollBar.Visible)
                {
                    _Thumb.Bounds = new Rectangle(_VScrollBar.Left, _VScrollBar.Bounds.Bottom, _VScrollBar.Width, _HScrollBar.Height);
                    _Thumb.Visible = true;
                }
                else
                {
                    _Thumb.Visible = false;
                }
            }
            finally
            {
                _UpdatingScrollbars = false;
            }
        }
        private System.Drawing.Rectangle GetScrollOverrideControlBounds()
        {
            return GetContentRectangle();
        }
        
        private Control _ScrollOverrideControl = null;
        internal Control ScrollOverrideControl
        {
            get { return _ScrollOverrideControl; }
            set
            {
                if (value != _ScrollOverrideControl)
                {
                    if (!(value is IScrollBarOverrideSupport))
                        throw new ArgumentException("ScrollOverrideControl must implement IScrollBarOverrideSupport interface.");
                    Control oldValue = _ScrollOverrideControl;
                    _ScrollOverrideControl = value;
                    OnScrollOverrideControlChanged(oldValue, value);
                }
            }
        }
        /// 
        /// Called when ScrollOverrideControl property has changed.
        /// 
        /// Old property value
        /// New property value
        protected virtual void OnScrollOverrideControlChanged(Control oldValue, Control newValue)
        {
            if (oldValue != null)
            {
                IScrollBarOverrideSupport sbo = (IScrollBarOverrideSupport)newValue;
                sbo.NonClientSizeChanged -= NonClientSizeChanged;
                sbo.ScrollBarValueChanged -= ControlScrollBarValueChanged;
                sbo.ControlMoved -= ScrollControlMoved;
                this.Controls.Remove(oldValue);
            }
            if (newValue != null)
            {
                IScrollBarOverrideSupport sbo = (IScrollBarOverrideSupport)newValue;
                sbo.NonClientSizeChanged += NonClientSizeChanged;
                sbo.ScrollBarValueChanged += ControlScrollBarValueChanged;
                sbo.ControlMoved += ScrollControlMoved;
                this.Controls.Add(newValue);
            }
            UpdateScrollOverrideControlBounds();
 
            if (IsHandleCreated)
            {
                //InitializeWindowOverride();
                UpdateScrollBars();
            }
        }
        void ScrollControlMoved(object sender, EventArgs e)
        {
            UpdateScrollOverrideControlBounds();
        }
        private bool _InternalVScrollPositionUpdated = false;
        private void VScrollBarScroll(object sender, ScrollEventArgs e)
        {
            if (e.NewValue == e.OldValue && e.Type != ScrollEventType.EndScroll) return;
            _InternalVScrollPositionUpdated = true;
            WinApi.SendMessage(_ScrollOverrideControl.Handle, (int)WinApi.WindowsMessages.WM_VSCROLL, WinApi.CreateLParam(MapVScrollType(e.Type), e.NewValue), IntPtr.Zero);
            _InternalVScrollPositionUpdated = false;
            if (e.Type == ScrollEventType.EndScroll)
                UpdateVerticalScrollBarValues();
        }
        private bool _InternalHScrollPositionUpdated = false;
        private void HScrollBarScroll(object sender, ScrollEventArgs e)
        {
            if (e.NewValue == e.OldValue && e.Type != ScrollEventType.EndScroll) return;
            _InternalHScrollPositionUpdated = true;
            //Console.WriteLine("{0} Setting Sys_HScrollBar value {1}, {2}", DateTime.Now, e.NewValue, e.Type);
            WinApi.SendMessage(_ScrollOverrideControl.Handle, (int)WinApi.WindowsMessages.WM_HSCROLL, WinApi.CreateLParam(MapHScrollType(e.Type), e.NewValue), IntPtr.Zero);
            _InternalHScrollPositionUpdated = false;
            if (e.Type == ScrollEventType.EndScroll)
                UpdateHorizontalScrollBarValues();
        }
        private static int MapVScrollType(ScrollEventType type)
        {
            if (type == ScrollEventType.EndScroll)
                return (int)WinApi.ScrollBarCommands.SB_ENDSCROLL;
            else if (type == ScrollEventType.First)
                return (int)WinApi.ScrollBarCommands.SB_TOP;
            else if (type == ScrollEventType.LargeDecrement)
                return (int)WinApi.ScrollBarCommands.SB_PAGEUP;
            else if (type == ScrollEventType.LargeIncrement)
                return (int)WinApi.ScrollBarCommands.SB_PAGEDOWN;
            else if (type == ScrollEventType.Last)
                return (int)WinApi.ScrollBarCommands.SB_BOTTOM;
            else if (type == ScrollEventType.SmallDecrement)
                return (int)WinApi.ScrollBarCommands.SB_LINEUP;
            else if (type == ScrollEventType.SmallIncrement)
                return (int)WinApi.ScrollBarCommands.SB_LINEDOWN;
            else if (type == ScrollEventType.ThumbPosition)
                return (int)WinApi.ScrollBarCommands.SB_THUMBPOSITION;
            else if (type == ScrollEventType.ThumbTrack)
                return (int)WinApi.ScrollBarCommands.SB_THUMBTRACK;
            return 0;
        }
        private static int MapHScrollType(ScrollEventType type)
        {
            if (type == ScrollEventType.EndScroll)
                return (int)WinApi.ScrollBarCommands.SB_ENDSCROLL;
            else if (type == ScrollEventType.First)
                return (int)WinApi.ScrollBarCommands.SB_LEFT;
            else if (type == ScrollEventType.LargeDecrement)
                return (int)WinApi.ScrollBarCommands.SB_PAGELEFT;
            else if (type == ScrollEventType.LargeIncrement)
                return (int)WinApi.ScrollBarCommands.SB_PAGERIGHT;
            else if (type == ScrollEventType.Last)
                return (int)WinApi.ScrollBarCommands.SB_RIGHT;
            else if (type == ScrollEventType.SmallDecrement)
                return (int)WinApi.ScrollBarCommands.SB_LINELEFT;
            else if (type == ScrollEventType.SmallIncrement)
                return (int)WinApi.ScrollBarCommands.SB_LINERIGHT;
            else if (type == ScrollEventType.ThumbPosition)
                return (int)WinApi.ScrollBarCommands.SB_THUMBPOSITION;
            else if (type == ScrollEventType.ThumbTrack)
                return (int)WinApi.ScrollBarCommands.SB_THUMBTRACK;
            return 0;
        }
        private void UpdateVerticalScrollBarValues()
        {
            if (_InternalVScrollPositionUpdated) return;
            WinApi.SCROLLINFO scrollInfo = new WinApi.SCROLLINFO();
            scrollInfo.cbSize = Marshal.SizeOf(scrollInfo);
            scrollInfo.fMask = WinApi.ScrollInfoMask.SIF_POS | WinApi.ScrollInfoMask.SIF_RANGE | WinApi.ScrollInfoMask.SIF_PAGE;
            if (WinApi.GetScrollInfo(_ScrollOverrideControl.Handle, WinApi.SBOrientation.SB_VERT, ref scrollInfo))
            {
                //Console.WriteLine("{0}   TRACKPOS={1}", DateTime.Now, scrollInfo.nTrackPos);
                if (_VScrollBar.Minimum != scrollInfo.nMin)
                    _VScrollBar.Minimum = scrollInfo.nMin;
                if (_VScrollBar.Maximum != scrollInfo.nMax)
                    _VScrollBar.Maximum = scrollInfo.nMax;
                if (_VScrollBar.Value != scrollInfo.nPos)
                    _VScrollBar.Value = scrollInfo.nPos;
                if (_VScrollBar.LargeChange != scrollInfo.nPage)
                    _VScrollBar.LargeChange = scrollInfo.nPage;
            }
        }
        private void UpdateHorizontalScrollBarValues()
        {
            if (_InternalHScrollPositionUpdated) return;
            WinApi.SCROLLINFO scrollInfo = new WinApi.SCROLLINFO();
            scrollInfo.cbSize = Marshal.SizeOf(scrollInfo);
            scrollInfo.fMask = WinApi.ScrollInfoMask.SIF_POS | WinApi.ScrollInfoMask.SIF_RANGE | WinApi.ScrollInfoMask.SIF_PAGE | WinApi.ScrollInfoMask.SIF_TRACKPOS;
            if (WinApi.GetScrollInfo(_ScrollOverrideControl.Handle, WinApi.SBOrientation.SB_HORZ, ref scrollInfo))
            {
                //Console.WriteLine("{0}   TRACKPOS={1}", DateTime.Now, scrollInfo);
                if (_HScrollBar.Minimum != scrollInfo.nMin)
                    _HScrollBar.Minimum = scrollInfo.nMin;
                if (_HScrollBar.Maximum != scrollInfo.nMax)
                    _HScrollBar.Maximum = scrollInfo.nMax;
                if (_HScrollBar.Value != scrollInfo.nPos)
                    _HScrollBar.Value = scrollInfo.nPos;
                if (_HScrollBar.LargeChange != scrollInfo.nPage)
                    _HScrollBar.LargeChange = scrollInfo.nPage;
            }
        }
        private void ControlScrollBarValueChanged(object sender, ScrollValueChangedEventArgs e)
        {
            if ((e.ScrollChange & eScrollBarScrollChange.Vertical) == eScrollBarScrollChange.Vertical)
            {
                UpdateVerticalScrollBarValues();
            }
            if ((e.ScrollChange & eScrollBarScrollChange.MouseWheel) == eScrollBarScrollChange.MouseWheel)
            {
                UpdateVerticalScrollBarValues();
                InvokeDelayed(new MethodInvoker(delegate { UpdateVerticalScrollBarValues(); }), ScrollPositionUpdateDelay);
            }
            if ((e.ScrollChange & eScrollBarScrollChange.Horizontal) == eScrollBarScrollChange.Horizontal)
            {
                UpdateHorizontalScrollBarValues();
            }
            UpdateScrollBars();
        }
        private void NonClientSizeChanged(object sender, EventArgs e)
        {
            UpdateScrollBars();
        }
        protected override void OnHandleCreated(EventArgs e)
        {
            UpdateScrollBars();
            base.OnHandleCreated(e);
        }
        #region Child Control WndProc Override
        internal static eScrollBarScrollChange MapMessageToScrollChange(int msg)
        {
            if (msg == (int)WinApi.WindowsMessages.WM_VSCROLL)
                return eScrollBarScrollChange.Vertical;
            else if (msg == (int)WinApi.WindowsMessages.WM_HSCROLL)
                return eScrollBarScrollChange.Horizontal;
            else if (msg == (int)WinApi.WindowsMessages.WM_MOUSEWHEEL)
                return eScrollBarScrollChange.MouseWheel;
            throw new ArgumentException("Message not recognized msg=" + msg.ToString());
        }
        #endregion
        #region Invoke Delayed
        protected void InvokeDelayed(MethodInvoker method)
        {
            InvokeDelayed(method, 10);
        }
        protected void InvokeDelayed(MethodInvoker method, int delayInterval)
        {
            if (delayInterval <= 0) { method.Invoke(); return; }
            Timer delayedInvokeTimer = new Timer();
            delayedInvokeTimer = new Timer();
            delayedInvokeTimer.Tag = method;
            delayedInvokeTimer.Interval = delayInterval;
            delayedInvokeTimer.Tick += new EventHandler(DelayedInvokeTimerTick);
            delayedInvokeTimer.Start();
        }
        void DelayedInvokeTimerTick(object sender, EventArgs e)
        {
            Timer timer = (Timer)sender;
            MethodInvoker method = (MethodInvoker)timer.Tag;
            timer.Stop();
            timer.Dispose();
            if (!this.IsDisposed)
                method.Invoke();
        }
        #endregion
    }
    public interface IScrollBarOverrideSupport
    {
        /// 
        /// Should be fired when non-client size of the control changes, i.e. when WM_NCCALCSIZE message is received.
        /// 
        event EventHandler NonClientSizeChanged;
        /// 
        /// Should be fired when scroll-bar value on child control changes.
        /// 
        event ScrollValueChangedHandler ScrollBarValueChanged;
        /// 
        /// Should be fired when control receives WM_MOVE message.
        /// 
        event EventHandler ControlMoved;
        /// 
        /// Gets whether control is in design mode.
        /// 
        bool DesignMode { get;}
    }
    /// 
    /// Defines arguments for IScrollBarOverrideSupport.ScrollBarValueChanged event.
    /// 
    public class ScrollValueChangedEventArgs : System.EventArgs
    {
        /// 
        /// Initializes a new instance of the ScrollValueChangedEventArgs class.
        /// 
        /// 
        public ScrollValueChangedEventArgs(eScrollBarScrollChange scrollChange)
        {
            ScrollChange = scrollChange;
        }
        public readonly eScrollBarScrollChange ScrollChange;
    }
    /// 
    /// Defines information for IScrollBarOverrideSupport.ScrollBarValueChanged event.
    /// 
    [Flags()]
    public enum eScrollBarScrollChange
    {
        Vertical = 1,
        Horizontal = 2,
        MouseWheel = 4
    }
    /// 
    /// Defines delegate for IScrollBarOverrideSupport.ScrollBarValueChanged event.
    /// 
    /// Sender.
    /// Event arguments
    public delegate void ScrollValueChangedHandler(object sender, ScrollValueChangedEventArgs e);
}