using System;
using System.Globalization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.ComponentModel;
using C1.Silverlight.FlexGrid;
using C1.Silverlight.FlexGridExcel;

namespace ExcelGrid
{
    public partial class MainPage : UserControl
    {
        //-----------------------------------------------------------------------------
        #region ** fields

        enum CustomContextCommands
        {
            UpperCase,
            LowerCase,
            MakeOdd,
            MakeEven,
            MakeZero,
            True,
            False,
            Toggle
        }

        #endregion

        //-----------------------------------------------------------------------------
        #region ** ctor

        public MainPage()
        {
            InitializeComponent();

            // set language to match the system settings
            this.Language = System.Windows.Markup.XmlLanguage.GetLanguage(CultureInfo.CurrentCulture.Name);

            // populate the grid
            var products = Product.GetProducts(250);
            products[0].Price = -10;
            products[1].Price = -10;
            _flex.ItemsSource = products;
            _flex.ShowBottomGroupRows = true;

            // enable editing lists
            _flex.Columns["Line"].ValueConverter = new ColumnValueConverter(Product.GetLines(), false);
            _flex.Columns["Color"].ValueConverter = new ColumnValueConverter(Product.GetColors(), false);

            // add custom sort descriptions
            // (commented out since this is for use with unbound grids only)
            //_flex.ColumnSortDescriptions.Add(new ColumnSortDescription(_flex.Columns[0], ListSortDirection.Ascending));
            //_flex.ColumnSortDescriptions.Add(new ColumnSortDescription(_flex.Columns[1], ListSortDirection.Descending));

            // customize the grid header row 
            // (commented out since this was already done in the XMAL file, but could also be done here)
            //var cs = new CellStyle();
            //cs.Background = new SolidColorBrush(Color.FromArgb(0xff, 0x80, 0x80, 0xc0));
            //cs.Foreground = new SolidColorBrush(Color.FromArgb(0xff, 0xff, 0xcf, 0x00));
            //cs.FontStyle = FontStyles.Italic;
            //cs.HorizontalAlignment = HorizontalAlignment.Left;
            //_flex.HeaderRowStyle = cs;

            // set up event handler to update the selection status
            _flex.SelectionChanged += (s, e) =>
            {
                var text = "Ready";
                var sel = _flex.Selection;
                if (!sel.IsSingleCell)
                {
                    var count = (int)_flex.GetAggregate(Aggregate.Count, sel);
                    var sum = _flex.GetAggregate(Aggregate.Sum, sel);
                    text = string.Format("{0} data cell{1} selected.", count, count == 1 ? string.Empty : "s");
                    if (sum > 0)
                    {
                        text += string.Format(" Sum: {0:#,##0.##}", sum);
                    }
                }
                _tbStatus.Text = text;
            };

            // give grid the focus when the app loads
            Loaded += (s, e) =>
            {
                if (!Application.Current.IsRunningOutOfBrowser)
                {
                    System.Windows.Browser.HtmlPage.Plugin.Focus();
                }
                _flex.Focus();
            };

            // create custom menu items
            CustomizeContextMenu();

            // optionally show extra group rows below the data in addition to
            // the regular group rows above the data
            _flex.ShowBottomGroupRows = true;
            _flex.BottomGroupRowCaption = "Summary for {0}: {1} ({2} item{3})";

            // optionally show outline bar on top left cell
            //_flex.ShowOutlineBar = true;

            // optionally defer scrolling to make things faster
            //this._flex.DeferScrolling = DeferScrolling.DeferBoth;

            // demonstrate using Excel range notation
            //var val = _flex["B4"];
            //_flex["B4"] = "Orange";
            //System.Diagnostics.Debug.WriteLine("cell 'b4' was '{0}' and is now '{1}'", val, _flex["b4"]);
        }

        #endregion

        //-----------------------------------------------------------------------------
        #region ** file

        void _btnSave_Click(object sender, RoutedEventArgs e)
        {
            var dlg = new SaveFileDialog();
            dlg.DefaultExt = "xlsx";
            dlg.Filter = 
                "Excel Workbook (*.xlsx)|*.xlsx|" +
                "HTML File (*.htm;*.html)|*.htm;*.html|" +
                "Comma Separated Values (*.csv)|*.csv|" +
                "Text File (*.txt)|*.txt|" +
                "PDF (*.pdf)|*.pdf";
            if (dlg.ShowDialog().Value)
            {
                using (var s = dlg.OpenFile())
                {
                    // dlg.FilterIndex is not working properly (SL bug) so we check
                    // the file extension to chose the proper save format.
                    var ext = System.IO.Path.GetExtension(dlg.SafeFileName).ToLower();
                    switch (ext)
                    {
                        case ".htm":
                        case ".html":
                            _flex.Save(s, FileFormat.Html, SaveOptions.Formatted);
                            break;
                        case ".csv":
                            _flex.Save(s, FileFormat.Csv, SaveOptions.Formatted);
                            break;
                        case ".txt":
                            _flex.Save(s, FileFormat.Text, SaveOptions.Formatted);
                            break;
                        case ".pdf":
                            _flex.SavePdf(s);
                            break;
                        case ".xlsx":
                        default:
                            _flex.SaveXlsx(s);
                            break;
                    }
                }
            }
        }
        void _btnSaveText_Click(object sender, RoutedEventArgs e)
        {
            var dlg = new SaveFileDialog();
            dlg.DefaultExt = "txt";
            dlg.Filter = "Text files (*.txt)|*.txt";
            if (dlg.ShowDialog().Value)
            {
                using (var s = dlg.OpenFile())
                {
                    _flex.Save(s, FileFormat.Text, SaveOptions.Formatted);
                }
            }
        }
        void _btnSaveHtml_Click(object sender, RoutedEventArgs e)
        {
            var dlg = new SaveFileDialog();
            dlg.DefaultExt = "html";
            dlg.Filter = "HTML files (*.htm;*.html)|*.htm;*.html";
            if (dlg.ShowDialog().Value)
            {
                using (var s = dlg.OpenFile())
                {
                    _flex.Save(s, FileFormat.Html, SaveOptions.Formatted);
                }
            }
        }
        void _btnSavePdf_Click(object sender, RoutedEventArgs e)
        {
            var dlg = new SaveFileDialog();
            dlg.DefaultExt = "pdf";
            dlg.Filter = "PDF files (*.pdf)|*.pdf";
            if (dlg.ShowDialog().Value)
            {
                using (var s = dlg.OpenFile())
                {
                    _flex.SavePdf(s);
                }
            }
        }

        #endregion

        //-----------------------------------------------------------------------------
        #region ** clipboard

        void _btnPaste_Click(object sender, RoutedEventArgs e)
        {
            // create undoable cell range action and record the cell values
            // of the current selection before changing them
            // var cellEditAction = new CellRangeEditAction(_flex);
            // cellEditAction.RecordUndoState(GetClipboardRange());

            _flex.Paste();

            // record the cell values after the changes and add the
            // undoable action to the undo stack
            // cellEditAction.RecordRedoState(GetClipboardRange());
            // _flex.UndoStack.AddAction(cellEditAction);
        }
        void _btnCut_Click(object sender, RoutedEventArgs e)
        {
            // create undoable cell range action and record the cell values
            // of the current selection before changing them
            var cellEditAction = new CellRangeEditAction(_flex);
            cellEditAction.RecordUndoState(_flex.Selection);
            
            _flex.Copy();
            foreach (var cell in _flex.Selection.Cells)
            {
                try
                {
                    _flex[cell.Row, cell.Column] = null;
                }
                catch { }
            }

            // record the cell values after the changes and add the
            // undoable action to the undo stack
            cellEditAction.RecordRedoState(_flex.Selection);
            _flex.UndoStack.AddAction(cellEditAction);
        }
        void _btnCopy_Click(object sender, RoutedEventArgs e)
        {
            _flex.Copy();
        }
        CellRange GetClipboardRange()
        {
            // get current location
            int row = _flex.Selection.TopRow;
            int col = _flex.Selection.LeftColumn;
            CellRange range = new CellRange(row, col);

            // split text into lines at line breaks
            string text = System.Windows.Clipboard.GetText().Replace("\r\n", "\r").Trim();
            var lines = text.Split('\r', '\n');
            range.Row2 = lines.Length + row - 1;
            if (lines.Length > 0)
            {
                // split line to get column number
                var line = lines[0];
                var cells = line.Split('\t');
                range.Column2 = cells.Length + col - 1;
            }
            return range;
        }

        #endregion

        //-----------------------------------------------------------------------------
        #region ** undo/redo

        void _btnUndo_Click(object sender, RoutedEventArgs e)
        {
            _flex.Undo();
        }
        void _btnRedo_Click(object sender, RoutedEventArgs e)
        {
            _flex.Redo();
        }

        #endregion

        //-----------------------------------------------------------------------------
        #region ** custom context menu

        void CustomizeContextMenu()
        {
            // create custom items
            _flex.ContextMenu.Items.Add(new Separator());

            // populate
            AddMenuItem("MAKE UPPER CASE", CustomContextCommands.UpperCase);
            AddMenuItem("make lower case", CustomContextCommands.LowerCase);
            AddMenuItem("Make Odd", CustomContextCommands.MakeOdd);
            AddMenuItem("Make Even", CustomContextCommands.MakeEven);
            AddMenuItem("Make Zero", CustomContextCommands.MakeZero);
            AddMenuItem("Make True", CustomContextCommands.True);
            AddMenuItem("Make False", CustomContextCommands.False);
            AddMenuItem("Toggle Value", CustomContextCommands.Toggle);

            // select items to display when showing the menu
            _flex.ContextMenu.Opened += ContextMenu_Opened;

            // add a menu with sub-items to show it's possible
            // NOTE: requires C1MenuItem, the MS Toolkit MenuItem class doesn't support this
            var newItem = new C1MenuItem();
            newItem.Header = "Country";
            foreach (string country in new string[] { "Austria", "Belgium", "Croatia", "Denmark", "Others" })
            {
                var subItem = new C1MenuItem();
                subItem.Header = country;
                subItem.Click += subItem_Click;
                newItem.Items.Add(subItem);
            }

            // add new item to context menu 
            _flex.ContextMenu.Items.Insert(0, newItem);
            _flex.ContextMenu.Items.Insert(1, new Separator());
        }

        // sub-menu clicked:
        void subItem_Click(object sender, RoutedEventArgs e)
        {
 	        var mi = sender as C1MenuItem;
            var msg = string.Format("Thank you for selecting '{0}'.", mi.Header as string);
            MessageBox.Show(msg);
        }

        // select items to display when showing the menu
        void ContextMenu_Opened(object sender, EventArgs e)
        {
            // get current selection
            var sel = _flex.Selection;
            var type = _flex.Columns[sel.Column].DataType;

            // show/hide commands depending on selection type
            foreach (var item in _flex.ContextMenu.Items)
            {
                C1MenuItem mi = item as C1MenuItem;
                if (mi != null && mi.Tag is CustomContextCommands)
                {
                    // hide options that don't apply
                    switch ((CustomContextCommands)mi.Tag)
                    {
                        case CustomContextCommands.UpperCase:
                        case CustomContextCommands.LowerCase:
                            mi.Visibility = type == typeof(string) 
                                ? Visibility.Visible
                                : Visibility.Collapsed;
                            break;
                        case CustomContextCommands.MakeEven:
                        case CustomContextCommands.MakeOdd:
                        case CustomContextCommands.MakeZero:
                            mi.Visibility = IsNumeric(type) 
                                ? Visibility.Visible
                                : Visibility.Collapsed;
                            break;
                        case CustomContextCommands.True:
                        case CustomContextCommands.False:
                        case CustomContextCommands.Toggle:
                            mi.Visibility = type == typeof(bool) || type == typeof(bool?)
                                ? Visibility.Visible
                                : Visibility.Collapsed;
                            break;
                    }
                }
            }
        }
        void AddMenuItem(string header, CustomContextCommands cmd)
        {
            var mi = new C1MenuItem();
            mi.Header = header;
            mi.Tag = cmd;
            mi.Click += mi_Click;
            _flex.ContextMenu.Items.Add(mi);
        }

        // execute context menu command
        void mi_Click(object sender, RoutedEventArgs e)
        {
            var mi = sender as C1MenuItem;
            if (mi != null && mi.Tag is CustomContextCommands)
            {
                // get current value in the cell
                var sel = _flex.Selection;
                object value = _flex[sel.Row, sel.Column];

                // perform command
                switch ((CustomContextCommands)mi.Tag)
                {
                    case CustomContextCommands.UpperCase:
                        if (value != null)
                        {
                            value = value.ToString().ToUpper();
                        }
                        break;
                    case CustomContextCommands.LowerCase:
                        if (value != null)
                        {
                            value = value.ToString().ToLower();
                        }
                        break;
                    case CustomContextCommands.MakeEven:
                        try
                        {
                            int i = (int)Convert.ChangeType(value, typeof(int), CultureInfo.CurrentCulture);
                            value = i % 2 == 0 ? i : i + 1;
                        }
                        catch { }
                        break;
                    case CustomContextCommands.MakeOdd:
                        try
                        {
                            int i = (int)Convert.ChangeType(value, typeof(int), CultureInfo.CurrentCulture);
                            value = i % 2 != 0 ? i : i + 1;
                        }
                        catch { }
                        break;
                    case CustomContextCommands.MakeZero:
                        value = 0;
                        break;
                    case CustomContextCommands.True:
                        value = true;
                        break;
                    case CustomContextCommands.False:
                        value = false;
                        break;
                    case CustomContextCommands.Toggle:
                        try
                        {
                            bool b = (bool)Convert.ChangeType(value, typeof(bool), CultureInfo.CurrentCulture);
                            value = !b;
                        }
                        catch { }
                        break;
                }

                // apply new value
                _flex[sel.Row, sel.Column] = value;
            }
        }
        static bool IsNumeric(Type type)
        {
            switch (Type.GetTypeCode(type))
            {
                case TypeCode.Byte:
                case TypeCode.Decimal:
                case TypeCode.Double:
                case TypeCode.Int16:
                case TypeCode.Int32:
                case TypeCode.Int64:
                case TypeCode.SByte:
                case TypeCode.Single:
                case TypeCode.UInt16:
                case TypeCode.UInt32:
                case TypeCode.UInt64:
                    return true;
            }
            return false;
        }

        #endregion

        //-----------------------------------------------------------------------------
        #region ** print

        void _btnPrint_Click(object sender, RoutedEventArgs e)
        {
            var scaleMode = ScaleMode.PageWidth;

            if (sender.Equals(_btnPrintPageWidth))
            {
                scaleMode = ScaleMode.PageWidth;
            }
            else if (sender.Equals(_btnPrintSinglePage))
            {
                scaleMode = ScaleMode.SinglePage;
            }
            else if (sender.Equals(_btnPrintSelection))
            {
                scaleMode = ScaleMode.Selection;
            }

            // print the grid
            _flex.Print("C1FlexGrid", scaleMode, new Thickness(96), 20);
        }

        #endregion

        //-----------------------------------------------------------------------------
        #region ** group

        // group and summarize data
        void _btnGroup_Click(object sender, RoutedEventArgs e)
        {
            var view = _flex.CollectionView;
            using (view.DeferRefresh())
            {
                // create undoable group action and record the grouping state before
                // changing the grouping settings
                GroupAction ga = new GroupAction(_flex);
                ga.RecordUndoState();

                // group by line and color
                var gd = view.GroupDescriptions;
                gd.Clear();
                foreach (string group in "Line,Color,Rating".Split(','))
                {
                    gd.Add(new PropertyGroupDescription(group));
                    _flex.Columns[group].Visible = false;
                }

                // record the grouping state after changing the grouping settings
                // and add the undoable action to the undo stack
                ga.RecordRedoState();
                _flex.UndoStack.AddAction(ga);

                // calculate average price, weight, cost, volume, and rating per group
                foreach (string s in "Price,Weight,Cost,Volume,Rating".Split(','))
                {
                    _flex.Columns[s].GroupAggregate = Aggregate.Sum; //.Average;
                }

                // make group rows scrollable
                _flex.AreRowGroupHeadersFrozen = false;
            }
        }
        // remove groups
        void _btnUngroup_Click(object sender, RoutedEventArgs e)
        {
            var view = _flex.CollectionView;
            if (view != null)
            {
                // create undoable group action and record the grouping state before
                // changing the grouping settings
                GroupAction ga = new GroupAction(_flex);
                ga.RecordUndoState();

                view.GroupDescriptions.Clear();
                foreach (var col in _flex.Columns)
                {
                    col.Visible = true;
                }

                // record the grouping state after changing the grouping settings
                // and add the undoable action to the undo stack
                ga.RecordRedoState();
                _flex.UndoStack.AddAction(ga);
            }
        }
        void _chkTotalsBelow_Click(object sender, RoutedEventArgs e)
        {
            _flex.GroupRowPosition = _chkTotalsBelow.IsChecked.Value
                ? GroupRowPosition.BelowData
                : GroupRowPosition.AboveData;
        }
        void _btnShowDetail_Click(object sender, RoutedEventArgs e)
        {
            var view = _flex.CollectionView;
            if (view != null && view.GroupDescriptions.Count > 0)
            {
                _flex.CollapseGroupsToLevel(view.GroupDescriptions.Count);
            }
        }
        void _btnHideDetail_Click(object sender, RoutedEventArgs e)
        {
            var view = _flex.CollectionView;
            if (view != null && view.GroupDescriptions.Count > 0)
            {
                _flex.CollapseGroupsToLevel(view.GroupDescriptions.Count - 1);
            }
        }

        #endregion

        //-----------------------------------------------------------------------------
        #region ** view

        // change color scheme (blue, silver, black)
        void ComboBox_ColorSchemeChanged(object sender, SelectionChangedEventArgs e)
        {
            if (_flex != null)
            {
                var cs = (ColorScheme)((ComboBox)sender).SelectedIndex;
                _flex.ColorScheme = cs;
                _flex.Select(0, 0);
                _flex.Focus();
                LayoutRoot.Background = _flex.RowHeaderBackground;
            }
        }

        // freeze/unfreeze panes
        void _btnFreeze_Click(object sender, RoutedEventArgs e)
        {
            if (_flex.Rows.Frozen > 0 || _flex.Columns.Frozen > 0)
            {
                // unfreeze
                _flex.Rows.Frozen = 0;
                _flex.Columns.Frozen = 0;
            }
            else
            {
                // freeze
                _flex.Rows.Frozen = Math.Min(5, _flex.Selection.TopRow);
                _flex.Columns.Frozen = Math.Min(3, _flex.Selection.LeftColumn);
            }
        }

        // toggle gridline visibility
        void _chkGridlines_Click(object sender, RoutedEventArgs e)
        {
            _flex.GridLinesVisibility = _chkGridlines.IsChecked.Value
                ? GridLinesVisibility.All
                : GridLinesVisibility.None;
        }

        // toggle heading visibility
        void _chkHeadings_Click(object sender, RoutedEventArgs e)
        {
            _flex.HeadersVisibility = _chkHeadings.IsChecked.Value
                ? HeadersVisibility.All
                : HeadersVisibility.None;
        }

        #endregion

        //-----------------------------------------------------------------------------
        #region ** editing

        // show sort dialog
        void _btnSort_Click(object sender, RoutedEventArgs e)
        {
            var sortDialog = new SortDialog(_flex);
            sortDialog.Show();
        }

        // show enhanced filter editor
        void _btnFilter_Click(object sender, RoutedEventArgs e)
        {
            _flex.ShowFilterEditor();
        }

        #endregion
    }
}
