using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Windows.Forms;
using JWH.DATA;
using Telerik.WinControls;
using Telerik.WinControls.Data;
using Telerik.WinControls.Export;
using Telerik.WinControls.UI;
using static JWH.DATA.CustomerAttributes;

namespace JWH.CONTROL
{

    public class GridViewEx : RadGridView
    {

        public static CultureInfo CultureInfo { get; set; } = CultureInfo.CurrentCulture;

        public delegate void ContextMenuHandler(object sender, params object[] args);

        [Flags]
        public enum eDateTimeGroupKey
        {
            None =  0b00000000,
            Year =  0b00000001,
            Month = 0b00000010,
            Day =   0b00000100,
            Hour =  0b00010000,
            Minute =0b00100000,
            Second =0b01000000,
            Date =  0b00000111,
        }

        #region [ Properties ] ================================================

        public override string ThemeClassName { get { return typeof(RadGridView).FullName; } }

        [Browsable(false), ReadOnly(true)]
        public Dictionary<string, List<ComboBoxItem>> ComboBoxCollection { get; } = new Dictionary<string, List<ComboBoxItem>>();

        [DefaultValue(true)]
        public bool ShowRowNumber { get; set; } = true;

        [Browsable(false), ReadOnly(true)]
        public List<object> DataBindingItems { get; set; } = new List<object>();

        [Browsable(false), DefaultValue(null)]
        public new RadContextMenu ContextMenu { get; set; } = null;

        [Browsable(false), DefaultValue(false)]
        public bool IsSetColumn { get; set; } = false;

        public Keys ColumnResizeKey { get; set; } = Keys.F6;

        /// <summary>
        /// HeaderText 값을 자동으로 생성하는 경우에 ToTitleCase로 변환한다. (AddColumn 메서드에서 HeaderText가 정의되지 않은 경우)
        /// </summary>
        [Browsable(true), DefaultValue(true)]
        public bool HeaderTextToTitleCase { get; set; } = true;

        [Browsable(false), DefaultValue(eDateTimeGroupKey.Date)]
        public eDateTimeGroupKey DateTimeGroupKey { get; set; } = eDateTimeGroupKey.Date;

        #endregion

        #region [ Constructor ] ===============================================

        public GridViewEx() : base()
        {
            if (LicenseManager.UsageMode == LicenseUsageMode.Designtime) return;

            this.SetLayout();
            this.SetEventHandler();
            if (Telerik.Data.Expressions.ExpressionContext.Context.GetType() != typeof(CustomerExpressionContext))
                Telerik.Data.Expressions.ExpressionContext.Context = new CustomerExpressionContext();
        }

        protected void SetLayout()
        {
            this.Font = new Font(new FontFamily("돋움체"), 9.0F);

            this.AllowAddNewRow = false;
            this.AddNewRowPosition = SystemRowPosition.Top;
            this.NewRowEnterKeyMode = RadGridViewNewRowEnterKeyMode.EnterMovesToLastAddedRow;

            this.SelectionMode = GridViewSelectionMode.FullRowSelect;

            this.AutoSizeRows = false;
            this.TableElement.RowHeight = 20;

            this.AllowAutoSizeColumns = false;
            this.AutoSizeColumnsMode = GridViewAutoSizeColumnsMode.None;

            this.EnableFiltering = true;
            this.ShowFilteringRow = false;
            this.ShowHeaderCellButtons = true;
            this.ShowNoDataText = true;
            this.ShowRowErrors = true;
            this.ShowGroupedColumns = true;
            this.ShowGroupPanelScrollbars = true;

            this.MasterTemplate.GroupPredicate = this.PerformGrouping;

            if (this.ContextMenu == null) this.ContextMenu = new RadContextMenu();
            this.AddContextMenu("Column Chooser", this.ShowColumnChooser);
            this.AddContextMenu("Copy Cell Text", this.Clipboard_CurrentCellValue, true);
            this.AddContextMenu("Copy Row Text", this.Clipboard_CurrentRowValue);
            this.AddContextMenu("Export Excel", this.ExportExcelDialog, true);
        }

        protected void SetEventHandler()
        {
            this.MouseDown += this.GridViewEx_MouseDown;

            this.DataBindingComplete += GridViewEx_DataBindingComplete;
            this.ContextMenuOpening += GridViewEx_ContextMenuOpening;
            this.GroupSummaryEvaluate += GridViewEx_GroupSummaryEvaluate;
            this.ValueChanged += GridViewEx_ValueChanged;
            this.CopyingCellClipboardContent += GridViewEx_CopyingCellClipboardContent;
            this.PastingCellClipboardContent += GridViewEx_PastingCellClipboardContent;

            this.RowsChanging += GridViewEx_RowChanging;
            this.Rows.CollectionChanged += GridViewEx_RowsCollectionChanged;

            this.CellEditorInitialized += GridViewEx_CellEditorInitialized;
            this.CellFormatting += GridViewEx_CellFormatting;
            this.CellBeginEdit += GridViewEx_CellBeginEdit;
            this.CellEndEdit += GridViewEx_CellEndEdit;
            this.ViewCellFormatting += GridViewEx_ViewCellFormatting;
        }

        protected override void OnLayout(LayoutEventArgs e)
        {
            base.OnLayout(e);
        }

        protected override void OnLoad(Size desiredSize)
        {
            base.OnLoad(desiredSize);

            this.BestFitColumns(BestFitColumnMode.HeaderCells);
        }

        protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
        {
            try
            {
                if (keyData == this.ColumnResizeKey) this.BestFitColumns();
            }
            catch (Exception ex)
            {
                XLogger.Instance.Fatal(ex);
            }
            return base.ProcessCmdKey(ref msg, keyData);
        }

        #endregion

        #region [ Events ] ====================================================

        private void GridViewEx_MouseDown(object sender, MouseEventArgs e)
        {
            switch (e.Button)
            {
                case MouseButtons.XButton1:
                    // PageDown
                    base.OnKeyDown(new KeyEventArgs(Keys.PageDown));
                    break;
                case MouseButtons.XButton2:
                    // PageUp
                    base.OnKeyDown(new KeyEventArgs(Keys.PageUp));
                    break;
            }
        }

        private void GridViewEx_DataBindingComplete(object sender, GridViewBindingCompleteEventArgs e)
        {
            this.SuspendLayout();
            ICollection<object> collection = this.DataSource as ICollection<object>;
            if (collection == null || collection.Count < 1) this.BestFitColumns(BestFitColumnMode.HeaderCells);
            else if (collection.Count < 500) this.BestFitColumns(BestFitColumnMode.AllCells);
            else this.BestFitColumns(BestFitColumnMode.DisplayedCells);

            // Resizing
            foreach (GridViewDataColumn column in this.Columns)
                if (column.Width > this.Width * 0.5F) column.Width = (int)(this.Width * 0.5F);

            if (this.ShowRowNumber)
                this.TableElement.RowHeaderColumnWidth = (int)this.CreateGraphics().MeasureString(this.Rows.Count.ToString(), this.Font).Width + 16;
            this.ResumeLayout();
        }

        private void GridViewEx_ContextMenuOpening(object sender, ContextMenuOpeningEventArgs e)
        {
            if (this.ContextMenu != null)
                e.ContextMenu = this.ContextMenu.DropDown;
        }

        private void GridViewEx_GroupSummaryEvaluate(object sender, GroupSummaryEvaluationEventArgs e)
        {
            if (e.Context.GetType() != typeof(GridViewSummaryRowInfo))
            {
                GridViewGroupRowInfo row = e.Parent as GridViewGroupRowInfo;
                e.FormatString = $"[{row?.Index}] {e.SummaryItem.Name}: {e.Value}  Count={e.Group.ItemCount}";
            }
        }

        private void GridViewEx_ValueChanged(object sender, EventArgs e)
        {
        }

        private void GridViewEx_CopyingCellClipboardContent(object sender, GridViewCellValueEventArgs e)
        {
        }

        private void GridViewEx_PastingCellClipboardContent(object sender, GridViewCellValueEventArgs e)
        {
            DataTableBase data = this.Rows[e.RowIndex].DataBoundItem as DataTableBase;
            if (data == null) return;
        }

        private void GridViewEx_RowChanging(object sender, GridViewCollectionChangingEventArgs e)
        {
            switch (e.Action)
            {
                case NotifyCollectionChangedAction.Add:
                    break;
                case NotifyCollectionChangedAction.Remove:
                    break;
                case NotifyCollectionChangedAction.Replace:
                case NotifyCollectionChangedAction.Move:
                case NotifyCollectionChangedAction.Reset:
                case NotifyCollectionChangedAction.Batch:
                    break;
                case NotifyCollectionChangedAction.ItemChanging:
                case NotifyCollectionChangedAction.ItemChanged:
                    break;
            }
        }

        private void GridViewEx_RowsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
        }

        private void GridViewEx_CellEditorInitialized(object sender, GridViewCellEventArgs e)
        {
        }

        private void GridViewEx_CellFormatting(object sender, Telerik.WinControls.UI.CellFormattingEventArgs e)
        {
            try
            {
                GridViewDataColumn column = e.CellElement.ColumnInfo as GridViewDataColumn;
                if (column.GetType() == typeof(GridViewDateTimeColumn))
                {
                    var value = e.CellElement.RowInfo.Cells[column.Name].Value;
                    DateTime dtValue = DateTime.MinValue;
                    if (value != null) dtValue = Convert.ToDateTime(value);

                    if (dtValue == DateTime.MinValue) e.CellElement.Text = "";
                }
            }
            catch (Exception ex)
            {
                XLogger.Instance.Fatal(ex);
            }
        }

        private void GridViewEx_CellBeginEdit(object sender, GridViewCellCancelEventArgs e)
        {
            DataTableBase data = e.Row.DataBoundItem as DataTableBase;
            if (data == null) return;

            // Check PrimaryKey Property
            PropertyInfo prop = null;
            PrimaryKeyAttribute attrPK = null;
            prop = data.GetType().GetProperty(e.Column.FieldName);
            if (prop != null) attrPK = prop.GetCustomAttribute(typeof(PrimaryKeyAttribute)) as PrimaryKeyAttribute;
            if (attrPK != null && attrPK.PrimaryKey)
            {
                e.Cancel = true;
                return;
            }
        }

        private void GridViewEx_CellEndEdit(object sender, GridViewCellEventArgs e)
        {
        }

        private void GridViewEx_ViewCellFormatting(object sender, Telerik.WinControls.UI.CellFormattingEventArgs e)
        {
            try
            {
                if (this.ShowRowNumber == false) return;

                if (e.CellElement is GridRowHeaderCellElement && e.Row is GridViewDataRowInfo)
                {
                    e.CellElement.Text = (e.CellElement.RowIndex + 1).ToString();
                    e.CellElement.TextImageRelation = TextImageRelation.ImageBeforeText;
                }
                else
                {
                    e.CellElement.ResetValue(LightVisualElement.TextImageRelationProperty, ValueResetFlags.Local);
                }
            }
            catch (Exception ex)
            {
                XLogger.Instance.Fatal(ex);
            }
        }

        #endregion

        #region [ Methods ] ===================================================

        #region [ AutoBinding ] -----------------------------------------------

        /// <summary>
        /// Add Column
        /// </summary>
        /// <param name="fieldName"></param>
        /// <param name="headerText"></param>
        /// <param name="readOnly"></param>
        /// <returns></returns>
        public GridViewDataColumn AddColumn(string fieldName, string headerText = "", bool readOnly = true, string formatString = "")
        {

            this.IsSetColumn = false;
            if (string.IsNullOrEmpty(headerText))
            {
                headerText = fieldName.Replace('_', ' ');
                if (HeaderTextToTitleCase)
                {
                    TextInfo textIF = CultureInfo.CurrentCulture.TextInfo;
                    headerText = textIF.ToTitleCase(headerText.ToLower());
                }
            }

            GridViewDataColumn column = null;
            {
                GridViewTextBoxColumn temp = new GridViewTextBoxColumn();
                temp.FieldName = fieldName;
                temp.HeaderText = headerText;
                temp.ReadOnly = readOnly;
                temp.FormatString = formatString;
                column = temp;
            }

            this.Columns.Add(column);

            return column;
        }

        public GridViewDataColumn AddColumn(Type type, string fieldName, string headerText = "", bool readOnly = true, string formatString = "")
        {
            GridViewDataColumn column = Activator.CreateInstance(type) as GridViewDataColumn;
            if (column == null) return null;

            this.IsSetColumn = false;
            if (string.IsNullOrEmpty(headerText))
            {
                TextInfo textIF = CultureInfo.TextInfo;
                headerText = textIF.ToTitleCase(fieldName.Replace('_', ' ').ToLower());
            }

            column.FieldName = fieldName;
            column.HeaderText = headerText;
            column.ReadOnly = readOnly;
            column.FormatString = formatString;

            this.Columns.Add(column);

            return column;
        }

        /// <summary>
        /// Add ColumnGroup
        /// </summary>
        /// <param name="columnNames"></param>
        /// <param name="showHeader"></param>
        /// <param name="name"></param>
        /// <param name="rowIndex"></param>
        /// <param name="groupParent"></param>
        /// <returns></returns>
        public GridViewColumnGroup AddColumnGroup(List<string> columnNames, bool showHeader, string name, GridViewColumnGroup groupParent = null, int rowIndex = 0)
        {
            if (this.ViewDefinition.GetType() != typeof(ColumnGroupsViewDefinition)) this.ViewDefinition = new ColumnGroupsViewDefinition();
            ColumnGroupsViewDefinition view = this.ViewDefinition as ColumnGroupsViewDefinition;

            GridViewColumnGroup group = null;
            if (groupParent != null)
            {
                if (!groupParent.Groups.Contains(name)) groupParent.Groups.Add(new GridViewColumnGroup(name, name));
                group = groupParent.Groups[name];
            }
            else
            {
                if (!view.ColumnGroups.Contains(name)) view.ColumnGroups.Add(new GridViewColumnGroup(name, name));
                group = view.ColumnGroups[name];
            }

            group.ShowHeader = showHeader;

            while (group.Rows.Count <= rowIndex)
                group.Rows.Add(new GridViewColumnGroupRow());

            foreach (string columnName in columnNames)
                group.Rows[rowIndex].ColumnNames.Add(columnName);

            return group;
        }

        /// <summary>
        /// Binding data to defined columns.
        /// </summary>
        /// <param name="data"></param>
        public void AutoBinding(DataTableBase[] data, bool reset = false)
        {
            try
            {
                this.DataBindingItems.Clear();
                if (data == null) return;

                this.DataBindingItems.AddRange(data);
                if (this.InvokeRequired)
                    Invoke((MethodInvoker)delegate () { this.AutoBinding_Execute(this.DataBindingItems, reset); });
                else
                    this.AutoBinding_Execute(this.DataBindingItems, reset);
            }
            catch (Exception ex)
            {
                XLogger.Instance.Fatal(ex);
            }
        }

        private void AutoBinding_Execute(List<object> dataBoundItems, bool reset)
        {
            this.DataSource = null;
            if (dataBoundItems == null || dataBoundItems.Count < 1) return;

            // Column Setting
            if (reset || this.IsSetColumn == false)
            {
                // Backup Columns
                Dictionary<string, GridViewDataColumn> lstColumn = new Dictionary<string, GridViewDataColumn>();
                foreach (GridViewDataColumn column in this.Columns)
                    lstColumn.Add(column.Name, column);

                // Create Columns
                this.Columns.Clear();
                TextInfo textIF = CultureInfo.TextInfo;

                DataTableBase tempData = dataBoundItems[0] as DataTableBase;
                foreach (PropertyInfo property in tempData.GetType().GetProperties())
                {
                    GridViewDataColumn column = null;

                    #region [ Create GridViewDataColumn ]
                    if (column == null && this.ComboBoxCollection.ContainsKey(property.Name))
                    {
                        column = new GridViewComboBoxColumn()
                        {
                            TextAlignment = ContentAlignment.MiddleCenter,
                            DataSource = this.ComboBoxCollection[property.Name],
                            DisplayMember = "Display",
                            ValueMember = "Value"
                        };
                    }
                    if (column == null && property.PropertyType == typeof(DateTime))
                    {
                        column = new GridViewDateTimeColumn()
                        {
                            TextAlignment = ContentAlignment.MiddleCenter,
                            FormatInfo = CultureInfo,
                            FormatString = "{0:" + CultureInfo.DateTimeFormat.ShortDatePattern + " HH:mm:ss}"
                        };
                    }
                    if (column == null && (property.PropertyType == typeof(int) || property.PropertyType == typeof(Int64)))
                    {
                        column = new GridViewDecimalColumn()
                        {
                            TextAlignment = ContentAlignment.MiddleRight,
                            FormatInfo = CultureInfo,
                            FormatString = "{0:#,##0}",
                        };
                        GridViewDecimalColumn decimalColumn = column as GridViewDecimalColumn;
                        decimalColumn.DecimalPlaces = 0;
                        decimalColumn.ThousandsSeparator = true;
                    }
                    if (column == null && (property.PropertyType == typeof(decimal) || property.PropertyType == typeof(Decimal)))
                    {
                        column = new GridViewDecimalColumn()
                        {
                            TextAlignment = ContentAlignment.MiddleRight,
                            FormatInfo = CultureInfo,
                            FormatString = "{0:#,##0.#####}",
                        };
                        GridViewDecimalColumn decimalColumn = column as GridViewDecimalColumn;
                        decimalColumn.DecimalPlaces = 6;
                        decimalColumn.ThousandsSeparator = true;
                    }
                    if (column == null && property.PropertyType == typeof(double))
                    {
                        column = new GridViewDecimalColumn()
                        {
                            TextAlignment = ContentAlignment.MiddleRight,
                            FormatInfo = CultureInfo,
                            FormatString = "{0:#,##0.####}",
                        };
                        GridViewDecimalColumn decimalColumn = column as GridViewDecimalColumn;
                        decimalColumn.DecimalPlaces = 6;
                        decimalColumn.ThousandsSeparator = true;
                    }
                    if (column == null)
                    {
                        column = new GridViewTextBoxColumn()
                        {
                            TextAlignment = ContentAlignment.MiddleLeft,
                            WrapText = true
                        };
                    }
                    #endregion

                    column.FieldName = property.Name;
                    column.MinWidth = 40;

                    if (lstColumn.ContainsKey(property.Name))
                    {
                        if (column.GetType() == lstColumn[property.Name].GetType())
                        {
                            lstColumn[property.Name].PropertiesCopy(column);
                        }
                        else
                        {
                            if (lstColumn.ContainsKey(property.Name) && lstColumn[property.Name].GetType() != typeof(GridViewTextBoxColumn))
                                column = lstColumn[property.Name];

                            column.HeaderText = lstColumn[property.Name].HeaderText;
                            column.ReadOnly = lstColumn[property.Name].ReadOnly;
                            column.IsVisible = lstColumn[property.Name].IsVisible;
                            if (!string.IsNullOrEmpty(lstColumn[property.Name].FormatString))
                                column.FormatString = lstColumn[property.Name].FormatString;
                        }
                    }
                    else
                    {
                        column.HeaderText = textIF.ToTitleCase(property.Name.Replace('_', ' ').ToLower());
                        column.ReadOnly = true;
                        if (lstColumn.Count > 0) column.IsVisible = false;
                    }

                    this.Columns.Add(column);
                }

                // Setting Columns
                int index = 0;
                foreach (string name in lstColumn.Keys)
                {
                    if (this.Columns.Contains(name) == false) this.Columns.Add(lstColumn[name]);
                    this.Columns.Move(this.Columns[name].Index, index++);
                }
                lstColumn.Clear();

                this.IsSetColumn = true;
            }

            this.SuspendLayout();
            this.DataSource = dataBoundItems;
            this.ResumeLayout();
        }

        #endregion

        #region [ SelectRow ] -------------------------------------------------

        /// <summary>
        /// Set CurrentRow
        /// </summary>
        /// <param name="name">Cell.Name</param>
        /// <param name="value">Cell.Value</param>
        /// <returns></returns>
        public GridViewRowInfo SetCurrentRow(string name, string value)
        {
            try
            {
                var row = this.GetRows(name, value).FirstOrDefault();
                if (row != null) this.CurrentRow = row;

                return row;
            }
            catch
            {
                return null;
            }
        }

        /// <summary>
        /// Search Rows
        /// </summary>
        /// <param name="name">Cell.Name</param>
        /// <param name="value">Cell.Value</param>
        /// <returns></returns>
        public GridViewRowInfo[] GetRows(string name, string value)
        {
            try
            {
                if (this.Rows.Count < 1) return null;
                var rows = this.Rows.Where(item => item.Cells[name].Value.ToString() == value).ToArray();

                return rows;
            }
            catch
            {
                return null;
            }
        }

        #endregion

        #region [ SetRowForeColor ] -------------------------------------------

        public void SetRowForeColor(string columnName, string condition, Color color)
        {
            if (!this.Columns.Contains(columnName)) return;

            string name = columnName;
            foreach (BaseFormattingObject item in this.Columns[columnName].ConditionalFormattingObjectList)
            {
                if (item.Name == name)
                {
                    if (item.GetType() == typeof(ExpressionFormattingObject))
                    {
                        ExpressionFormattingObject obj = item as ExpressionFormattingObject;
                        if (obj.Expression == condition) return;
                    }
                }
            }

            ExpressionFormattingObject formatting = new ExpressionFormattingObject(name, condition, true);
            formatting.RowForeColor = color;
            this.Columns[columnName].ConditionalFormattingObjectList.Add(formatting);
        }

        public void SetCellForeColor(string columnName, string condition, Color color)
        {
            if (!this.Columns.Contains(columnName)) return;

            string name = columnName;
            foreach (BaseFormattingObject item in this.Columns[columnName].ConditionalFormattingObjectList)
            {
                if (item.Name == name)
                {
                    if (item.GetType() == typeof(ExpressionFormattingObject))
                    {
                        ExpressionFormattingObject obj = item as ExpressionFormattingObject;
                        if (obj.Expression == condition) return;
                    }
                }
            }

            ExpressionFormattingObject formatting = new ExpressionFormattingObject(name, condition, true);
            formatting.CellForeColor = color;
            this.Columns[columnName].ConditionalFormattingObjectList.Add(formatting);
        }

        public void SetRowBackColor(string columnName, string condition, Color color)
        {
            if (!this.Columns.Contains(columnName)) return;

            string name = columnName;
            foreach (BaseFormattingObject item in this.Columns[columnName].ConditionalFormattingObjectList)
            {
                if (item.Name == name)
                {
                    if (item.GetType() == typeof(ExpressionFormattingObject))
                    {
                        ExpressionFormattingObject obj = item as ExpressionFormattingObject;
                        if (obj.Expression == condition) return;
                    }
                }
            }

            ExpressionFormattingObject formatting = new ExpressionFormattingObject(name, condition, true);
            formatting.RowBackColor = color;
            this.Columns[columnName].ConditionalFormattingObjectList.Add(formatting);
        }

        #endregion

        #region [ ContextMenu ] -----------------------------------------------

        /// <summary>
        /// 
        /// </summary>
        /// <param name="text"></param>
        /// <param name="method"></param>
        /// <param name="separator"></param>
        /// <param name="args"></param>
        public RadMenuItem AddContextMenu(string text, ContextMenuHandler method = null, bool separator = false, object[] args = null)
        {
            if (this.ContextMenu == null) this.ContextMenu = new RadContextMenu();

            if (string.IsNullOrWhiteSpace(text)) { this.ContextMenu.Items.Add(new RadMenuSeparatorItem()); return null; }
            if (separator) this.ContextMenu.Items.Add(new RadMenuSeparatorItem());

            RadMenuItem item = new RadMenuItem(text);
            item.Name = text;
            if (method != null) item.Click += new EventHandler(delegate (object sender, EventArgs arg) { method(item, args); });

            this.ContextMenu.Items.Add(item);
            return item;
        }

        public RadMenuItem InsertContextMenu(int index, string text, ContextMenuHandler method = null, bool separator = false, object[] args = null)
        {
            if (this.ContextMenu == null) this.ContextMenu = new RadContextMenu();

            if (string.IsNullOrWhiteSpace(text)) { this.ContextMenu.Items.Add(new RadMenuSeparatorItem()); return null; }
            if (separator) this.ContextMenu.Items.Add(new RadMenuSeparatorItem());

            RadMenuItem item = new RadMenuItem(text);
            item.Name = text;
            if (method != null) item.Click += new EventHandler(delegate (object sender, EventArgs arg) { method(item, args); });

            if (index > this.ContextMenu.Items.Count) index = this.ContextMenu.Items.Count;
            this.ContextMenu.Items.Insert(index, item);
            return item;
        }

        public RadMenuItem AddContextMenu(string text, bool isChecked, ContextMenuHandler method = null, bool separator = false, object[] args = null)
        {
            RadMenuItem item = this.AddContextMenu(text, method, separator, args);
            item.CheckOnClick = true;
            item.IsChecked = isChecked;

            return item;
        }

        public RadMenuItem AddContextMenu(RadMenuItem parent, string text, ContextMenuHandler method = null, bool separator = false, object[] args = null)
        {
            if (separator) parent.Items.Add(new RadMenuSeparatorItem());

            RadMenuItem item = new RadMenuItem(text);
            item.Name = text;
            if (method != null) item.Click += new EventHandler(delegate (object sender, EventArgs arg) { method(args); });

            parent.Items.Add(item);
            return item;
        }

        public RadMenuItem InsertContextMenu(RadMenuItem parent, int index, string text, ContextMenuHandler method = null, bool separator = false, object[] args = null)
        {
            if (string.IsNullOrWhiteSpace(text)) { parent.Items.Insert(index, new RadMenuSeparatorItem()); return null; }
            if (separator) parent.Items.Add(new RadMenuSeparatorItem());

            RadMenuItem item = new RadMenuItem(text);
            item.Name = text;
            if (method != null) item.Click += new EventHandler(delegate (object sender, EventArgs arg) { method(args); });

            parent.Items.Insert(index, item);
            return item;
        }

        public bool RemoveContextMenu(string text)
        {
            foreach (RadItem item in this.ContextMenu.Items)
            {
                if (item.Text == text)
                {
                    this.ContextMenu.Items.Remove(item);
                    return true;
                }
            }
            return false;
        }

        #endregion

        #region [ ExportExcel ] -----------------------------------------------

        public delegate void ExportExcelClickHandler(object sender, ExportExcelClickArgument arg);

        public event ExportExcelClickHandler ExportExcelClick;

        private void ExportExcelDialog(object sender, params object[] args)
        {
            ExportExcelClickArgument arg = new ExportExcelClickArgument();
            if (this.ExportExcelClick != null) this.ExportExcelClick(this, arg);
            if (arg.Cancel) return;

            SaveFileDialog dlg = new SaveFileDialog();
            dlg.InitialDirectory = $"{arg.InitialDirectory}";
            dlg.Filter = "Excel File|*.xlsx|All Childs|*.*";
            dlg.DefaultExt = "xlsx";
            dlg.FileName = $"{arg.FileName}";
            if (dlg.ShowDialog() == DialogResult.OK)
            {
                this.ExportExcel(dlg.FileName);
                if (arg.FolderOpen)
                {
                    string command = $"/select,{dlg.FileName}";
                    System.Diagnostics.Process.Start($"explorer.exe", command);
                }
            }
        }

        public void ExportExcel(string fileName)
        {
            try
            {
                GridViewSpreadStreamExport export = new GridViewSpreadStreamExport(this);
                export.HiddenColumnOption = Telerik.WinControls.UI.Export.HiddenOption.DoNotExport;
                export.HiddenRowOption = Telerik.WinControls.UI.Export.HiddenOption.DoNotExport;
                export.FreezeHeaderRow = true;

                export.RunExport(fileName, new SpreadStreamExportRenderer());
            }
            catch (Exception ex)
            {
                XLogger.Instance.Fatal(ex);
                throw ex;
            }
        }

        #endregion

        #region [ ColumnVisible ] ---------------------------------------------

        public RadMenuItem AddContextMenuColumnVisible()
        {
            TextInfo textIF = CultureInfo.TextInfo;

            RadMenuItem menu = this.ContextMenu.Items["COLUMNS_VISIBLE"] as RadMenuItem;
            if (menu == null)
            {
                menu = new RadMenuItem("Columns Visible");
                menu.Name = "COLUMNS_VISIBLE";
                this.ContextMenu.Items.Add(menu);
            }

            foreach (GridViewDataColumn column in this.Columns)
            {
                string text = textIF.ToTitleCase(column.Name.Replace('_', ' ').ToLower());
                if (column.Tag != null && column.Tag.GetType() == typeof(string)) text = textIF.ToTitleCase(((string)column.Tag).Replace('_', ' ').ToLower());

                RadMenuItem item = new RadMenuItem(text);
                item.CheckOnClick = true;
                item.IsChecked = false;
                item.Tag = column.Name;
                item.Click += this.ColumnVisible_Click;
                if (column.IsVisible) item.IsChecked = true;

                menu.Items.Add(item);
            }

            return menu;
        }

        public RadMenuItem SetValueColumnVisible()
        {
            RadMenuItem menu = this.ContextMenu.Items["COLUMNS_VISIBLE"] as RadMenuItem;
            if (menu == null) return null;

            foreach (RadMenuItem item in menu.Items)
            {
                if (item.Tag == null || item.Tag.GetType() != typeof(string)) continue;
                string columnName = item.Tag.ToString();
                if (!this.Columns.Contains(columnName)) continue;

                GridViewDataColumn column = this.Columns[columnName];
                item.IsChecked = column.IsVisible;
            }

            return menu;
        }

        private void ColumnVisible_Click(object sender, EventArgs e)
        {
            RadMenuItem item = (RadMenuItem)sender;
            string name = item.Tag as string;
            GridViewDataColumn column = this.Columns[name];
            column.IsVisible = item.IsChecked;
            //this.BestFitColumns();
        }

        private void ColumnVisible(params object[] args)
        {

        }

        #endregion

        #region [ Grouping ] --------------------------------------------------

        private object PerformGrouping(GridViewRowInfo row, int level)
        {
            GroupDescriptor groupDescriptor = this.GroupDescriptors[level];
            object[] key = new object[groupDescriptor.GroupNames.Count];

            for (int i = 0; i < groupDescriptor.GroupNames.Count; i++)
            {
                SortDescriptor descriptor = groupDescriptor.GroupNames[i];
                int index = descriptor.PropertyIndex;
                if (index < 0) continue;

                key[i] = this.GetGroupKey(row, descriptor);
            }

            return key;
        }

        public virtual object GetGroupKey(GridViewRowInfo row, SortDescriptor descriptor)
        {
            int index = descriptor.PropertyIndex;
            object keyValue = row.Cells[index].Value;
            GridViewDateTimeColumn column = row.Cells[index].ColumnInfo as GridViewDateTimeColumn;

            if (keyValue == DBNull.Value)
            {
                keyValue = null;
            }
            else if (column != null)
            {
                DateTime value = (DateTime)keyValue;
                StringBuilder sb = new StringBuilder();
                if (this.DateTimeGroupKey != eDateTimeGroupKey.None)
                {
                    if ((this.DateTimeGroupKey & eDateTimeGroupKey.Year) > 0) sb.Append("yyyy");
                    if ((this.DateTimeGroupKey & eDateTimeGroupKey.Month) > 0)
                        if ((this.DateTimeGroupKey & eDateTimeGroupKey.Year) > 0) sb.Append("-MM");
                        else sb.Append("MM");
                    if ((this.DateTimeGroupKey & eDateTimeGroupKey.Day) > 0)
                        if ((this.DateTimeGroupKey & eDateTimeGroupKey.Month) > 0) sb.Append("-dd");
                        else sb.Append("dd");
                    if ((this.DateTimeGroupKey & eDateTimeGroupKey.Hour) > 0)
                        if (sb.Length > 0) sb.Append(" HH");
                        else sb.Append("HH");
                    if ((this.DateTimeGroupKey & eDateTimeGroupKey.Minute) > 0)
                        if ((this.DateTimeGroupKey & eDateTimeGroupKey.Hour) > 0) sb.Append(":mm");
                        else sb.Append("mm");
                    if ((this.DateTimeGroupKey & eDateTimeGroupKey.Second) > 0)
                        if ((this.DateTimeGroupKey & eDateTimeGroupKey.Minute) > 0) sb.Append(":ss");
                        else sb.Append("ss");
                }
                keyValue = ((DateTime)keyValue).ToString(sb.ToString());
            }

            return keyValue;
        }

        #endregion

        #region [ ETC ] -------------------------------------------------------

        public int GetVisualRows_FirstDataRowIndex()
        {
            int index = -1;
            try
            {
                foreach (GridRowElement element in this.TableElement.VisualRows)
                {
                    if (element.RowInfo is GridViewDataRowInfo)
                    {
                        index = element.RowInfo.Index;
                        break;
                    }
                }

                return index;
            }
            catch (Exception ex)
            {
                XLogger.Instance.Fatal(ex);
                return index;
            }
        }

        private void Clipboard_CurrentCellValue(object sender, params object[] args)
        {
            try
            {
                Clipboard.SetText($"{this.CurrentCell?.Text}");
            }
            catch
            {
            }
        }

        private void Clipboard_CurrentRowValue(object sender, params object[] args)
        {
            try
            {
                StringBuilder sbRow = new StringBuilder();
                foreach (GridViewRowInfo row in this.SelectedRows)
                {
                    StringBuilder sbCell = new StringBuilder();
                    foreach (GridViewCellInfo cell in row.Cells)
                    {
                        if (!cell.ColumnInfo.IsVisible) continue;
                        if (sbCell.Length > 0) sbCell.Append("\t");
                        sbCell.AppendFormat($"{cell?.Value}");
                    }
                    sbRow.AppendLine(sbCell.ToString());
                }
                Clipboard.SetText(sbRow.ToString());
            }
            catch
            {
            }
        }

        private void ShowColumnChooser(object sender, params object[] args)
        {
            this.ShowColumnChooser();
        }

        #endregion

        #endregion

    }

    public class ComboBoxItem
    {

        public string Display { get; set; } = string.Empty;

        public object Value { get; set; } = null;

        public ComboBoxItem()
        {
        }

        public ComboBoxItem(string display, object value)
        {
            this.Display = display;
            this.Value = value;
        }

    }

    public class ExportExcelClickArgument
    {

        public bool Cancel { get; set; } = false;

        public string InitialDirectory { get; set; } = $"{Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments)}";

        public string FileName { get; set; } = $"";

        public bool FolderOpen { get; set; } = true;

        public ExportExcelClickArgument()
        {
        }

    }

    public class CustomerExpressionContext : Telerik.Data.Expressions.ExpressionContext
    {

        public string Int2Hex(string strDecimal)
        {
            int value = 0;
            if (int.TryParse(strDecimal, out value) == false) return string.Empty;
            return $"0x{value.ToString("x")}";
        }

    }

}
