/*
 * 本代码受中华人民共和国著作权法保护，作者仅授权下载代码之人在学习和交流范围内
 * 自由使用与修改代码；欲将代码用于商业用途的，请与作者联系。
 * 使用本代码请保留此处信息。作者联系方式：ping3108@163.com, 欢迎进行技术交流
 */

using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
using System.ComponentModel;

using MGdu.WinFormUI.MyGraphics;
//忽略没有注释警告
#pragma warning disable 1591
namespace MGdu.WinFormUI
{
    [ToolboxItem(true), DefaultEvent("ValueChanged")]
    public class GMTrackBar : GMBarControlBase, IGMControl
    {

        #region 构造函数及初始化

        static GMTrackBar()
        {
            EVENT_VALUECHANGED = new object();
            EVENT_PAINTBUTTON = new object();
            EVENT_PAINTMAINLINE = new object();
        }

        public GMTrackBar()
        {            
            ThumeButtonIni();
            presenter = new CutePointAndValuePresenter();
            base.TabStop = true;
        }        

        private void ThumeButtonIni()
        {
            thumbButton = new WLButton(this);
            thumbButton.RestrictedBounds = false;
            SetThumbButtonInfo();
        }

        private void SetThumbButtonInfo()
        {
            thumbButton.ColorTable = ThumbButtonColorTable;
            thumbButton.BorderType = ThumbButtonBorderType;
        }

        #endregion

        #region 控件事件相关

        private static readonly object EVENT_VALUECHANGED;
        private static readonly object EVENT_PAINTMAINLINE;
        private static readonly object EVENT_PAINTBUTTON;

        /// <summary>
        /// 控件Value值发生变化后引发
        /// </summary>
        [Description("控件Value值发生变化后引发")]
        public event EventHandler ValueChanged
        {
            add
            {
                base.Events.AddHandler(EVENT_VALUECHANGED, value);
            }
            remove
            {
                base.Events.RemoveHandler(EVENT_VALUECHANGED, value);
            }
        }

        /// <summary>
        /// 控件绘制MainLine前引发, 可以设置Cancel参数来取消默认的绘制
        /// </summary>
        [Description("控件绘制MainLine前引发, 可以设置Cancel参数来取消默认的绘制, 请把参数转化为 GMTrackBarPaintEventArgs")]
        public event PaintEventHandler BeforePaintMainLine
        {
            add
            {
                base.Events.AddHandler(EVENT_PAINTMAINLINE, value);
            }
            remove
            {
                base.Events.RemoveHandler(EVENT_PAINTMAINLINE, value);
            }
        }

        /// <summary>
        /// 控件绘制Button前引发
        /// </summary>
        [Description("控件绘制ThumbButton前引发, 可以设置Cancel参数来取消默认的绘制, 请把参数转化为 GMTrackBarPaintEventArgs")]
        public event PaintEventHandler BeforePaintButton
        {
            add
            {
                base.Events.AddHandler(EVENT_PAINTBUTTON, value);
            }
            remove
            {
                base.Events.RemoveHandler(EVENT_PAINTBUTTON, value);
            }
        }


        /// <summary>
        /// 引发 ValueChanged 事件
        /// </summary>
        protected virtual void OnValueChanged(EventArgs e)
        {
            EventHandler handle = (EventHandler)base.Events[EVENT_VALUECHANGED];
            if (handle != null)
            {
                handle(this, e);
            }
        }

        /// <summary>
        /// 引发 BeforePaintMainLine 事件
        /// </summary>
        protected virtual void OnBeforePaintMainLine(PaintEventArgs e)
        {
            PaintEventHandler handle = (PaintEventHandler)base.Events[EVENT_PAINTMAINLINE];
            if (handle != null)
            {
                handle(this, e);
            }
        }

        /// <summary>
        /// 引发 BeforePaintButton 事件
        /// </summary>
        protected virtual void OnBeforePaintButton(PaintEventArgs e)
        {
            PaintEventHandler handle = (PaintEventHandler)base.Events[EVENT_PAINTBUTTON];
            if (handle != null)
            {
                handle(this, e);
            }
        }

        #endregion

        #region 公开的属性

        int _value = 0;
        int _minimum = 0;
        int _maximum = 10;
        int _smallChange = 1;
        int _largeChange = 5;
        int _tickFrequency = 1;
        TickStyle _tickSide = TickStyle.None;
        Orientation _barOrientation = Orientation.Horizontal;
        GMTrackBarThemeBase _xtheme = null;
        bool _drawFocusRect = false;
        bool _autoSize = true;

        [DefaultValue(0)]
        public int Value
        {
            get
            {
                return _value;
            }
            set
            {
                if (_value != value)
                {
                    if (value > _maximum)
                        value = _maximum;
                    if (value < _minimum)
                        value = _minimum;

                    _value = value;
                    ResetButtonPositionFromValue();
                    OnValueChanged(EventArgs.Empty);
                }
            }
        }

        [DefaultValue(0)]
        public int Minimum
        {
            get
            {
                return _minimum;
            }
            set
            {
                if (_minimum != value)
                {
                    if (value > _maximum)
                        _maximum = value;
                    if (_value < value)
                        _value = value;
                    _minimum = value;
                    UpdatePresenterAndRedraw();
                }
            }
        }

        [DefaultValue(10)]
        public int Maximum
        {
            get
            {
                return _maximum;
            }
            set
            {
                if (_maximum != value)
                {
                    if (value < _minimum)
                        _minimum = value;
                    if (_value > value)
                        _value = value;
                    _maximum = value;
                    UpdatePresenterAndRedraw();
                }
            }
        }

        [DefaultValue(1)]
        public int SmallChange
        {
            get
            {
                return _smallChange;
            }
            set
            {
                _smallChange = value < 1 ? 1 : value;
            }
        }

        [DefaultValue(5)]
        public int LargeChange
        {
            get
            {
                return _largeChange;
            }
            set
            {
                _largeChange = value < 1 ? 1 : value;
            }
        }

        [DefaultValue(1)]
        public int TickFrequency
        {
            get
            {
                return _tickFrequency;
            }
            set
            {
                if (_tickFrequency != value)
                {
                    _tickFrequency = value < 1 ? 1 : value;
                    if (_tickSide != TickStyle.None)
                        Invalidate();
                }
            }
        }

        [DefaultValue(0)]
        public TickStyle TickSide
        {
            get
            {
                return _tickSide;
            }
            set
            {
                if (_tickSide != value)
                {
                    _tickSide = value;
                    DoWhenResize();                    
                    Invalidate();
                }
            }
        }

        [DefaultValue(0)]
        public Orientation BarOrientation
        {
            get
            {
                return _barOrientation;
            }
            set
            {
                if (_barOrientation != value)
                {
                    _barOrientation = value;
                    DoWhenBarOrientationChanged();
                }
            }
        }

        [DefaultValue(true), Browsable(true)]
        public override bool AutoSize
        {
            get
            {
                return _autoSize;
            }
            set
            {
                if (_autoSize != value)
                {
                    _autoSize = value;

                    if (_barOrientation == Orientation.Horizontal)
                    {
                        base.SetStyle(ControlStyles.FixedHeight, _autoSize);
                        base.SetStyle(ControlStyles.FixedWidth, false);
                    }
                    else
                    {
                        base.SetStyle(ControlStyles.FixedWidth, _autoSize);
                        base.SetStyle(ControlStyles.FixedHeight, false);
                    }

                    DoWhenAutoSizeChanged();
                }
            }
        }

        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
        public GMTrackBarThemeBase XTheme
        {
            get
            {
                return _xtheme;
            }
            set
            {
                if (_xtheme != value)
                {
                    _xtheme = value;
                    DoWhenXThemeChanged();
                }
            }
        }

        [DefaultValue(false)]
        public bool DrawFocusRect
        {
            get
            {
                return _drawFocusRect;
            }
            set
            {
                _drawFocusRect = value;
                Invalidate();
            }
        }

        #endregion

        #region 公开的方法

        /// <summary>
        /// 将Value值加一个LargeChange量
        /// </summary>
        public void DoLargeUp()
        {
            ValueAdd(LargeChange);
        }

        /// <summary>
        /// 将Value值减一个LargeChange量
        /// </summary>
        public void DoLargeDown()
        {
            ValueAdd(-LargeChange);
        }

        /// <summary>
        /// 将Value值加一个SmallChange量
        /// </summary>
        public void DoSmallUp()
        {
            ValueAdd(SmallChange);
        }

        /// <summary>
        /// 将Value值减一个SmallChange量
        /// </summary>
        public void DoSmallDown()
        {
            ValueAdd(-SmallChange);
        }

        #endregion

        #region 可通过XTheme配置的内部 look and feel 属性

        protected virtual int ButtonLength1
        {
            get
            {
                if (_xtheme == null)
                    return 8;
                else
                    return _xtheme.ButtonLength1;
            }
        }

        protected virtual int ButtonLength2
        {
            get
            {
                if (_xtheme == null)
                    return 18;
                else
                    return _xtheme.ButtonLength2;
            }
        }

        protected virtual int ButtonOutterSpace1
        {
            get
            {
                if (_xtheme == null)
                    return 4;
                else
                    return _xtheme.ButtonOutterSpace1;
            }
        }

        protected virtual int ButtonOutterSpace2
        {
            get
            {
                if (_xtheme == null)
                    return 2;
                else
                    return _xtheme.ButtonOutterSpace2;
            }
        }

        protected virtual int MainLineLength
        {
            get
            {
                if (_xtheme == null)
                    return 4;
                else
                    return _xtheme.MainLineLength;
            }
        }

        protected virtual int TickLineLength
        {
            get
            {
                if (_xtheme == null)
                    return 3;
                else
                    return _xtheme.TickLineLength;
            }
        }

        protected virtual int TickLineSpaceWithButton
        {
            get
            {
                if (_xtheme == null)
                    return 2;
                else
                    return _xtheme.TickLineSpaceWithButton;
            }
        }

        protected virtual int TickLineSpaceWithBorder
        {
            get
            {
                if (_xtheme == null)
                    return 6;
                else
                    return _xtheme.TickLineSpaceWithBorder;
            }
        }

        protected virtual int BorderWidth
        {
            get
            {
                if (_xtheme == null)
                    return 1;
                else
                    return _xtheme.BorderWidth;
            }
        }

        protected virtual bool DrawBackground
        {
            get
            {
                if (_xtheme == null)
                    return true;
                else
                    return _xtheme.DrawBackground;
            }
        }

        protected virtual bool DrawBorder
        {
            get
            {
                if (_xtheme == null)
                    return false;
                else
                    return _xtheme.DrawBorder;
            }
        }

        protected virtual bool DrawInnerBorder
        {
            get
            {
                if (_xtheme == null)
                    return false;
                else
                    return _xtheme.DrawInnerBorder;
            }
        }

        protected virtual Color GMBackColor
        {
            get
            {
                if (_xtheme == null)
                    return Color.Transparent;
                else
                    return _xtheme.BackColor;
            }
        }

        protected virtual Color BorderColor
        {
            get
            {
                if (_xtheme == null)
                    return Color.LightGray;
                else
                    return _xtheme.BorderColor;
            }
        }

        protected virtual Color InnerBorderColor
        {
            get
            {
                if (_xtheme == null)
                    return Color.Empty;
                else
                    return _xtheme.InnerBorderColor;
            }
        }        

        protected virtual Color TickLineColor
        {
            get
            {
                if (_xtheme == null)
                    return Color.FromArgb(185,185,185);
                else
                    return _xtheme.TickLineColor;
            }
        }

        protected virtual Color MainLineRange1BackColor
        {
            get
            {
                if (_xtheme == null)
                    return Color.LightGray;
                else
                    return _xtheme.MainLineRange1BackColor;
            }
        }

        protected virtual Color MainLineRange2BackColor
        {
            get
            {
                if (_xtheme == null)
                    return Color.White;
                else
                    return _xtheme.MainLineRange2BackColor;
            }
        }

        protected virtual Color MainLineBorderColor
        {
            get
            {
                if (_xtheme == null)
                    return Color.FromArgb(0,114,198);
                else
                    return _xtheme.MainLineBorderColor;
            }
        }

        protected virtual bool MainLineDrawBorder
        {
            get
            {
                if (_xtheme == null)
                    return true;
                else
                    return _xtheme.MainLineDrawBorder;
            }
        }

        protected virtual int MainLineRadius
        {
            get
            {
                if (_xtheme == null)
                    return 0;
                else
                    return _xtheme.MainLineRadius;
            }
        }

        protected virtual ButtonColorTable ThumbButtonColorTable
        {
            get
            {
                if (_xtheme == null)
                    return DefaultThumbColor();
                else
                    return _xtheme.ThumbButtonColorTable;
            }
        }

        protected virtual ButtonBorderType ThumbButtonBorderType
        {
            get
            {
                if (_xtheme == null)
                    return ButtonBorderType.Rectangle;
                else
                    return _xtheme.ThumbButtonBorderType;
            }
        }       

        protected virtual ButtonColorTable DefaultThumbColor()
        {
            ButtonColorTable table = new ButtonColorTable();
            table.BorderColorNormal = table.BorderColorHover = table.BorderColorPressed =
                Color.FromArgb(0, 114, 198);
            table.BackColorNormal = Color.White;
            table.BackColorHover = Color.FromArgb(177, 214, 240);
            table.BackColorPressed = ColorHelper.GetDarkerColor(table.BackColorHover, 16);
            return table;
        }

        #endregion

        #region 内部使用的变量

        CutePointAndValuePresenter presenter;

        bool isMouseDownInButton;

        WLButton thumbButton;

        #endregion

        #region 内部函数, 属性

        int _buttonCurrentPositionDot;

        private int ButtonCurrentPositionDot
        {
            get
            {
                return _buttonCurrentPositionDot;
            }
            set
            {
                if (_buttonCurrentPositionDot != value)
                {
                    if (_barOrientation == Orientation.Horizontal)
                    {
                        if (value < BBDot)
                            _buttonCurrentPositionDot = BBDot;
                        else if (value > BMDot)
                            _buttonCurrentPositionDot = BMDot;
                        else
                            _buttonCurrentPositionDot = value;
                    }
                    else
                    {
                        if (value > BBDot)
                            _buttonCurrentPositionDot = BBDot;
                        else if (value < BMDot)
                            _buttonCurrentPositionDot = BMDot;
                        else
                            _buttonCurrentPositionDot = value;
                    }
                    if (thumbButton != null)
                        thumbButton.Bounds = BTR;
                }
            }
        }

        private void DoWhenResize()
        {
            SetFittedSize();
            thumbButton.Bounds = BTR;
            UpdatePresenterAndRedraw();
        }

        private void DoWhenMouseDownInMainLine(Point p)
        {            
            DoWhenMouseMoveInButton(p);
        }

        private void DoWhenMouseMoveInButton(Point p)
        {
            int begin = BBDot;
            int max = BMDot;

            if (_barOrientation == Orientation.Horizontal)
                p.Offset(-ButtonLength1 / 2, 0);
            else
                p.Offset(0, -ButtonLength1 / 2);

            if (_barOrientation == Orientation.Horizontal)
            {
                int pos = p.X;
                if (pos < (begin - 100) || pos > (max + 100))
                    return;
                if (pos < begin)
                    pos = begin;
                if (pos > max)
                    pos = max;

                int v1, v2;
                int locIndex = pos - begin;
                presenter.GetValueIndexFromPointIndex(locIndex, out v1, out v2);
                if (_value < v1 || _value > v2)
                {
                    if (pos == begin)
                        Value = Minimum + v1;
                    else if (pos == max)
                        Value = Minimum + v2;
                    else
                        Value = Minimum + (v1 + v2) / 2;
                }
            }
            else
            {
                int pos = p.Y;
                if (pos < (max - 100) || pos > (begin + 100))
                    return;
                if (pos < max)
                    pos = max;
                if (pos > begin)
                    pos = begin;
                pos = (begin + max) - pos;
                int v1, v2;
                int locIndex = pos - max;
                presenter.GetValueIndexFromPointIndex(locIndex, out v1, out v2);
                if (_value < v1 || _value > v2)
                {
                    if (pos == begin)
                        Value = Minimum + v2;
                    else if (pos == max)
                        Value = Minimum + v1;
                    else
                        Value = Minimum + (v1 + v2) / 2;
                }
            }
        }

        private void UpdatePresenterAndRedraw()
        {
            int pointCount = RemainPointCount;
            int valueCount = Maximum - Minimum + 1;
            if (pointCount != presenter.PointCount || valueCount != presenter.ValueCount)
            {                
                presenter.SetPointAndValueCount(pointCount, valueCount);

                ResetButtonPositionFromValue();
                Invalidate();
            }
        }

        private void ResetButtonPositionFromValue()
        {
            int newPos = GetDotFromValue(Value);
            if (newPos != ButtonCurrentPositionDot)
            {
                ButtonCurrentPositionDot = newPos;
                Invalidate();
            }
        }

        private int GetDotFromValue(int value)
        {
            int p1, p2;
            int valueIndex = value - Minimum;
            presenter.GetPointIndexFromValueIndex(valueIndex, out p1, out p2);

            int targetPos;
            if (value == Minimum)
                targetPos = p1;
            else if (value == Maximum)
                targetPos = p2;
            else
                targetPos = (p1 + p2) / 2;
            
            if (_barOrientation == Orientation.Horizontal)
                targetPos += BBDot;
            else
            {
                targetPos += BMDot;
                targetPos = (BMDot + BBDot) - targetPos;
            }
            return targetPos;
        }

        private void ValueAdd(int amount)
        {            
            if (Value == Minimum && amount <= 0)
                return;
            if (Value == Maximum && amount >= 0)
                return;
            int v = Value;
            v += amount;
            if (v < Minimum)
                v = Minimum;
            if (v > Maximum)
                v = Maximum;
            Value = v;
        }

        private void SetFittedSize()
        {
            if (AutoSize)
            {
                int bestLen = BestFittedLength;
                if (_barOrientation == Orientation.Horizontal)
                {
                    if (base.Height != bestLen)
                        base.Height = bestLen;
                }
                else
                {
                    if (base.Width != bestLen)
                        base.Width = bestLen;
                }
            }
        }

        private void DoWhenBarOrientationChanged()
        {
            var newSize = new Size(base.Height, base.Width);

            base.Size = newSize;            
            ResetButtonPositionFromValue();
            UpdatePresenterAndRedraw();
            Invalidate();
        }

        private void DoWhenXThemeChanged()
        {
            SetThumbButtonInfo();
            DoWhenResize();
            Invalidate();
        }

        private void DoWhenAutoSizeChanged()
        {
            SetFittedSize();       
        }

        #endregion

        #region 内部绘图

        protected virtual void OnPaintGMTrackbar(Graphics g)
        {
            if (DrawBackground)
                RenderBackground(g);
            if (TickSide != TickStyle.None)
                RenderTickLines(g);
            RenderMainLine(g);
            RenderButton(g);
            if (DrawBorder)
                RenderBorders(g);
            RenderFocusRect(g); 
        }

        private void RenderBackground(Graphics g)
        {
            using (SolidBrush sb = new SolidBrush(GMBackColor))
            {
                g.FillRectangle(sb, ClientRectangle);
            }
        }

        private void RenderFocusRect(Graphics g)
        {
            if (DrawFocusRect && base.Focused)
            {
                Rectangle rect = ClientRectangle;
                rect.Width--;
                rect.Height--;
                rect.Inflate(-1, -1);
                using (Pen p = new Pen(Color.Black))
                {
                    p.DashStyle = DashStyle.Dot;
                    g.DrawRectangle(p, rect);
                }
            }
        }

        private void RenderBorders(Graphics g)
        {
            Rectangle rect = ClientRectangle;
            rect.Width--;
            rect.Height--;
            using (Pen p = new Pen(BorderColor))
            {
                g.DrawRectangle(p, rect);
            }
            if (DrawInnerBorder)
            {
                rect.Inflate(-1, -1);
                using (Pen p2 = new Pen(InnerBorderColor))
                {
                    g.DrawRectangle(p2, rect);
                }
            }
        }

        private void RenderMainLine(Graphics g)
        {
            GMTrackBarPaintEventArgs e = new GMTrackBarPaintEventArgs(g,
                MainLineRectToDraw, MainLineRange1Rect, MainLineRange2Rect);
            OnBeforePaintMainLine(e);

            if (!e.Cancel)
            {
                RenderMainLine(g, MainLineRange1Rect, MainLineRange1BackColor);
                RenderMainLine(g, MainLineRange2Rect, MainLineRange2BackColor);
            }
            e.Dispose();
        }

        private void RenderMainLine(Graphics g, Rectangle rect, Color backColor)
        {
            SmoothingMode newMode = (MainLineRadius > 0) ? SmoothingMode.AntiAlias : SmoothingMode.HighSpeed;
            RoundStyle style = (MainLineRadius > 0) ? RoundStyle.All : RoundStyle.None;

            using (NewSmoothModeGraphics ng = new NewSmoothModeGraphics(g, newMode))
            {
                using (SolidBrush sb = new SolidBrush(backColor))
                {
                    using (GraphicsPath path = GraphicsPathHelper.CreateRoundedRect(
                        rect, MainLineRadius, style, false))
                    {
                        g.FillPath(sb, path);
                    }
                }
                if (MainLineDrawBorder)
                {
                    using (Pen p = new Pen(MainLineBorderColor))
                    {
                        using (GraphicsPath path = GraphicsPathHelper.CreateRoundedRect(
                            rect, MainLineRadius, style, true))
                        {
                            g.DrawPath(p, path);
                        }
                    }
                }
            }
        }

        private void RenderButton(Graphics g)
        {
            GMTrackBarPaintEventArgs e = new GMTrackBarPaintEventArgs(g, BTR);
            e.BtnState = thumbButton.State;
            OnBeforePaintButton(e);

            if (!e.Cancel)
                thumbButton.DrawButton(g);
            e.Dispose();
        }

        private void RenderTickLines(Graphics g)
        {
            Pen p = new Pen(TickLineColor);
            
            if (TickSide == TickStyle.Both || TickSide == TickStyle.TopLeft)
            {
                if (_barOrientation == Orientation.Horizontal)
                    RenderTickLinesTopBottom(g, p, TickLinesRect1, true);
                else
                    RenderTickLinesLeftRight(g, p, TickLinesRect1, true);
            }
            if (TickSide == TickStyle.Both || TickSide == TickStyle.BottomRight)
            {
                if (_barOrientation == Orientation.Horizontal)
                    RenderTickLinesTopBottom(g, p, TickLinesRect2, false);
                else
                    RenderTickLinesLeftRight(g, p,TickLinesRect2, false);
            }

            p.Dispose();
        }

        private void RenderTickLinesTopBottom(Graphics g, Pen p, Rectangle rect, bool isTop)
        {            
            int y1 = rect.Top;
            int y2 = rect.Bottom - 1;
            int prex = -1;
            int addExtra = ButtonLength1 / 2;

            int xfirst = GetDotFromValue(Minimum) + addExtra;
            int xlast = GetDotFromValue(Maximum) + addExtra;            

            for (int i = Minimum; i <= Maximum; i += TickFrequency)
            {
                int x = GetDotFromValue(i) + addExtra;

                if (x == xfirst || x == (xfirst + 1) || x == xlast || x == (xlast - 1))
                    continue;
                if (x == prex || x == (prex + 1))
                    continue;

                g.DrawLine(p, x, y1, x, y2);
                prex = x;
            }
            
            // render the first and last points with a little longer line
            if (isTop)
                y1 -= 1;
            else
                y2 += 1;
            g.DrawLine(p, xfirst, y1, xfirst, y2);
            g.DrawLine(p, xlast, y1, xlast, y2);
        }        

        private void RenderTickLinesLeftRight(Graphics g, Pen p, Rectangle rect, bool isLeft)
        {            
            int x1 = rect.Left;
            int x2 = rect.Right - 1;
            int prey = -1;
            int addExtra = ButtonLength1 / 2;

            int yfirst = GetDotFromValue(Minimum) + addExtra;
            int ylast = GetDotFromValue(Maximum) + addExtra;

            for (int i = Minimum; i <= Maximum; i += TickFrequency)
            {
                int y = GetDotFromValue(i) + addExtra;

                if (y == yfirst || y == yfirst - 1 || y == ylast || y == ylast + 1)
                    continue;
                if (y == prey || y == (prey - 1))
                    continue;

                g.DrawLine(p, x1, y, x2, y);
                prey = y;
            }

            // render the first and last points
            if (isLeft)
                x1 -= 1;
            else
                x2 += 1;
            g.DrawLine(p, x1, yfirst, x2, yfirst);
            g.DrawLine(p, x1, ylast, x2, ylast);
        }        

        #endregion

        #region 计算而得的内部只读属性

        private int RemainPointCount
        {
            get
            {
                int count = 0;

                if (_barOrientation == Orientation.Horizontal)
                {
                    count = Size.Width - BorderWidth * 2 - ButtonOutterSpace1 * 2
                        - ButtonLength1 + 1;
                }
                else
                {
                    count = Size.Height - BorderWidth * 2 - ButtonOutterSpace1 * 2
                        - ButtonLength1 + 1;
                }

                return count;
            }
        }

        private int BBDot
        {
            get
            {
                if (_barOrientation == Orientation.Horizontal)
                    return BorderWidth + ButtonOutterSpace1;
                else
                    return Size.Height - 1 - BorderWidth - ButtonOutterSpace1 - ButtonLength1 + 1;
            }
        }

        private int BMDot
        {
            get
            {
                if (_barOrientation == Orientation.Horizontal)
                    return (Size.Width - 1) - BorderWidth - ButtonOutterSpace1 - ButtonLength1 + 1;
                else
                    return BorderWidth + ButtonOutterSpace1;
            }
        }

        private int MainLineBegionDot
        {
            get
            {
                return BBDot + ButtonLength1 / 2;
            }
        }

        private int MainLineEndDot
        {
            get
            {
                return BMDot + ButtonLength1 / 2;
            }
        }

        private Rectangle BTR
        {
            get
            {

                if (_barOrientation == Orientation.Horizontal)
                {
                    int x = ButtonCurrentPositionDot;
                    int y = 0;
                    if (TickSide == TickStyle.Both || TickSide == TickStyle.TopLeft)
                        y = BorderWidth + TickLineSpaceWithBorder + TickLineLength +
                            TickLineSpaceWithButton + ButtonOutterSpace2;
                    else
                        y = BorderWidth + ButtonOutterSpace2;
                    return new Rectangle(x, y, ButtonLength1, ButtonLength2);
                }
                else
                {
                    int x = 0;
                    if (TickSide == TickStyle.Both || TickSide == TickStyle.TopLeft)
                        x = BorderWidth + TickLineSpaceWithBorder + TickLineLength +
                            TickLineSpaceWithButton + ButtonOutterSpace2;
                    else
                        x = BorderWidth + ButtonOutterSpace2;
                    int y = ButtonCurrentPositionDot;
                    return new Rectangle(x, y, ButtonLength2, ButtonLength1);
                }                
            }
        }

        private Rectangle MainLineRect
        {
            get
            {
                Rectangle btnRect = BTR;

                if (_barOrientation == Orientation.Horizontal)
                {
                    int h = MainLineLength;
                    int x = MainLineBegionDot;
                    int y = btnRect.Y + (btnRect.Height - h) / 2;
                    if ((btnRect.Height - h) % 2 != 0)
                        y++;
                    int w = MainLineEndDot - MainLineBegionDot + 1;
                    return new Rectangle(x, y, w, h);
                }
                else
                {
                    int w = MainLineLength;
                    int x = btnRect.X + (btnRect.Width - w) / 2;
                    if ((btnRect.Width - w) % 2 != 0)
                        x++;
                    int y = MainLineEndDot;
                    int h = MainLineBegionDot - y + 1;
                    return new Rectangle(x, y, w, h);
                }
            }
        }

        private Rectangle MainLineRectToDraw
        {
            get
            {
                Rectangle rect = MainLineRect;

                if (_barOrientation == Orientation.Horizontal)
                    rect.Inflate(2, 0);
                else
                    rect.Inflate(0, 2);

                return rect;
            }
        }

        private Rectangle MainLineRectHitTest
        {
            get
            {
                Rectangle rect = MainLineRect;
                int diff = (ButtonLength2 - MainLineLength) / 2;
                if (_barOrientation == Orientation.Horizontal)
                    rect.Inflate(0, diff);
                else
                    rect.Inflate(diff, 0);
                return rect;
            }
        }

        private Rectangle TickLinesRect1
        {
            get
            {
                int lineBegin = MainLineBegionDot;
                int lineEnd  = MainLineEndDot;

                if (_barOrientation == Orientation.Horizontal)
                {
                    int x = lineBegin;
                    int y = BorderWidth + TickLineSpaceWithBorder;
                    int w = lineEnd - lineBegin + 1;
                    int h = 0;
                    if (TickSide == TickStyle.Both || TickSide == TickStyle.TopLeft)
                        h = TickLineLength;
                    return new Rectangle(x, y, w, h);
                }
                else
                {
                    int x = BorderWidth + TickLineSpaceWithBorder;
                    int y = lineEnd;
                    int h = lineBegin - lineEnd + 1;
                    int w = 0;
                    if (TickSide == TickStyle.Both || TickSide == TickStyle.TopLeft)
                        w = TickLineLength;
                    return new Rectangle(x, y, w, h);
                }                
            }
        }

        private Rectangle TickLinesRect2
        {
            get
            {
                Rectangle btnRect = BTR;

                if (_barOrientation == Orientation.Horizontal)
                {
                    int x = MainLineBegionDot;
                    int y = btnRect.Bottom + ButtonOutterSpace2 + TickLineSpaceWithButton;
                    int w = MainLineEndDot - MainLineBegionDot + 1;
                    int h = 0;
                    if(TickSide == TickStyle.Both || TickSide == TickStyle.BottomRight)
                        h = TickLineLength;
                    return new Rectangle(x, y, w, h);
                }
                else
                {
                    int x = btnRect.Right + ButtonOutterSpace2 + TickLineSpaceWithButton;
                    int y = MainLineEndDot;
                    int h = MainLineBegionDot - MainLineEndDot + 1;
                    int w = 0;
                    if (TickSide == TickStyle.Both || TickSide == TickStyle.BottomRight)
                        w = TickLineLength;
                    return new Rectangle(x, y, w, h);
                }                
            }
        }

        private Rectangle MainLineRange1Rect
        {
            get
            {
                Rectangle rect = MainLineRectToDraw;
                int pos = ButtonCurrentPositionDot + ButtonLength1 / 2;

                if (_barOrientation == Orientation.Horizontal)
                {
                    rect.Width = pos - 1 - rect.X + 1;
                    return rect;
                }
                else
                {
                    return new Rectangle(rect.X, pos + 1,
                        rect.Width, rect.Bottom - pos - 1);
                }                
            }
        }

        private Rectangle MainLineRange2Rect
        {
            get
            {
                Rectangle rect = MainLineRectToDraw;
                int pos = ButtonCurrentPositionDot + ButtonLength1 / 2;

                if (_barOrientation == Orientation.Horizontal)
                {
                    return new Rectangle(pos + 1, rect.Y, rect.Right - pos - 1, rect.Height);
                }
                else
                {
                    rect.Height = pos - 1 - rect.Y + 1;
                    return rect;
                }                
            }
        }

        private int BestFittedLength
        {
            get
            {
                int value = BorderWidth * 2 + ButtonOutterSpace2 * 2 + ButtonLength2;
                if (TickSide == TickStyle.Both || TickSide == TickStyle.TopLeft)
                    value += TickLineSpaceWithBorder + TickLineLength + TickLineSpaceWithButton;
                if (TickSide == TickStyle.Both || TickSide == TickStyle.BottomRight)
                    value += TickLineSpaceWithBorder + TickLineLength + TickLineSpaceWithButton;
                return value;
            }
        }

        #endregion
        
        #region IGMControl实现

        [Browsable(false),EditorBrowsable(EditorBrowsableState.Never)]
        public GMControlType ControlType
        {
            get { return GMControlType.TrackBar; }
        }

        #endregion

        #region 重写基类方法

        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);
            OnPaintGMTrackbar(e.Graphics);
        }

        protected override void OnResize(EventArgs e)
        {
            base.OnResize(e);
            DoWhenResize();
        }

        protected override void OnMouseDown(MouseEventArgs e)
        {
            base.OnMouseDown(e);            

            if (e.Button == System.Windows.Forms.MouseButtons.Left)
            {
                if (TabStop)
                    base.Focus();

                if (BTR.Contains(e.Location))
                {
                    isMouseDownInButton = true;
                    thumbButton.State = GMButtonState.Pressed;
                }
                else if(MainLineRectHitTest.Contains(e.Location))
                {
                    DoWhenMouseDownInMainLine(e.Location);
                }
            }
        }

        protected override void OnMouseUp(MouseEventArgs e)
        {
            base.OnMouseUp(e);
            if (e.Button == System.Windows.Forms.MouseButtons.Left)
            {
                isMouseDownInButton = false;
                if (BTR.Contains(e.Location))
                    thumbButton.State = GMButtonState.Hover;
                else
                    thumbButton.State = GMButtonState.Normal;
            }
        }

        protected override void OnMouseLeave(EventArgs e)
        {
            base.OnMouseLeave(e);
            thumbButton.State = GMButtonState.Normal;
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);
            if (isMouseDownInButton)
            {
                DoWhenMouseMoveInButton(e.Location);
            }
            if (!base.Capture)
            {
                if(BTR.Contains(e.Location))
                    thumbButton.State = GMButtonState.Hover;
                else
                    thumbButton.State = GMButtonState.Normal;
            }
        }

        protected override bool IsInputKey(Keys keyData)
        {
            if ((keyData & Keys.Alt) == Keys.Alt)
            {
                return false;
            }
            switch ((keyData & Keys.KeyCode))
            {
                case Keys.PageUp:
                case Keys.PageDown:
                case Keys.End:
                case Keys.Home:

                case Keys.Down:
                case Keys.Up:
                case Keys.Left:
                case Keys.Right:
                    return true;
            }
            return base.IsInputKey(keyData);
        }

        protected override void OnEnter(EventArgs e)
        {
            base.OnEnter(e);
            if (DrawFocusRect)
                base.Invalidate();
        }

        protected override void OnLeave(EventArgs e)
        {
            base.OnLeave(e);
            if (DrawFocusRect)
                base.Invalidate();
        }

        protected override void OnKeyDown(KeyEventArgs e)
        {            
            switch (e.KeyData)
            {
                case Keys.Left:
                case Keys.Up:
                    if (_barOrientation == Orientation.Horizontal)
                        DoSmallDown();
                    else
                        DoSmallUp();
                    break;
                case Keys.Down:
                case Keys.Right:
                    if (_barOrientation == Orientation.Horizontal)
                        DoSmallUp();
                    else
                        DoSmallDown();
                    break;
                case Keys.PageUp:
                    if (_barOrientation == Orientation.Horizontal)
                        DoLargeDown();
                    else
                        DoLargeUp();
                    break;
                case Keys.PageDown:
                    if (_barOrientation == Orientation.Horizontal)
                        DoLargeUp();
                    else
                        DoLargeDown();
                    break;
                case Keys.Home:
                    if (_barOrientation == Orientation.Horizontal)
                        Value = Minimum;
                    else
                        Value = Maximum;
                    break;
                case Keys.End:
                    if (_barOrientation == Orientation.Horizontal)
                        Value = Maximum;
                    else
                        Value = Minimum;
                    break;
            }
            base.OnKeyDown(e);
        }

        #endregion

        #region 内部类

        public class GMTrackBarPaintEventArgs : PaintEventArgs
        {
            public Rectangle MainLineRange1Rect { get; private set; }
            public Rectangle MainLineRange2Rect { get; private set; }
            public bool Cancel { get; set; }
            public GMButtonState BtnState { get; set; }

            public GMTrackBarPaintEventArgs(Graphics g, Rectangle clipRect)
                : this(g, clipRect, Rectangle.Empty, Rectangle.Empty)
            {

            }

            public GMTrackBarPaintEventArgs(Graphics g, Rectangle mainLineRect,
                Rectangle range1Rect, Rectangle range2Rect) : base(g, mainLineRect)
            {
                MainLineRange1Rect = range1Rect;
                MainLineRange2Rect = range2Rect;
                Cancel = false;
                BtnState = GMButtonState.Normal;
            }
        }

        #endregion

    }
}
