using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Windows.Forms;
using System.Diagnostics;

namespace Sheng.Winform.Controls
{
    /// <summary>
    /// 默认布局引擎，以类似微软ListView的方式按行排列项
    /// </summary>
    public class ShengListViewLayoutManager
    {
        #region 常量

        /// <summary>
        /// 框选的最短开始长度
        /// </summary>
        private const int SELECTION_TOLERANCE = 5;

        /// <summary>
        /// 框选时滚动条的自动滚动速度
        /// </summary>
        private const int AUTOSCROLL_VALUE = 10;

        #endregion

        #region 私有成员

        private ShengListView _imageListView;

        //滚动条放到布局引擎中来定义，这样可以实现不同布局引擎完全不同的布局方式
        //比如为图像分组显示的引擎可以同时显示多个水平滚动条
        private readonly VScrollBar _vScrollBar = new VScrollBar();

        private int ScrollBarWidth
        {
            get { return _vScrollBar.Width; }
        }

        /// <summary>
        /// 用于鼠标框选时，框出了控件中项的显示范围时，自动滚动滚动条
        /// </summary>
        private readonly Timer _scrollTimer = new Timer();

        /// <summary>
        /// 自动滚动时，滚动值
        /// </summary>
        private int _autoScrollValue = 0;

        /// <summary>
        /// 是否处于框选状态中
        /// </summary>
        internal bool MouseSelecting { get; private set; }

        /// <summary>
        /// 内框offset，内框offset是绝对相对于滚动条的
        /// </summary>
        private int _itemsAreaOffset = 0;

        /// <summary>
        /// 整个可显示项的边界的offset，包括上下padding部分
        /// </summary>
        private int ItemsBoundsOffset
        {
            get
            {
                int offSet = _itemsAreaOffset - (ItemsArea.Location.Y - ItemsBounds.Location.Y);
                if (offSet < 0)
                    offSet = 0;
                return offSet;
            }
        }

        /// <summary>
        /// 鼠标按下时项区域边界的offset，即 _itemsAreaOffset
        /// 用于框选时，跨越可视部队画框
        /// </summary>
        private int _mouseItemsAreaOffset = 0;

        ///// <summary>
        ///// 当前所能显示的最大列数
        ///// </summary>
        //private int _columnCount;

        /// <summary>
        /// 当前所能显示的最大行数
        /// </summary>
        private int _rowCount;

        #endregion

        #region 公开属性

        private ShengListViewRenderer _renderer;
        /// <summary>
        /// 
        /// </summary>
        protected ShengListViewRenderer Renderer
        {
            get { return _renderer; }
            set { _renderer = value; }
        }

        /// <summary>
        /// Gets whether the shift key is down.
        /// </summary>
        internal bool ShiftKey { get { return _imageListView.ShiftKey; } }
        /// <summary>
        /// Gets whether the control key is down.
        /// </summary>
        internal bool ControlKey { get { return _imageListView.ControlKey; } }

        internal bool Focused { get { return _imageListView.Focused; } }

        internal Rectangle SelectionRectangle { get; private set; }

        internal bool Suspend
        {
            get { return _imageListView.Suspend; }
        }

        internal string DisplayMember
        {
            get
            {
                return _imageListView.DisplayMember;
            }
        }

        //TODO:在出现滚动条后，计算错误
        //临时调用用，完成后改为internal
        private int _firstPartiallyVisible;
        /// <summary>
        /// 
        /// </summary>
        public int FirstPartiallyVisible { get { return _firstPartiallyVisible; } }

        private int _lastPartiallyVisible;
        /// <summary>
        /// 
        /// </summary>
        public int LastPartiallyVisible { get { return _lastPartiallyVisible; } }

        private int _firstVisible;
        /// <summary>
        /// 
        /// </summary>
        public int FirstVisible { get { return _firstVisible; } }

        private int _lastVisible;
        /// <summary>
        /// 
        /// </summary>
        public int LastVisible { get { return _lastVisible; } }

        /// <summary>
        /// 没有任何项
        /// </summary>
        public bool IsEmpty
        {
            get
            {
                return _imageListView.IsEmpty;
            }
        }

        /// <summary>
        /// 没有任何项处于可显示状态
        /// </summary>
        public bool NoneItemVisible
        {
            get
            {
                return _firstPartiallyVisible == -1 || _lastPartiallyVisible == -1;
            }
        }

        /// <summary>
        /// 整个控件区域
        /// </summary>
        public Rectangle ClientArea
        {
            get { return _imageListView.ClientRectangle; }
        }

        /// <summary>
        /// 整个可用于绘制项的可视区域
        /// 去除左右Padding部分，去除滚动条
        /// </summary>
        public Rectangle ItemsBounds
        {
            get
            {
                int scrollBarWidth = this.ScrollBarWidth;
                Rectangle clientArea = this.ClientArea;
                Padding padding = _imageListView.Padding;
                int x = padding.Left;
                int width = clientArea.Width - padding.Left - padding.Right - scrollBarWidth;

                Rectangle itemsBounds = new Rectangle(x, clientArea.Y, width, clientArea.Height);
                return itemsBounds;
            }
        }

        /// <summary>
        /// 用于绘制项的区域
        /// 考虑边距间隔大小和滚动条区域
        /// </summary>
        public Rectangle ItemsArea
        {
            //要考虑边距间隔大小和滚动条区域
            get
            {
                int scrollBarWidth = this.ScrollBarWidth;
                Rectangle clientArea = this.ClientArea;
                Padding padding = _imageListView.Padding;
                int x = padding.Left;
                int y = padding.Top;
                int width = clientArea.Width - padding.Left - padding.Right - scrollBarWidth;
                int height = clientArea.Height - padding.Top - padding.Bottom;

                //在最小化窗口后，clientArea的尺寸为0
                if (width < 0) width = 1;
                if (height < 0) height = 1;

                Rectangle itemsArea = new Rectangle(x, y, width, height);
                return itemsArea;
            }
        }

        //TODO:项的大小是不能固定死的，因为要考虑到操作系统的默认字体大小
        private int _itemHeight = 24;
        /// <summary>
        /// 项的尺寸
        /// 不放在ListView本身中定义而是放在LayoutManager中定义，是因为不同的布局方式
        /// 可能会是带长宽的Size做为itemSize，比如平铺的方式
        /// </summary>
        internal int ItemHeight
        {
            get { return _itemHeight; }
            set
            {
                _itemHeight = value;
                _itemHeightWithMargin = _itemHeight + _margin;
            }
        }

        private int _margin = 2;
        /// <summary>
        /// 项周围的边距
        /// </summary>
        public int Margin
        {
            get { return _margin; }
            set
            {
                _margin = value;
                _itemHeightWithMargin = _itemHeight + value;
            }
        }

        private int _itemHeightWithMargin;
        internal int ItemHeightWithMargin
        {
            get { return _itemHeightWithMargin; }
        }

        #endregion

        #region debug 临时调试用
        /// <summary>
        /// 
        /// </summary>
        public int StartRow { get; set; }
        /// <summary>
        /// 
        /// </summary>
        public int EndRow { get; set; }
        //public int StartCol { get; set; }
        //public int EndCol { get; set; }

        #endregion

        #region 构造
        /// <summary>
        /// 
        /// </summary>
        /// <param name="imageListView"></param>
        public ShengListViewLayoutManager(ShengListView imageListView)
        {
            _imageListView = imageListView;

            //_itemSize = new Size(ImageSize, ImageSize);
            _itemHeightWithMargin = _itemHeight + _margin;

            UpdateScrollBars();

            _vScrollBar.Dock = DockStyle.Right;
            _imageListView.Controls.Add(_vScrollBar);
            _vScrollBar.Scroll += new ScrollEventHandler(VScrollBar_Scroll);
            _vScrollBar.ValueChanged += new EventHandler(VScrollBar_ValueChanged);

            _scrollTimer.Interval = 20;
            _scrollTimer.Enabled = false;
            _scrollTimer.Tick += new EventHandler(ScrollTimer_Tick);

            //_renderer = new ListViewStandardRenderer(this);
            //_renderer = new ListViewRenderer(this);
            //_renderer = new ListViewDescriptiveRenderer(this);

            //_renderer.Theme = _imageListView.Theme;
        }

        #endregion

        #region 事件处理

        void VScrollBar_Scroll(object sender, ScrollEventArgs e)
        {
            //这里判断的原因是在鼠标操作滚动条时，即使只点一下，这里也会进来两次，原因不明
            if (_itemsAreaOffset != e.NewValue)
            {
                _itemsAreaOffset = e.NewValue;
            }
        }

        void VScrollBar_ValueChanged(object sender, EventArgs e)
        {
            //这里判断的原因是可能在 _vScrollBar_Scroll 事件里就赋值过了
            if (_itemsAreaOffset != _vScrollBar.Value)
            {
                _itemsAreaOffset = _vScrollBar.Value;
            }

            _imageListView.SuspendLayout();

            if (MouseSelecting)
            {
                //如果处于框选状态，在重新绘制控件之前需要计算新的 SelectionRectangle
                //因为SelectionRectangle一开始是在MouseMove中计算的，而当鼠标拉出控件边界时
                //滚动条会继续滚动，如果此时保持鼠标不动，就要靠这里计算 SelectionRectangle 了
                SelectionRectangle = CreateSelectionRectangle();
                SelectItemsByRectangle(SelectionRectangle);
            }

            _imageListView.ResumeLayout(true);

            CalculateVisibleItemsRange();            
        }

        void ScrollTimer_Tick(object sender, EventArgs e)
        {
            //自动滚动之后，必须重新绘制控件
            //借助 _vScrollBar_ValueChanged 事件实现

            //另外，在自动滚动之后，光重绘还不行，必须重新计算 SelectionRectangle
            //SelectionRectangle 一开始是在 MouseMove 中计算的，如果在自动滚动的过程中不移动鼠标
            //那就靠 _vScrollBar_ValueChanged 计算了

            int scrollValue = _vScrollBar.Value + _autoScrollValue;
            if (scrollValue > _vScrollBar.Maximum)
                _vScrollBar.Value = _vScrollBar.Maximum;
            else if (scrollValue < 0)
                _vScrollBar.Value = 0;
            else
                _vScrollBar.Value = scrollValue;
        }

        #endregion

        #region 受保护的方法

        internal bool ContainerExtendMember(string member)
        {
            return _imageListView.ContainerExtendMember(member);
        }

        internal string GetExtendMember(string member)
        {
            return _imageListView.GetExtendMember(member);
        }

        internal string GetItemText(object itemValue)
        {
            return _imageListView.GetItemText(itemValue);
        }

        internal string GetItemText(object itemValue, string propertyName)
        {
            return _imageListView.GetItemText(itemValue, propertyName);
        }

        internal void OnItemsRemoved(List<ShengListViewItem> items)
        {
            _renderer.OnItemsRemoved(items);
        }

        internal void Dispose()
        {
            _imageListView.Controls.Remove(_vScrollBar);
            _vScrollBar.Dispose();
            _scrollTimer.Stop();
            _scrollTimer.Enabled = false;
            _scrollTimer.Dispose();
            _renderer.Dispose();
            _imageListView = null;
        }

        #endregion

        #region 公开方法

        ///// <summary>
        ///// 测量当前项应该的高度
        ///// </summary>
        ///// <returns></returns>
        //public int MeasureItemHeight()
        //{
        //    int textHeight = _imageListView.Font.Height;

        //    int height = _borderSize * 2 + textHeight + Padding.Top + Padding.Bottom;

        //    if (ShowDescription)
        //        height = height + textHeight + _textSpaceBetween;

        //    return height;
        //}
        /// <summary>
        /// 
        /// </summary>
        /// <param name="graphics"></param>
        public void Render(Graphics graphics)
        {
            //Debug.Write("Render(Graphics graphics)" + Environment.NewLine);

            Update();

            _renderer.Render(graphics);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="graphics"></param>
        /// <param name="item"></param>
        public void RenderItem(Graphics graphics, ShengListViewItem item)
        {
            Debug.Assert(MouseSelecting == false, "MouseSelecting 为 " + MouseSelecting.ToString());

            _renderer.RenderItem(graphics, item);
        }

        /// <summary>
        /// 更新整个布局引擎的状态
        /// </summary>
        public void Update()
        {
            //Debug.Write("Update()" + Environment.NewLine);

            CalculateGrid();

            CalculateVisibleItemsRange();

            UpdateScrollBars();
        }

        /// <summary>
        /// 判断指定的项是否处于可见状态
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        public ShengListViewItemVisibility IsItemVisible(ShengListViewItem item)
        {
            int itemIndex = _imageListView.Items.IndexOf(item);

            if (_imageListView.Items.Count == 0)
                return ShengListViewItemVisibility.NotVisible;

            if (itemIndex < 0 || itemIndex > _imageListView.Items.Count - 1)
                return ShengListViewItemVisibility.NotVisible;

            if (itemIndex < _firstPartiallyVisible || itemIndex > _lastPartiallyVisible)
                return ShengListViewItemVisibility.NotVisible;
            else if (itemIndex >= _firstVisible && itemIndex <= _lastVisible)
                return ShengListViewItemVisibility.Visible;
            else
                return ShengListViewItemVisibility.PartiallyVisible;
        }

        /// <summary>
        /// 获取项的呈现区域
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        public Rectangle GetItemBounds(ShengListViewItem item)
        {
            int index = _imageListView.Items.IndexOf(item);

            return GetItemBounds(index);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        public Rectangle GetItemBounds(int index)
        {
            Point location = ItemsArea.Location;

            //测算滚动条向下滚动过的高度，做为初始 Y 坐标
            location.Y += _margin / 2 - _itemsAreaOffset;

            ////itemIndex % _columnCount 得到项在第几列
            ////itemIndex / _columnCount 得到项在第几行
            //location.X += _margin / 2 + (index % _columnCount) * _itemSizeWithMargin.Width;

            ////在初始 Y 坐标的基础上，算出此项所在行，计算出其应该在的Y坐标
            location.Y += index * _itemHeightWithMargin;

            location.X = ItemsArea.X;

            return new Rectangle(location, new Size(ItemsArea.Width, _itemHeight));
        }
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public List<ShengListViewItem> GetItems()
        {
            return _imageListView.Items.ToList();
        }

        /// <summary>
        /// 获取当前所有可见项
        /// </summary>
        /// <returns></returns>
        public List<ShengListViewItem> GetVisibleItems()
        {
            List<ShengListViewItem> items = new List<ShengListViewItem>();

            for (int i = _firstPartiallyVisible; i <= _lastPartiallyVisible; i++)
            {
                items.Add(_imageListView.Items[i]);
            }

            return items;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="point"></param>
        /// <returns></returns>
        public ShengListViewHitInfo HitTest(Point point)
        {
            int itemIndex = -1;

            //这里对X，Y坐标的减，实现目的是平移坐标
            //传进来的是相对控件左上角的坐标，应平移为Padding后的内框左上角相对坐标
            //但是要考虑滚动时，上面的Padding也允许显示项
            //X坐标直接向右平移即可
            //Y坐标在没有滚动时，直接向下平移，如果存在滚动，当滚动值小于顶部Padding时
            //将Y坐标平移至项的最层端Y坐标上，即：Padding-滚动值

            //相对于Padding后的内框坐标系的坐标
            Point relativePoint = point;

            // Normalize to item area coordinates
            relativePoint.X -= ItemsBounds.Left;

            //y即平移量
            //此处y坐标需要在绘制项的区域（Padding后的区域）的基础上，考虑滚动条的offset
            int y = ItemsArea.Top - _itemsAreaOffset;
            if (y < 0) y = 0;
            relativePoint.Y -= y;

            if (relativePoint.X > 0 && relativePoint.Y > 0)
            {
                //当前点击的行和列的索引，从0开始
                //int col = relativePoint.X / _itemSizeWithMargin.Width;
                int row = (relativePoint.Y + ItemsBoundsOffset) / _itemHeightWithMargin;

                ////判断点的是不是右边的空白，可能右边会有比较大的空白，又没大到够完整的显示一列图像
                //bool isNotHitRightEmptyArea = col <= _columnCount - 1;
                //if (isNotHitRightEmptyArea)
                //{
                //    int index = row * _columnCount + col;

                //    //判断是不是点在图像区域内，还是图像边上，四周的Margin上
                //    Rectangle bounds = GetItemBounds(index);
                //    //判断点的坐标是不是在项的显示区域（Bounds)内，要用相对整个控件的原始坐标
                //    //因为项的bounds是相对整个控件的
                //    bool isHitInItem = bounds.Contains(point.X, point.Y);

                //    if (isHitInItem)
                //    {
                //        itemIndex = index;
                //    }
                //}

                int index = row;
                Rectangle bounds = GetItemBounds(index);
                bool isHitInItem = bounds.Contains(point.X, point.Y);

                if (isHitInItem)
                {
                    itemIndex = index;
                }
            }

            //是否点击在了有效项上
            bool itemHit = itemIndex >= 0 && itemIndex < _imageListView.Items.Count;

            ShengListViewHitInfo hitInfo = new ShengListViewHitInfo(itemIndex, itemHit);
            return hitInfo;
        }

        #endregion

        #region 事件响应方法
        /// <summary>
        /// 
        /// </summary>
        /// <param name="e"></param>
        public void MouseDown(MouseEventArgs e)
        {
            /*
             * 如果按下的是鼠标右键
             * 如果按在已选定的项上，只切换焦点，不改变选择
             * 如果按在未选定项上，则切换为点的项为选中项
             * 不考虑键盘按键
             */

            _mouseItemsAreaOffset = _itemsAreaOffset;

            List<ShengListViewItem> oldSelectedItems = _imageListView.GetSelectedItems();

            ShengListViewHitInfo hitInfo = HitTest(e.Location);

            if (hitInfo.ItemHit)
            {
                ShengListViewItem item = _imageListView.Items[hitInfo.ItemIndex];
                List<ShengListViewItem> allItems = _imageListView.Items.ToList();
                ShengListViewItem currentFocusedItem = _imageListView.FocusedItem;

                if (_imageListView.LeftButton)
                {
                    #region 如果不允许多选
                    if (_imageListView.AllowMultiSelection == false)
                    {
                        //如果点击的项就是当前选择的项
                        if (oldSelectedItems.Count > 0 && oldSelectedItems.Contains(item))
                        {
                            //判断Control键是否按下，如果按下了Control键，取消当前项的选择状态
                            if (_imageListView.ControlKey)
                                item.Selected = false;
                        }
                        else
                        {
                            //如果点击的项不是当前选择的项
                            //清除原选定项的选定状态
                            _imageListView.ClearSelect();
                            //设置新项为选定项
                            item.Selected = true;
                        }
                    }
                    #endregion
                    #region 如果允许多选
                    //在同时按下 Control 和 Shift 的情况下，优先考虑 Shift
                    else
                    {
                        #region 如果按下 Shift
                        //判断是否按下了 Shift ，如果按下 Shift，不考虑 Control 的状态
                        //也不用考虑是否点击的项是否是现有选定项之一
                        if (_imageListView.ShiftKey)
                        {
                            //如果当前存在具有输入焦点的项
                            if (currentFocusedItem != null && currentFocusedItem != item)
                            {
                                //连续选中从当前具有焦点的项至点击的项之间的所有项
                                //并将不在此范围内的项取消选中状态
                                int startIndex = Math.Min(currentFocusedItem.Index, hitInfo.ItemIndex);
                                int endIndex = Math.Max(currentFocusedItem.Index, hitInfo.ItemIndex);
                                foreach (var i in from c in oldSelectedItems where c.Index < startIndex || c.Index > endIndex select c)
                                {
                                    i.Selected = false;
                                }
                                for (int i = startIndex; i <= endIndex; i++)
                                {
                                    ShengListViewItem eachItem = allItems[i];
                                    if (eachItem.Selected == false)
                                        eachItem.Selected = true;
                                }
                            }
                            //如果当前不存在具有输入焦点的项
                            else
                            {
                                //清除原选定项的选定状态
                                _imageListView.ClearSelect();
                                item.Selected = true;
                            }
                        }
                        #endregion
                        #region 如果 Shift键没有处于按下状态
                        else
                        {
                            #region 如果点击的项 是 当前选择的项之一
                            if (oldSelectedItems.Count > 0 && oldSelectedItems.Contains(item))
                            {
                                //判断是否按下了 Control，且没有按下 Shift
                                if (_imageListView.ControlKey && _imageListView.ShiftKey == false)
                                {
                                    item.Selected = false;
                                }

                                //判断是否什么键都没有按下
                                if (_imageListView.ControlKey == false && _imageListView.ShiftKey == false)
                                {
                                    //清除原选定项的选定状态
                                    _imageListView.ClearSelect();
                                    item.Selected = true;
                                }
                            }
                            #endregion
                            #region 如果点击的项 不是 当前选择的项之一
                            else
                            {
                                //判断Control键是否按下，如果按下了Control键，则保持原有选择的情况把新项也设置为选中
                                //否则清除当前选择
                                if (_imageListView.ControlKey == false)
                                {
                                    //清除原选定项的选定状态
                                    _imageListView.ClearSelect();
                                }
                                item.Selected = true;
                            }
                            #endregion
                        }
                        #endregion
                    }
                    #endregion
                }
                else
                {
                    //如果点在未选中的项上
                    if (oldSelectedItems.Contains(item) == false)
                    {
                        _imageListView.ClearSelect();
                        //设置新项为选定项
                        item.Selected = true;
                    }
                }

                #region 为项设置输入焦点

                //设置新的输入焦点要放在后面处理，因为在使用Shift连续选择时，需要用到原具有焦点的项
                if (currentFocusedItem == null || (currentFocusedItem != null && currentFocusedItem != item))
                {
                    if (currentFocusedItem != null)
                        currentFocusedItem.Focused = false;
                    item.Focused = true;
                }
                #endregion
            }
            else
            {
                _imageListView.ClearSelect();
            }

            List<ShengListViewItem> newSelectedItems = _imageListView.GetSelectedItems();
            if (oldSelectedItems.SequenceEqual(newSelectedItems) == false)
            {
                _imageListView.NeedPaint();
                _imageListView.OnSelectedItemChanged();
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="e"></param>
        public void MouseUp(MouseEventArgs e)
        {
            if (MouseSelecting)
            {
                MouseSelecting = false;
                _scrollTimer.Enabled = false;
                _autoScrollValue = 0;
                _imageListView.NeedPaint();
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="e"></param>
        public void MouseMove(MouseEventArgs e)
        {
            Point lastMouseDownLocation = _imageListView.LastMouseDownLocation;

            #region 如果处于框选状态
            if (MouseSelecting)
            {
                //处于框选状态时，框内的项被选中不能在 MouseMove 事件中处理
                //因为当鼠标移出控件，滚动条自动滚动时，不动鼠标，就不会触发 MouseMove 事件

                #region 判断是否需要自动滚动滚动条

                if (_scrollTimer.Enabled == false)
                {
                    //需向下滚动
                    if (e.Y > ItemsBounds.Bottom)
                    {
                        _autoScrollValue = AUTOSCROLL_VALUE;
                        _scrollTimer.Enabled = true;
                    }
                    //需向上滚动
                    else if (e.Y < ItemsBounds.Top)
                    {
                        _autoScrollValue = -AUTOSCROLL_VALUE;
                        _scrollTimer.Enabled = true;
                    }
                }
                //鼠标从控件外面又回到了控件内，则停止自动滚动
                else if (_scrollTimer.Enabled && ItemsBounds.Contains(e.Location))
                {
                    _scrollTimer.Enabled = false;
                    _autoScrollValue = 0;
                }

                #endregion

                //创建选择框 Rectangle
                SelectionRectangle = CreateSelectionRectangle();
                SelectItemsByRectangle(SelectionRectangle);

                _imageListView.NeedPaint();
            }
            #endregion
            #region 如果不是处于框选状态
            else
            {
                //如果允许多选，鼠标处于按下状态，且距上次点击的点，大于了最小框选开始尺寸
                if (_imageListView.AllowMultiSelection && _imageListView.AnyMouseButton && (
                    (Math.Abs(e.Location.X - lastMouseDownLocation.X) > SELECTION_TOLERANCE ||
                    Math.Abs(e.Location.Y - lastMouseDownLocation.Y) > SELECTION_TOLERANCE)))
                {
                    MouseSelecting = true;
                }
            }
            #endregion
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="e"></param>
        public void OnMouseWheel(MouseEventArgs e)
        {
            int offSet = _itemsAreaOffset;
            int newYOffset = offSet - (e.Delta / SystemInformation.MouseWheelScrollDelta)
                   * _vScrollBar.SmallChange;
            if (newYOffset > _vScrollBar.Maximum - _vScrollBar.LargeChange + 1)
                newYOffset = _vScrollBar.Maximum - _vScrollBar.LargeChange + 1;
            if (newYOffset < 0)
                newYOffset = 0;
            if (newYOffset < _vScrollBar.Minimum) newYOffset = _vScrollBar.Minimum;
            if (newYOffset > _vScrollBar.Maximum) newYOffset = _vScrollBar.Maximum;
            _vScrollBar.Value = newYOffset;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="e"></param>
        public void OnKeyDown(KeyEventArgs e)
        {

            // If the shift key or the control key is pressed and there is no focused item
            // set the first item as the focused item.
            if ((ShiftKey || ControlKey) && _imageListView.Items.Count != 0 &&
                _imageListView.FocusedItem == null)
            {
                _imageListView.Items[0].Focused = true;
            }

            ShengListViewItem currentFocusedItem = _imageListView.FocusedItem;

            if (_imageListView.Items.Count != 0)
            {
                int index = 0;
                if (currentFocusedItem != null)
                    index = currentFocusedItem.Index;

                int newindex = ApplyNavKey(index, e.KeyCode);
                if (index != newindex)
                {
                    #region 根据新index做选择

                    if (ControlKey)
                    {
                        // Just move the focus
                    }
                    else if (_imageListView.AllowMultiSelection && ShiftKey)
                    {
                        int startIndex = 0;
                        int endIndex = 0;
                        List<ShengListViewItem> selectedItems = _imageListView.GetSelectedItems();
                        if (selectedItems.Count != 0)
                        {
                            startIndex = selectedItems[0].Index;
                            endIndex = selectedItems[selectedItems.Count - 1].Index;
                            _imageListView.ClearSelect();
                        }
                        if (index == startIndex)
                            startIndex = newindex;
                        else if (index == endIndex)
                            endIndex = newindex;
                        for (int i = Math.Min(startIndex, endIndex); i <= Math.Max(startIndex, endIndex); i++)
                        {
                            _imageListView.Items[i].Selected = true;
                        }
                    }
                    else
                    {
                        _imageListView.ClearSelect();
                        _imageListView.Items[newindex].Selected = true;
                    }

                    currentFocusedItem.Focused = false;
                    _imageListView.Items[newindex].Focused = true;

                    EnsureVisible(newindex);

                    #endregion

                    //触发事件
                    _imageListView.OnSelectedItemChanged();
                }
            }

            _imageListView.NeedPaint();
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="e"></param>
        public void OnKeyUp(KeyEventArgs e)
        {
            //Refresh();
        }

        #endregion

        #region 私有方法
        
        private void Refresh()
        {
            _imageListView.Refresh();
        }

        /// <summary>
        /// Calculates the maximum number of rows and columns 
        /// that can be fully displayed.
        /// </summary>
        private void CalculateGrid()
        {
            Rectangle itemArea = this.ItemsArea;
            //_columnCount = (int)System.Math.Floor((float)itemArea.Width / (float)_itemSizeWithMargin.Width);
            _rowCount = (int)System.Math.Floor((float)itemArea.Height / (float)_itemHeightWithMargin);

            //if (_columnCount < 1) _columnCount = 1;
            if (_rowCount < 1) _rowCount = 1;
        }

        /// <summary>
        /// 计算当前可见项的index范围
        /// </summary>
        private void CalculateVisibleItemsRange()
        {
            //Rectangle itemsArea;

            //这里必须把控件的内部Padding值考虑进来
            //_visibleOffset 是相对于 ItemsArea 的，此处要得到相对于整个控件可视区域的 offSet
            //因为显示的图片项即使超出了 ItemsArea ，但还是在可视区域内，还是完全可见的，在计算时需要考虑
            //ItemsArea.Location.Y - ItemsBounds.Location.Y 实际上就是Padding
            //但是这样写逻辑上好些，因为实际意义在于 ItemsArea 和 ItemsBounds 之间的区域也是可以显示内容的，在意义上与Padding无关

            int offSet = _itemsAreaOffset - (ItemsArea.Location.Y - ItemsBounds.Location.Y);
            if (offSet < 0)
                offSet = 0;

            int itemAreaHeight = ItemsBounds.Height;

            _firstPartiallyVisible = (int)System.Math.Floor((float)offSet / (float)_itemHeightWithMargin);
            _lastPartiallyVisible = (int)System.Math.Ceiling((float)(offSet + itemAreaHeight) / (float)_itemHeightWithMargin) - 1;

            if (_firstPartiallyVisible < 0) _firstPartiallyVisible = 0;
            if (_firstPartiallyVisible > _imageListView.Items.Count - 1) _firstPartiallyVisible = _imageListView.Items.Count - 1;
            if (_lastPartiallyVisible < 0) _lastPartiallyVisible = 0;
            if (_lastPartiallyVisible > _imageListView.Items.Count - 1) _lastPartiallyVisible = _imageListView.Items.Count - 1;

            _firstVisible = (int)System.Math.Ceiling((float)offSet / (float)_itemHeightWithMargin);
            _lastVisible = (int)System.Math.Floor((float)(offSet + itemAreaHeight) / (float)_itemHeightWithMargin) - 1;

            if (_firstVisible < 0) _firstVisible = 0;
            if (_firstVisible > _imageListView.Items.Count - 1) _firstVisible = _imageListView.Items.Count - 1;
            if (_lastVisible < 0) _lastVisible = 0;
            if (_lastVisible > _imageListView.Items.Count - 1) _lastVisible = _imageListView.Items.Count - 1;
        }

        /// <summary>
        /// 更新滚动条状态
        /// </summary>
        private void UpdateScrollBars()
        {
            if (_imageListView.Items.Count > 0)
            {
                _vScrollBar.Minimum = 0;
                _vScrollBar.Maximum = Math.Max(0,
                    (int)Math.Ceiling(
                    (float)_imageListView.Items.Count) * _itemHeightWithMargin - 1);
                _vScrollBar.LargeChange = ItemsArea.Height;
                _vScrollBar.SmallChange = _itemHeightWithMargin;

                bool vScrollRequired = (_imageListView.Items.Count > 0) &&
                    (_rowCount < _imageListView.Items.Count);
                _vScrollBar.Visible = vScrollRequired;

                //此处重新计算滚动条的滚动值
                //当滚动条出现，并滚动到底部时，改变控件的高度，就是向下拉大窗体
                //已经绘制的项下面会开始出现空白，就是因为滚动条的值还是旧值，_itemsAreaOffset也没有变化
                //除非用鼠标点一下滚动条，否则滚动条的 ValueChanged 事件也不会触发
                //所以绘制出的项的起始Y轴是不对的，必须在此重新计算滚动条的Value值
                if (_itemsAreaOffset > _vScrollBar.Maximum - _vScrollBar.LargeChange + 1)
                {
                    _vScrollBar.Value = _vScrollBar.Maximum - _vScrollBar.LargeChange + 1;
                    _itemsAreaOffset = _vScrollBar.Value;
                }
            }
            else
            {
                _vScrollBar.Visible = false;
                _vScrollBar.Value = 0;
                _vScrollBar.Minimum = 0;
                _vScrollBar.Maximum = 0;
            }
        }

        /// <summary>
        /// 创建框选框
        /// </summary>
        /// <returns></returns>
        private Rectangle CreateSelectionRectangle()
        {
            Point mousePoint = _imageListView.PointToClient(Cursor.Position);
            Point lastMouseDownLocation = _imageListView.LastMouseDownLocation;

            #region 说明
            
            //当框选的同时，滚动滚动条
            //计算offset:
            //由于可视区域和项的一般显示区域之间有个padding，而滚动条是以内部区域为标准的
            //所以当滚动条开始滚动时，可能项还是全部显示在可视范围内的（padding区也可以显示）
            //那么此时 _itemsBoundsOffset 还是 0，而按下鼠标时 _mouseDownOffset 记录的也是当时的 _itemsBoundsOffset
            //那么在计算 SelectionRectangle 的 Y 坐标时，
            //如果直接用 _itemsBoundsOffset 参与计算，就会产生一个和padding有关的误差
            //如 lastMouseDownLocation.Y - (viewOffset - _mouseDownOffset) ，假如此时向下滚动了一点
            //但所有的项还在可视范围内，那么 就会是 lastMouseDownLocation.Y - (0 - 0) 
            //SelectionRectangle 的 Y 坐标就差生了误差
            //解决的办法是使用 _itemsAreaOffset（既滚动条的Value），使框框的Y坐标与滚动条同步滚动即可

            #endregion

            int viewOffset = _itemsAreaOffset;
            Point pt1 = new Point(lastMouseDownLocation.X, lastMouseDownLocation.Y - (viewOffset - _mouseItemsAreaOffset));
            Point pt2 = new Point(mousePoint.X, mousePoint.Y);
            Rectangle rect = new Rectangle(Math.Min(pt1.X, pt2.X), Math.Min(pt1.Y, pt2.Y),
                Math.Abs(pt1.X - pt2.X), Math.Abs(pt1.Y - pt2.Y));

            return rect;
        }

        /// <summary>
        /// 根据矩形区域选择项
        /// </summary>
        /// <param name="rect"></param>
        private void SelectItemsByRectangle(Rectangle rect)
        {
            int viewOffset = _itemsAreaOffset;

            Point pt1 = new Point(SelectionRectangle.Left, SelectionRectangle.Top);
            Point pt2 = new Point(SelectionRectangle.Right, SelectionRectangle.Bottom);

            //- ItemsArea.Y 和 - ItemsArea.X
            //是因为，选择框的是以整个控件可视区域为坐标系的，而绘制的项是在Padding区内的
            //那么就需要修正或者说同步这个偏差，才能得到正确的行列
            int startRow = (int)Math.Floor((float)(Math.Min(pt1.Y, pt2.Y) + viewOffset - ItemsArea.Y) /
                  (float)this._itemHeightWithMargin);
            int endRow = (int)Math.Floor((float)(Math.Max(pt1.Y, pt2.Y) + viewOffset - ItemsArea.Y) /
                (float)this._itemHeightWithMargin);
            //int startCol = (int)Math.Floor((float)(Math.Min(pt1.X, pt2.X) - ItemsArea.X) /
            //    (float)this._itemSizeWithMargin.Width);
            //int endCol = (int)Math.Floor((float)(Math.Max(pt1.X, pt2.X) - ItemsArea.X) /
            //    (float)this._itemSizeWithMargin.Width);

            //行不能这样判断，因为_rowCount表示的只是控件可视范围内的可视行数
            //在框选时，框会跨越可视区域的，这里的endRow要的是实际的项所在行数
            //列不存在这个问题，因为不支持水平滚动
            //if (endRow >= _rowCount)
            //    endRow = _rowCount -1;
            //if (endCol >= _columnCount)
            //    endCol = _columnCount - 1;
            //if (startCol < 0)
            //    startCol = 0;

            //创建一个应该被选定的项的index数组
            int itemsCount = _imageListView.Items.Count;
            List<int> selectItemsIndex = new List<int>();
            for (int i = startRow; i <= endRow; i++)
            {
                int index = i;
                if (index >= 0 && index < itemsCount)
                    selectItemsIndex.Add(index);

                //for (int j = startCol; j <= endCol; j++)
                //{
                //    int index = i * _columnCount + j;
                //    if (index >= 0 && index < itemsCount)
                //        selectItemsIndex.Add(index);
                //}
            }

            //如果当前没有按下Shift键，那么
            //判断当前选定的项中有没有不在框选区内的，如果有将其取消选定
            if (ShiftKey == false)
            {
                List<ShengListViewItem> currentSlectedItems = _imageListView.GetSelectedItems();
                foreach (var item in currentSlectedItems)
                {
                    if (selectItemsIndex.Contains(item.Index) == false)
                        item.Selected = false;
                }
            }

            ShengListViewItemCollection allItems = _imageListView.Items;
            //使框选区内的项都处于选中状态
            foreach (var index in selectItemsIndex)
            {
                if (allItems[index].Selected == false)
                    allItems[index].Selected = true;
            }

            //debug
            StartRow = startRow;
            EndRow = endRow;
            //StartCol = startCol;
            //EndCol = endCol;
        }

        /// <summary>
        /// 应用导航键，如上下左右，返回应用导航键之后的项的坐标
        /// </summary>
        private int ApplyNavKey(int index, Keys key)
        {
            int itemsCount = _imageListView.Items.Count;

            if (key == Keys.Up && index > 0)
                index -= 1;
            else if (key == Keys.Down && index < itemsCount - 1)
                index += 1;
            else if (key == Keys.Left && index > 0)
                index--;
            else if (key == Keys.Right && index < itemsCount - 1)
                index++;
            else if (key == Keys.PageUp && index >= (_rowCount - 1))
                index -= (_rowCount - 1);
            else if (key == Keys.PageDown && index < itemsCount - (_rowCount - 1))
                index += (_rowCount - 1);
            else if (key == Keys.Home)
                index = 0;
            else if (key == Keys.End)
                index = itemsCount - 1;

            if (index < 0)
                index = 0;
            else if (index > itemsCount - 1)
                index = itemsCount - 1;

            return index;
        }

        /// <summary>
        /// 使指定下标的项处于可见状态
        /// </summary>
        /// <param name="itemIndex"></param>
        public void EnsureVisible(int itemIndex)
        {
            int itemCount = _imageListView.Items.Count;
            if (itemCount == 0) return;
            if (itemIndex < 0 || itemIndex >= itemCount) return;

            // Already visible?
            Rectangle bounds = this.ItemsBounds;
            Rectangle itemBounds = GetItemBounds(itemIndex);

            if (bounds.Contains(itemBounds) == false)
            {
                int delta;
                if (itemBounds.Top < bounds.Top)
                    delta = bounds.Top - itemBounds.Top;
                else
                {
                    int topItemIndex = itemIndex - (_rowCount - 1) ;
                    if (topItemIndex < 0) topItemIndex = 0;
                    delta = bounds.Top - GetItemBounds(topItemIndex).Top;
                }
                int newYOffset = this.ItemsBoundsOffset - delta;
                if (newYOffset > _vScrollBar.Maximum - _vScrollBar.LargeChange + 1)
                    newYOffset = _vScrollBar.Maximum - _vScrollBar.LargeChange + 1;
                if (newYOffset < _vScrollBar.Minimum)
                    newYOffset = _vScrollBar.Minimum;
                //mViewOffset.X = 0;
                //mViewOffset.Y = newYOffset;
                //hScrollBar.Value = 0;
                _vScrollBar.Value = newYOffset;
            }
        }

        #endregion
    }
}
