#region COPYRIGHT
//
//     THIS IS GENERATED BY TEMPLATE
//     
//     AUTHOR  :     ROYE
//     DATE       :     2010
//
//     COPYRIGHT (C) 2010, TIANXIAHOTEL TECHNOLOGIES CO., LTD. ALL RIGHTS RESERVED.
//
#endregion

using System;
using System.ComponentModel;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Text.Template;
using System.Threading;
using System.Runtime.InteropServices;
using System.Win32;
using System.Drawing.Drawing2D;
using System.Windows.Forms.VisualStyles;
using System.Windows.Forms;

namespace TX.Framework.WindowUI.Controls
{
    [
    Serializable,
    DesignerCategory("Code"),
    ToolboxBitmapAttribute(typeof(TemplateListView), "TemplateListView.ico")
    ]
    public class TemplateListView : TXListView
    {
        private List<EmbeddedControl> _EmbeddedControls;

        private object _DataSource;
        private TemplateColumnHeaderCollection _ColumnHeaders = null;
        private bool _DataBindThreading;
        private Thread _DataBindThread = null;
        private bool _ResizeColumnsThreading;
        private Thread _ResizeColumnsThread = null;
        private TemplateContext _TemplateContext;

        private const int HDN_FIRST = -300;
        private const int HDN_BEGINDRAG = (HDN_FIRST - 10);
        private const int HDN_ITEMCHANGINGA = (HDN_FIRST - 0);
        private const int HDN_ITEMCHANGINGW = (HDN_FIRST - 20);

        private Control _EditingControl;
        private ListViewItem _EditItem;
        private IContainer components;
        private ImageList imageList1;
        private int _EditSubItem;

        private Color _rowBackColor1 = Color.White;
        private Color _rowBackColor2 = Color.FromArgb(233, 248, 242);
        private Color _selectedColor = Color.LightBlue;
        private Color _headColor = Color.LightBlue;

        public event SubItemEventHandler SubItemClicked;
        public event SubItemEventHandler SubItemBeginEditing;
        public event SubItemEndEditingEventHandler SubItemEndEditing;

        public TemplateListView()
            : base()
        {
            InitializeComponent();
            _EmbeddedControls = new List<EmbeddedControl>();
            this._ColumnHeaders = new TemplateColumnHeaderCollection();
            this._TemplateContext = new TemplateContext();
            this.ColumnClick += new ColumnClickEventHandler(TemplateListView_ColumnClick);
        }

        protected override void OnDrawColumnHeader(DrawListViewColumnHeaderEventArgs e)
        {
            try
            {
                base.OnDrawColumnHeader(e);
                string key = this.Columns[e.ColumnIndex].ImageKey;
                if (!string.IsNullOrEmpty(key))
                {
                    Rectangle rect = e.Bounds;
                    int offset = 3;
                    Size arrowSize = new System.Drawing.Size(8, 6);
                    Rectangle sortRect = new Rectangle(rect.Right - offset - arrowSize.Width,
                        rect.Y + (rect.Height - arrowSize.Height) / 2,
                        arrowSize.Width, arrowSize.Height);
                    Graphics g = e.Graphics;
                    GDIHelper.InitializeGraphics(g);
                    Color c = Color.FromArgb(46, 117, 35);
                    if (key == "up")
                    {
                        GDIHelper.DrawArrow(g, ArrowDirection.Up, sortRect, arrowSize, 1.5f, c);
                    }
                    else
                    {
                        GDIHelper.DrawArrow(g, ArrowDirection.Down, sortRect, arrowSize, 1.5f, c);
                    }
                }
            }catch{}
            
        }

        private void TemplateListView_ColumnClick(object sender, ColumnClickEventArgs e)
        {
            ListView listView = sender as ListView;
            if (listView != null)
            {
                TemplateColumnHeader sorter = listView.Columns[e.Column] as TemplateColumnHeader;
                if (sorter != null)
                {
                    foreach (ColumnHeader header in this.Columns)
                    {
                        header.ImageKey = null;
                    }

                    if (listView.Sorting == SortOrder.None)
                    {
                        listView.Sorting = SortOrder.Ascending;
                        this.Columns[e.Column].ImageKey = "up";
                    }
                    else if (listView.Sorting == SortOrder.Ascending)
                    {
                        listView.Sorting = SortOrder.Descending;
                        this.Columns[e.Column].ImageKey = "down";
                    }
                    else
                    {
                        listView.Sorting = SortOrder.Ascending;
                        this.Columns[e.Column].ImageKey = "up";
                    }

                    sorter.Column = e.Column;
                    this.ListViewItemSorter = sorter;
                }
            }
        }

        public int[] GetColumnOrder()
        {
            IntPtr lPar = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(int)) * Columns.Count);
            int res = 0;
            try
            {
                res = NativeMethods.SendMessage(this.Handle, (int)ListViewMessages.GETCOLUMNORDERARRAY, new IntPtr(Columns.Count), lPar);
                if (res == 0)
                {
                    return null;
                }

                int[] order = new int[Columns.Count];
                Marshal.Copy(lPar, order, 0, Columns.Count);
                return order;
            }
            finally
            {
                Marshal.FreeHGlobal(lPar);
            }
        }

        public int GetSubItemAt(int x, int y, out ListViewItem item, out bool canEdit)
        {
            canEdit = false;
            item = this.GetItemAt(x, y);
            if (item != null)
            {
                int[] order = GetColumnOrder();
                Rectangle lviBounds;
                int subItemX;

                lviBounds = item.GetBounds(ItemBoundsPortion.Entire);
                subItemX = lviBounds.Left;
                for (int i = 0; i < order.Length; i++)
                {
                    TemplateColumnHeader columnHeader = this.Columns[order[i]];
                    if (x < subItemX + columnHeader.Width)
                    {
                        canEdit = columnHeader.CanEdit;
                        return columnHeader.Index;
                    }
                    subItemX += columnHeader.Width;
                }
            }

            return -1;
        }

        public Rectangle GetSubItemBounds(ListViewItem item, int subItem)
        {
            Guard.ArgumentNotNull(item, "item");

            int[] columnOrder = GetColumnOrder();
            Rectangle subItemRect = Rectangle.Empty;
            if (subItem >= columnOrder.Length)
            {
                throw new IndexOutOfRangeException("SubItem " + subItem + " out of range");
            }

            Rectangle lviBounds = item.GetBounds(ItemBoundsPortion.Entire);
            int subItemX = lviBounds.Left;

            ColumnHeader columnHeader;
            int i;
            for (i = 0; i < columnOrder.Length; i++)
            {
                columnHeader = this.Columns[columnOrder[i]];
                if (columnHeader.Index == subItem)
                {
                    break;
                }
                subItemX += columnHeader.Width;
            }
            subItemRect = new Rectangle(subItemX, lviBounds.Top, this.Columns[columnOrder[i]].Width, lviBounds.Height);

            return subItemRect;
        }

        public void AddEmbeddedControl(Control c, int column, int row, DockStyle dock)
        {
            Guard.ArgumentNotNull(c, "c");
            if (column >= Columns.Count || row >= Items.Count)
            {
                throw new ArgumentOutOfRangeException();
            }

            EmbeddedControl ec;
            ec.Control = c;
            ec.Column = column;
            ec.Row = row;
            ec.Dock = dock;
            ec.Item = Items[row];
            ec.Control.Visible = true;

            _EmbeddedControls.Add(ec);
            c.Click += new EventHandler(EmbeddedControl_Click);
            this.Controls.Add(c);
        }

        public void RemoveEmbeddedControl(Control c)
        {
            Guard.ArgumentNotNull(c, "c");
            for (int i = 0; i < _EmbeddedControls.Count; i++)
            {
                EmbeddedControl ec = (EmbeddedControl)_EmbeddedControls[i];
                if (ec.Control == c)
                {
                    c.Click -= new EventHandler(EmbeddedControl_Click);
                    this.Controls.Remove(c);
                    _EmbeddedControls.RemoveAt(i);
                    return;
                }
            }
            throw new Exception("未找到要移除的控件！");
        }

        private void EmbeddedControl_Click(object sender, EventArgs e)
        {
            foreach (EmbeddedControl ec in _EmbeddedControls)
            {
                if (ec.Control == (Control)sender)
                {
                    this.SelectedItems.Clear();
                    ec.Item.Selected = true;
                }
            }
        }

        public Control GetEmbeddedControl(int column, int row)
        {
            foreach (EmbeddedControl ec in _EmbeddedControls)
            {
                if (ec.Row == row && ec.Column == column)
                {
                    return ec.Control;
                }
            }
            return null;
        }

        private void DataBind()
        {
            if (this._DataBindThreading)
            {
                this._DataBindThread = new Thread(new ThreadStart(DataBindThread));
                this._DataBindThread.IsBackground = true;
                this._DataBindThread.Name = "Data Binding Thread";
                this._DataBindThread.Start();
            }
            else
            {
                this.DataBinding();
            }
        }

        private void DataBindThread()
        {
            lock (this)
            {
                if (this.InvokeRequired)
                {
                    this.BeginInvoke(new Action(DataBinding));
                }
                else
                {
                    this.DataBinding();
                }
            }
        }

        private void DataBinding()
        {
            base.Clear();

            IEnumerable dataSource = GetDataSource();

            if (dataSource == null) return;
            if (this._ColumnHeaders.Count == 0) return;

            ListViewItem item = null;
            Cursor current = this.Cursor;
            this.Cursor = Cursors.WaitCursor;
            this.BeginUpdate();

            for (int i = 0; i < this._ColumnHeaders.Count; i++)
            {
                base.Columns.Add(this._ColumnHeaders[i]);
            }

            if (_TemplateContext == null)
            {
                _TemplateContext = new TemplateContext();
            }

            ITemplateContext context = null;
            foreach (object dataItem in dataSource)
            {
                context = this._TemplateContext.CreateLocal();
                context.Add("this", dataItem);

                item = new ListViewItem();
                item.Tag = dataItem;
                item.Text = TemplateEngine.Velocity.Render(_ColumnHeaders[0].Template, context);
                item.SubItems[0].Tag = item.Text;

                if ( CheckedTester!=null)
                {
                    item.Checked = CheckedTester(dataItem);
                }

                for (int i = 1; i < this._ColumnHeaders.Count; i++)
                {
                    string text = TemplateEngine.Velocity.Render(_ColumnHeaders[i].Template, context);
                    ListViewItem.ListViewSubItem subItem = new ListViewItem.ListViewSubItem(item, text);
                    subItem.Tag = text;
                    item.SubItems.Add(subItem);
                }
                this.Items.Add(item);
            }

            this.EndUpdate();
            this.Cursor = current;
        }

        protected virtual IEnumerable GetDataSource()
        {
            if (_DataSource == null)
            {
                return null;
            }

            IEnumerable resolvedDataSource = _DataSource as IEnumerable;
            if (resolvedDataSource != null)
            {
                return resolvedDataSource;
            }

            return null;
        }

        public void ResizeColumns()
        {
            if (this._ResizeColumnsThreading)
            {
                this._ResizeColumnsThread = new Thread(new ThreadStart(ResizeColumnsThread));
                this._ResizeColumnsThread.IsBackground = true;
                this._ResizeColumnsThread.Name = "Resize Columns Thread";
                this._ResizeColumnsThread.Start();
            }
            else
            {
                this.InnerResizeColumns();
            }
        }

        private void ResizeColumnsThread()
        {
            lock (this)
            {
                if (this.InvokeRequired)
                {
                    this.BeginInvoke(new Action(InnerResizeColumns));
                }
                else
                {
                    this.InnerResizeColumns();
                }
            }
        }

        private void InnerResizeColumns()
        {
            Cursor current = this.Cursor;
            this.Cursor = Cursors.WaitCursor;
            if (this.Items.Count >= 1)
            {
                if (this._ColumnHeaders.Count >= 1)
                {
                    this.BeginUpdate();
                    for (int i = 0; i < this._ColumnHeaders.Count; i++)
                    {
                        int nColHdrSize = 0, nColSize = 0;
                        this.GetLargestColHdrTextExtent(i, ref nColHdrSize);
                        this.GetLargestTextExtent(i, ref nColSize);
                        if (nColHdrSize > nColSize)
                        {
                            this._ColumnHeaders[i].Width = nColHdrSize + 8; // Fudge Factor
                        }
                        else
                        {
                            this._ColumnHeaders[i].Width = nColSize + 8;
                        }
                        nColHdrSize = nColSize = 0;
                    }
                    this.EndUpdate();
                }
            }
            this.Cursor = current;
        }

        private void GetLargestColHdrTextExtent(int columnIndex, ref int largestWidth)
        {
            if (this.Items.Count >= 1)
            {
                if (columnIndex >= 0 && columnIndex < this._ColumnHeaders.Count)
                {
                    using (Graphics g = this.CreateGraphics())
                    {
                        largestWidth = (int)g.MeasureString(this._ColumnHeaders[columnIndex].Text, this.Font).Width;
                    }
                }
            }
        }

        private void GetLargestTextExtent(int columnIndex, ref int largestWidth)
        {
            int maxLen = -1;
            ListViewItem item = null;
            if (this.Items.Count >= 1)
            {
                if (columnIndex >= 0 && columnIndex < this._ColumnHeaders.Count)
                {
                    using (Graphics g = this.CreateGraphics())
                    {
                        int newWidth = -1;
                        for (int i = 0; i < this.Items.Count; i++)
                        {
                            item = this.Items[i];
                            if (item != null)
                            {
                                newWidth = (int)g.MeasureString(item.SubItems[columnIndex].Text, this.Font).Width;
                            }
                            else
                            {
                                newWidth = 0;
                            }
                            if (newWidth > maxLen)
                            {
                                maxLen = newWidth;
                            }
                        }
                    }
                }
            }
            largestWidth = maxLen;
        }

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

            if (DoubleClickActivation)
            {
                return;
            }

            EditSubitemAt(new Point(e.X, e.Y));
        }

        protected override void OnDoubleClick(EventArgs e)
        {
            base.OnDoubleClick(e);

            if (!DoubleClickActivation)
            {
                return;
            }

            Point pt = this.PointToClient(Cursor.Position);

            EditSubitemAt(pt);
        }

        private void EditSubitemAt(Point p)
        {
            ListViewItem item;
            bool canEdit;
            int idx = GetSubItemAt(p.X, p.Y, out item, out canEdit);
            if (canEdit)
            {
                if (idx >= 0)
                {
                    OnSubItemClicked(new SubItemEventArgs(item, idx));
                }
            }
        }

        #region In-place Editing Functions

        protected void OnSubItemBeginEditing(SubItemEventArgs e)
        {
            if (SubItemBeginEditing != null)
            {
                SubItemBeginEditing(this, e);
            }
        }

        protected void OnSubItemEndEditing(SubItemEndEditingEventArgs e)
        {
            if (SubItemEndEditing != null)
            {
                SubItemEndEditing(this, e);
            }
        }

        protected void OnSubItemClicked(SubItemEventArgs e)
        {
            if (SubItemClicked != null)
            {
                SubItemClicked(this, e);
            }
        }

        public void StartEditing(Control control, ListViewItem item, int subItem)
        {
            OnSubItemBeginEditing(new SubItemEventArgs(item, subItem));

            Rectangle rcSubItem = GetSubItemBounds(item, subItem);

            if (rcSubItem.X < 0)
            {
                // Left edge of SubItem not visible - adjust rectangle position and width
                rcSubItem.Width += rcSubItem.X;
                rcSubItem.X = 0;
            }
            if (rcSubItem.X + rcSubItem.Width > this.Width)
            {
                // Right edge of SubItem not visible - adjust rectangle width
                rcSubItem.Width = this.Width - rcSubItem.Left;
            }

            // Subitem bounds are relative to the location of the ListView!
            rcSubItem.Offset(Left, Top);

            // In case the editing control and the listview are on different parents,
            // account for different origins
            Point origin = new Point(0, 0);
            Point lvOrigin = this.Parent.PointToScreen(origin);
            Point ctlOrigin = control.Parent.PointToScreen(origin);

            rcSubItem.Offset(lvOrigin.X - ctlOrigin.X, lvOrigin.Y - ctlOrigin.Y);

            // Position and show editor
            control.Bounds = rcSubItem;
            control.Text = item.SubItems[subItem].Text;
            control.Visible = true;
            control.BringToFront();
            control.Focus();

            _EditingControl = control;
            _EditingControl.Leave += new EventHandler(_EditingControl_Leave);
            _EditingControl.KeyPress += new KeyPressEventHandler(_EditingControl_KeyPress);

            _EditItem = item;
            _EditSubItem = subItem;
        }


        private void _EditingControl_Leave(object sender, EventArgs e)
        {
            // cell editor losing focus
            EndEditing(true);
        }

        private void _EditingControl_KeyPress(object sender, KeyPressEventArgs e)
        {
            switch (e.KeyChar)
            {
                case (char)(int)Keys.Escape:
                    {
                        EndEditing(false);
                        break;
                    }

                case (char)(int)Keys.Enter:
                    {
                        EndEditing(true);
                        break;
                    }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="AcceptChanges"></param>
        public void EndEditing(bool AcceptChanges)
        {
            if (_EditingControl == null)
            {
                return;
            }

            SubItemEndEditingEventArgs e = new SubItemEndEditingEventArgs(
                _EditItem,		// The item being edited
                _EditSubItem,	// The subitem index being edited
                // Use editControl text if changes are accepted
                AcceptChanges ? _EditingControl.Text : _EditItem.SubItems[_EditSubItem].Text,	// or the original subitem's text, if changes are discarded
                !AcceptChanges	// Cancel?
            );

            OnSubItemEndEditing(e);

            _EditItem.SubItems[_EditSubItem].Text = e.DisplayText;

            _EditingControl.Leave -= new EventHandler(_EditingControl_Leave);
            _EditingControl.KeyPress -= new KeyPressEventHandler(_EditingControl_KeyPress);

            _EditingControl.Visible = false;

            _EditingControl = null;
            _EditItem = null;
            _EditSubItem = -1;
        }

        #endregion

        protected override void WndProc(ref Message m)
        {
            switch ((WindowMessages)m.Msg)
            {
                case WindowMessages.VSCROLL:
                case WindowMessages.HSCROLL:
                case WindowMessages.SIZE:
                    EndEditing(false);
                    break;
                case WindowMessages.NOTIFY:
                    NMHDR h = (NMHDR)Marshal.PtrToStructure(m.LParam, typeof(NMHDR));
                    if (h.code == HDN_BEGINDRAG ||
                        h.code == HDN_ITEMCHANGINGA ||
                        h.code == HDN_ITEMCHANGINGW)
                        EndEditing(false);
                    break;
                case WindowMessages.PAINT:
                    if (View != View.Details)
                    {
                        break;
                    }
                    foreach (EmbeddedControl ec in _EmbeddedControls)
                    {
                        Rectangle rc = this.GetSubItemBounds(ec.Item, ec.Column);

                        if ((this.HeaderStyle != ColumnHeaderStyle.None) && (rc.Top < this.Font.Height))
                        {
                            ec.Control.Visible = false;
                            continue;
                        }
                        else
                        {
                            ec.Control.Visible = true;
                        }
                        switch (ec.Dock)
                        {
                            case DockStyle.Fill:
                                break;
                            case DockStyle.Top:
                                rc.Height = ec.Control.Height;
                                break;
                            case DockStyle.Left:
                                rc.Width = ec.Control.Width;
                                break;
                            case DockStyle.Bottom:
                                rc.Offset(0, rc.Height - ec.Control.Height);
                                rc.Height = ec.Control.Height;
                                break;
                            case DockStyle.Right:
                                rc.Offset(rc.Width - ec.Control.Width, 0);
                                rc.Width = ec.Control.Width;
                                break;
                            case DockStyle.None:
                                rc.Size = ec.Control.Size;
                                break;
                        }
                        ec.Control.Bounds = rc;
                    }
                    break;
            }

            base.WndProc(ref m);
        }

        [
        Category("数据"),
        Browsable(true),
        RefreshProperties(RefreshProperties.Repaint),
        TypeConverter("System.Windows.Forms.Design.DataSourceConverter, System.Design"),
        Description("数据源")
        ]
        public object DataSource
        {
            get { return this._DataSource; }
            set
            {
                if (value != null)
                {
                    this._DataSource = value;
                    this.DataBind();
                }
            }
        }

        [
        Category("数据"),
        Browsable(true),
        Description("数据列."),
        Localizable(true),
        DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
        MergableProperty(false)
        ]
        public new TemplateColumnHeaderCollection Columns
        {
            get { return this._ColumnHeaders; }
        }

        [
        Category("线程"),
        Browsable(true),
        Description("大数据量的时可以用后台线程去绑定。"),
        DefaultValue(false)
        ]
        public bool DataBindThreading
        {
            get { return this._DataBindThreading; }
            set
            {
                if (this._DataBindThreading != value)
                {
                    this._DataBindThreading = value;
                }
            }
        }

        [
        Category("线程"),
        Browsable(true),
        Description("自动调整列宽"),
        DefaultValue(true)
        ]
        public bool ResizeColumnsThreading
        {
            get { return this._ResizeColumnsThreading; }
            set
            {
                if (this._ResizeColumnsThreading != value)
                {
                    this._ResizeColumnsThreading = value;
                }
            }
        }

        public bool DoubleClickActivation { get; set; }

        [Browsable(false)]
        public TemplateContext TemplateContext
        {
            get
            {
                if (_TemplateContext == null)
                {
                    _TemplateContext = new TemplateContext();
                }
                return _TemplateContext;
            }
            set { _TemplateContext = value; }
        }

        private void InitializeComponent()
        {
            this.components = new System.ComponentModel.Container();
            System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(TemplateListView));
            this.imageList1 = new System.Windows.Forms.ImageList(this.components);
            this.SuspendLayout();
            // 
            // imageList1
            // 
            this.imageList1.ImageStream = ((System.Windows.Forms.ImageListStreamer)(resources.GetObject("imageList1.ImageStream")));
            this.imageList1.TransparentColor = System.Drawing.Color.Transparent;
            this.imageList1.Images.SetKeyName(0, "up");
            this.imageList1.Images.SetKeyName(1, "down");
            // 
            // TemplateListView
            // 
            this.SmallImageList = this.imageList1;
            this.ResumeLayout(false);

        }

        private struct EmbeddedControl
        {
            public Control Control;
            public int Column;
            public int Row;
            public DockStyle Dock;
            public ListViewItem Item;
        }

        public Func<object, bool> CheckedTester { get; set; }
    }
}
