using C1.Xaml.RichTextBox;
using C1.Xaml.RichTextBox.Documents;
using System;
using System.Collections.Generic;
using System.Linq;
using Windows.UI;
using Windows.UI.Text;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Documents;
using Windows.UI.Xaml.Media;
using docs = C1.Xaml.RichTextBox.Documents;

namespace ControlExplorer.Controls.RichTextBox
{
    public partial class FormatPopup : UserControl
    {
        public C1RichTextBox RichTextBox
        {
            get;
            set;
        }

        public Popup Popup
        {
            get;
            set;
        }

        // defined colors
        private SolidColorBrush _highlightColor1 = new SolidColorBrush(Colors.Yellow);
        private SolidColorBrush _highlightColor2 = new SolidColorBrush(Color.FromArgb(255, 0, 255, 0)); // lime
        private SolidColorBrush _highlightColor3 = new SolidColorBrush(Colors.Orange);
        private SolidColorBrush _fontColor1 = new SolidColorBrush(Colors.Blue);
        private SolidColorBrush _fontColor2 = new SolidColorBrush(Colors.Green);
        private SolidColorBrush _fontColor3 = new SolidColorBrush(Colors.Red);

        Thickness _checkedThickness = new Thickness(3);
        Thickness _uncheckedThickness = new Thickness(1);

        public FormatPopup()
        {
            InitializeComponent();

            // init color buttons
            btnHighlight1.Background = _highlightColor1;
            btnHighlight2.Background = _highlightColor2;
            btnHighlight3.Background = _highlightColor3;
            btnFontColor1.Background = _fontColor1;
            btnFontColor2.Background = _fontColor2;
            btnFontColor3.Background = _fontColor3;

            Loaded += (s, e) =>
            {
                InitializeButtons();
            };

        }

        private void ClosePopup()
        {
            if (Popup != null)
            {
                // close C1Popup
                Popup.IsOpen = false;
            }
        }

        private void InitializeButtons()
        {
            if (RichTextBox == null)
            {
                throw new ArgumentException("RichTextBox property is null");
            }            

            // checks which buttons should be toggled
            btnBold.IsChecked = RichTextBox.Selection.EditRanges.All(r => (FontWeights.Bold).Equals(r.FontWeight));
            btnBold.BorderThickness = (bool)btnBold.IsChecked ? _checkedThickness : _uncheckedThickness;
            btnItalic.IsChecked = RichTextBox.Selection.EditRanges.All(r => FontStyle.Italic.Equals(r.FontStyle));
            btnItalic.BorderThickness = (bool)btnItalic.IsChecked ? _checkedThickness : _uncheckedThickness;
            btnUnderline.IsChecked = RichTextBox.Selection.Runs.All(r => r.TextDecorations != null && r.TextDecorations.Contains(C1TextDecorations.Underline[0]));
            btnUnderline.BorderThickness = (bool)btnUnderline.IsChecked ? _checkedThickness : _uncheckedThickness;
            btnStrike.IsChecked = RichTextBox.Selection.Runs.All(r => r.TextDecorations != null && r.TextDecorations.Contains(C1TextDecorations.Strikethrough[0]));
            btnStrike.BorderThickness = (bool)btnStrike.IsChecked ? _checkedThickness : _uncheckedThickness;
            btnSubscript.IsChecked = RichTextBox.Selection.EditRanges.All(r => (C1VerticalAlignment.Sub).Equals(r.InlineAlignment));
            btnSubscript.BorderThickness = (bool)btnSubscript.IsChecked ? _checkedThickness : _uncheckedThickness;
            btnSuperscript.IsChecked = RichTextBox.Selection.EditRanges.All(r => (C1VerticalAlignment.Super).Equals(r.InlineAlignment));
            btnSuperscript.BorderThickness = (bool)btnSuperscript.IsChecked ? _checkedThickness : _uncheckedThickness;

            btnHighlight1.IsChecked = RichTextBox.Selection.EditRanges.All(r => _highlightColor1.Equals(r.InlineBackground));
            btnHighlight1.BorderThickness = (bool)btnHighlight1.IsChecked ? _checkedThickness : _uncheckedThickness;
            btnHighlight2.IsChecked = RichTextBox.Selection.EditRanges.All(r => _highlightColor2.Equals(r.InlineBackground));
            btnHighlight2.BorderThickness = (bool)btnHighlight2.IsChecked ? _checkedThickness : _uncheckedThickness;
            btnHighlight3.IsChecked = RichTextBox.Selection.EditRanges.All(r => _highlightColor3.Equals(r.InlineBackground));
            btnHighlight3.BorderThickness = (bool)btnHighlight3.IsChecked ? _checkedThickness : _uncheckedThickness;

            btnFontColor1.IsChecked = RichTextBox.Selection.EditRanges.All(r => _fontColor1.Equals(r.Foreground));
            btnFontColor1.BorderThickness = (bool)btnFontColor1.IsChecked ? _checkedThickness : _uncheckedThickness;
            btnFontColor2.IsChecked = RichTextBox.Selection.EditRanges.All(r => _fontColor2.Equals(r.Foreground));
            btnFontColor2.BorderThickness = (bool)btnFontColor2.IsChecked ? _checkedThickness : _uncheckedThickness;
            btnFontColor3.IsChecked = RichTextBox.Selection.EditRanges.All(r => _fontColor3.Equals(r.Foreground));
            btnFontColor3.BorderThickness = (bool)btnFontColor3.IsChecked ? _checkedThickness : _uncheckedThickness;

            btnAlignLeft.IsChecked = RichTextBox.Selection.EditRanges.All(r => (C1TextAlignment.Left).Equals(r.TextAlignment));
            btnAlignLeft.BorderThickness = (bool)btnAlignLeft.IsChecked ? _checkedThickness : _uncheckedThickness;
            btnAlignCenter.IsChecked = RichTextBox.Selection.EditRanges.All(r => (C1TextAlignment.Center).Equals(r.TextAlignment));
            btnAlignCenter.BorderThickness = (bool)btnAlignCenter.IsChecked ? _checkedThickness : _uncheckedThickness;
            btnAlignRight.IsChecked = RichTextBox.Selection.EditRanges.All(r => (C1TextAlignment.Right).Equals(r.TextAlignment));
            btnAlignRight.BorderThickness = (bool)btnAlignRight.IsChecked ? _checkedThickness : _uncheckedThickness;

            btnNumbering.IsChecked = RichTextBox.Selection.EditRanges.All(r => docs.TextMarkerStyle.Decimal.Equals(GetMarkerStyle(r)));
            btnNumbering.BorderThickness = (bool)btnNumbering.IsChecked ? _checkedThickness : _uncheckedThickness;
            btnBullets.IsChecked = RichTextBox.Selection.EditRanges.All(r => docs.TextMarkerStyle.Disc.Equals(GetMarkerStyle(r)));
            btnBullets.BorderThickness = (bool)btnBullets.IsChecked ? _checkedThickness : _uncheckedThickness;
        }

        /// <summary>
        /// Fires when Button is clicked.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ButtonBase_Click(object sender, RoutedEventArgs e)
        {
            ButtonBase btn = sender as ButtonBase;
            if (btn == null)
                return;

            using (new DocumentHistoryGroup(RichTextBox.DocumentHistory))
            {
                var range = RichTextBox.Selection;
                range.TrimRuns();
                RichTextBox.Selection = range;
                foreach (var r in range.EditRanges)
                {
                    switch (btn.Name)
                    {
                        case "btnBold":
                            // bold
                            range.FontWeight = ((bool)btnBold.IsChecked) ? FontWeights.Bold : FontWeights.Normal;
                            break;
                        case "btnItalic":
                            // italic
                            range.FontStyle = ((bool)btnItalic.IsChecked) ? FontStyle.Italic : FontStyle.Normal;
                            break;
                        case "btnUnderline":
                            // underline
                            {
                                var value = ((bool)btnUnderline.IsChecked) ? C1TextDecorations.Underline : null;
                                SetTextDecorations(range, value, C1TextDecorations.Underline);
                            }
                            break;
                        case "btnStrike":
                            // strike
                            {
                                var value = ((bool)btnStrike.IsChecked) ? C1TextDecorations.Strikethrough : null;
                                SetTextDecorations(range, value, C1TextDecorations.Strikethrough);
                            }
                            break;
                        case "btnSubscript":
                            // sub script
                            if ((bool)btnSubscript.IsChecked)
                            {
                                range.InlineAlignment = C1VerticalAlignment.Sub;
                                ChangeFontSize(range, -4);
                            }
                            else
                            {
                                range.InlineAlignment = C1VerticalAlignment.Baseline;
                                ChangeFontSize(range, 4);
                            }
                            break;
                        case "btnSuperscript":
                            // super script
                            if ((bool)btnSuperscript.IsChecked)
                            {
                                RichTextBox.Selection.InlineAlignment = C1VerticalAlignment.Super;
                                this.ChangeFontSize(range, -4);
                            }
                            else
                            {
                                RichTextBox.Selection.InlineAlignment = C1VerticalAlignment.Baseline;
                                this.ChangeFontSize(range, 4);
                            }
                            break;
                        case "btnGrowFont" :
                            // grow font
                            ChangeFontSize(range, 2);
                            break;
                        case "btnShrinkFont":
                            // shrink font
                            ChangeFontSize(range, - 2);
                            break;
                        case "btnHighlight1":
                            // highlight 1
                            range.InlineBackground = ((bool)btnHighlight1.IsChecked) ? _highlightColor1 : null;
                            break;
                        case "btnHighlight2":
                            // highlight 2
                            range.InlineBackground = ((bool)btnHighlight2.IsChecked) ? _highlightColor2 : null;
                            break;
                        case "btnHighlight3":
                            // highlight 3
                            range.InlineBackground = ((bool)btnHighlight3.IsChecked) ? _highlightColor3 : null;
                            break;
                        case "btnFontColor1":
                            // font color 1
                            range.Foreground = ((bool)btnFontColor1.IsChecked) ? _fontColor1 : null;
                            break;
                        case "btnFontColor2":
                            // font color 2
                            range.Foreground = ((bool)btnFontColor2.IsChecked) ? _fontColor2 : null;
                            break;
                        case "btnFontColor3":
                            // font color 3
                            range.Foreground = ((bool)btnFontColor3.IsChecked) ? _fontColor3 : null;
                            break;
                        case "btnAlignLeft":
                            // left align
                            range.TextAlignment = ((bool)btnAlignLeft.IsChecked) ? C1TextAlignment.Left : C1TextAlignment.Center;
                            break;
                        case "btnAlignCenter":
                            // center align
                            range.TextAlignment = ((bool)btnAlignCenter.IsChecked) ? C1TextAlignment.Center : C1TextAlignment.Left;
                            break;
                        case "btnAlignRight":
                            // right align
                            range.TextAlignment = ((bool)btnAlignRight.IsChecked) ? C1TextAlignment.Right : C1TextAlignment.Left;
                            break;
                        case "btnBullets":
                            // bullets lists
                            {
                                if((bool)btnBullets.IsChecked)
                                {
                                    range.MakeList(docs.TextMarkerStyle.Disc);
                                }
                                else
                                {
                                    range.UndoList();
                                }
                            }
                            break;
                        case "btnNumbering":
                            // numbering lists
                            {
                                if ((bool)btnNumbering.IsChecked)
                                {
                                    range.MakeList(docs.TextMarkerStyle.Decimal);
                                }
                                else
                                {
                                    range.UndoList();
                                }
                            }
                            break;
                        default:
                            break; 
                    }                                          
                }
                this.ClosePopup();
            }
        }

        /// <summary>
        /// Set text decorations to the range.
        /// </summary>
        /// <param name="range"></param>
        /// <param name="value"></param>
        /// <param name="defaultValue"></param>
        void SetTextDecorations(C1TextRange range, C1TextDecorationCollection value, C1TextDecorationCollection defaultValue)
        {
            foreach (var run in range.Runs)
            {
                var collection = new C1TextDecorationCollection();
                if (value == null)
                {
                    foreach (var decoration in run.TextDecorations)
                            collection.Add(decoration);
                    collection.Remove(defaultValue[0]);
                    if (collection.Count == 0)
                        collection = null;
                }
                else if (run.TextDecorations == null)
                {
                    collection.Add(value[0]);
                }
                else if (!run.TextDecorations.Contains(value[0]))
                {
                    foreach (var decoration in run.TextDecorations)
                        collection.Add(decoration);
                    collection.Add(value[0]);
                }
                else
                {
                    continue;
                }
                run.TextDecorations = null;
                run.TextDecorations = collection;
            }
        }

        /// <summary>
        /// Change the fontsize of the selection.
        /// </summary>
        /// <param name="size"></param>
        void ChangeFontSize(C1TextRange range, int size)
        {
            range.TrimRuns();
            if (!range.Runs.Any())
                return;

            List<C1ListItem> list = new List<C1ListItem>();
            foreach (var run in range.Runs)
            {
                // Change FontSize of each run in the range.
                run.FontSize += size;
                var listItem = run.GetParents().Where(e => e is C1ListItem).FirstOrDefault() as C1ListItem;
                if (listItem != null && !list.Contains(listItem))
                {
                    if ((Contains(range, listItem.ContentEnd) || !new C1TextRange(range.End, listItem.ContentEnd).Runs.Any()))
                        list.Add(listItem);
                }
            }

            if (list.Count == 0)
                return;
            foreach (var item in list)
            {
                // If C1ListItem is in the range, set all its children's FontSize and then change the FontSize of C1ListItem.
                if (item.Children.Count > 0)
                {
                    foreach (var child in item.Children)
                    {
                        child.FontSize = child.FontSize;
                    }
                }
                item.FontSize += size;
            }
        }

        /// <summary>
        /// Check whether the position is contained in the range or not.
        /// </summary>
        /// <param name="range"></param>
        /// <param name="position"></param>
        /// <returns></returns>
        bool Contains(C1TextRange range, C1TextPointer position)
        {
            return (position < range.End || (position == range.End && position.LogicalDirection == LogicalDirection.Backward))
                && (position > range.Start || (position == range.Start && position.LogicalDirection == LogicalDirection.Forward));
        }

        /// <summary>
        /// Get the MarkerStyle of the selection.
        /// </summary>
        /// <param name="range"></param>
        /// <returns></returns>
        docs.TextMarkerStyle? GetMarkerStyle(C1TextRange range)
        {
            var lists = range.Lists.ToList();
            docs.TextMarkerStyle? marker = lists.Any() ? new docs.TextMarkerStyle?(lists.First().MarkerStyle) : null;
            foreach (var list in lists)
            {
                if (marker != list.MarkerStyle) return null;
            }
            return marker;
        }

        /// <summary>
        /// Set the MarkerStyle to the range.
        /// </summary>
        /// <param name="range"></param>
        /// <param name="value"></param>
        void SetTextMarkerStyle(C1TextRange range, docs.TextMarkerStyle? value)
        {
            if (!value.HasValue)
            {
                range.UndoList();
            }
            else
            {
                range.MakeList(value.Value);
            }
        }
    }
}
