using System;
using System.IO;
using System.Linq;
using System.Reflection;
using Windows.UI.Popups;
using Windows.UI.Text;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Navigation;
using C1.Xaml;
using C1.Xaml.RichTextBox;
using C1.Xaml.RichTextBox.Documents;
using RichTextBoxSamples.Tools;
using System.Collections.Generic;
using docs = C1.Xaml.RichTextBox.Documents;
using Windows.Storage.Pickers;
using Windows.Storage;
using Windows.UI.Xaml.Media.Imaging;
using Windows.Storage.Streams;

namespace RichTextBoxSamples
{
    /// <summary>
    /// An empty page that can be used on its own or navigated to within a Frame.
    /// </summary>
    public sealed partial class AppBarDemo : Page
    {
        Dictionary<C1Run, double> dicFontSize;
        public AppBarDemo()
        {
            this.InitializeComponent();

            InitMorePopup();
            InitAlignPopup();
            dicFontSize = new Dictionary<C1Run, double>();

            btnBold.RichTextBox = rtb;
            btnItalic.RichTextBox = rtb;
            btnUnderline.RichTextBox = rtb;

            Assembly asm = typeof(DemoRichTextBox).GetTypeInfo().Assembly;
            Stream stream = asm.GetManifestResourceStream("RichTextBoxSamples.Resources.simple.htm");
            var html = new StreamReader(stream).ReadToEnd();
            rtb.Html = html;

            rtb.RightTapped += rtb_RightTapped;
            rtb.SelectionChanged += rtb_SelectionChanged;
        }

        void rtb_SelectionChanged(object sender, EventArgs e)
        {
            dicFontSize.Clear();
            var range = rtb.Selection;
            if (!range.Runs.Any())
                return;
            foreach (var run in range.Runs)
            {
                dicFontSize.Add(run, run.FontSize);
            }
        }

        void rtb_RightTapped(object sender, RightTappedRoutedEventArgs e)
        {
            bottomAppBar.IsOpen = true; 
        }

        private async void rtb_RequestNavigate(object sender, RequestNavigateEventArgs e)
        {
            var md = new MessageDialog("The document is requesting to navigate to " + e.Hyperlink.NavigateUri, "Navigate");

            md.Commands.Add(new UICommand("OK", (UICommandInvokedHandler) =>
            {
                Windows.System.Launcher.LaunchUriAsync(e.Hyperlink.NavigateUri);
            }));

            md.Commands.Add(new UICommand("Cancel", (UICommandInvokedHandler) =>
            {
                rtb.Select(e.Hyperlink.ContentStart.TextOffset, e.Hyperlink.ContentRange.Text.Length);
            }));

            await md.ShowAsync();
        }
        #region Clipboard
        private void btnCopy_Click(object sender, Windows.UI.Xaml.RoutedEventArgs e)
        {
            rtb.ClipboardCopy();
        }

        private void btnCut_Click(object sender, Windows.UI.Xaml.RoutedEventArgs e)
        {
            if (rtb.IsReadOnly)
                rtb.ClipboardCopy();
            else
                rtb.ClipboardCut();
        }

        private void btnPaste_Click(object sender, Windows.UI.Xaml.RoutedEventArgs e)
        {
            if (!rtb.IsReadOnly)
            {
                rtb.ClipboardPaste();
            }
        }
        #endregion

        #region Undo/Redo
        void btnRedo_Click(object sender, RoutedEventArgs e)
        {
            if (rtb.DocumentHistory.CanRedo)
            {
                rtb.DocumentHistory.Redo();
            }
            morePopUp.Hide();
        }

        void btnUndo_Click(object sender, RoutedEventArgs e)
        {
            if (rtb.DocumentHistory.CanUndo)
            {
                rtb.DocumentHistory.Undo();
            }
            morePopUp.Hide();
        }
        #endregion

        #region Lists
        void btnBulletedList_Click(object sender, RoutedEventArgs e)
        {
            using (new DocumentHistoryGroup(rtb.DocumentHistory))
            {
                var range = rtb.Selection;
                // check if selection is already a list
                var isChecked = range.EditRanges.All(r => docs.TextMarkerStyle.Disc.Equals(GetMarkerStyle(r)));
                range.TrimRuns();
                rtb.Selection = range;
                foreach (var r in range.EditRanges)
                {
                    if (isChecked)
                    {
                        // undo list
                        range.UndoList();
                    }
                    else
                    {
                        // make bullet list
                        range.MakeList(docs.TextMarkerStyle.Disc);
                    }
                }
            }
            morePopUp.Hide();
        }

        void btnNumberedList_Click(object sender, RoutedEventArgs e)
        {
            using (new DocumentHistoryGroup(rtb.DocumentHistory))
            {
                var range = rtb.Selection;
                // check if selection is already a list
                var isChecked = range.EditRanges.All(r => docs.TextMarkerStyle.Decimal.Equals(GetMarkerStyle(r)));
                range.TrimRuns();
                rtb.Selection = range;
                foreach (var r in range.EditRanges)
                {
                    if (isChecked)
                    {
                        // undo list
                        range.UndoList();
                    }
                    else
                    {
                        // make number list
                        range.MakeList(docs.TextMarkerStyle.Decimal);
                    }
                }
            }
            morePopUp.Hide();
        }

        /// <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;
        }
        #endregion

        #region Font Colors

        private void btnFontColor_Click(object sender, RoutedEventArgs e)
        {
            FontColorTool fontColorPicker = new FontColorTool();
            fontColorPicker.RichTextBox = rtb;
            fontColorPicker.ColorMode = FontColorMode.Foreground;
            fontColorPicker.Popup.Placement = FlyoutPlacementMode.Top;
            fontColorPicker.Show(sender as FrameworkElement);
        }

        private void btnHighlight_Click(object sender, RoutedEventArgs e)
        {
            FontColorTool fontColorPicker = new FontColorTool();
            fontColorPicker.RichTextBox = rtb;
            fontColorPicker.ColorMode = FontColorMode.Background;
            fontColorPicker.Popup.Placement = FlyoutPlacementMode.Top;
            fontColorPicker.Show(sender as FrameworkElement);
        }

        #endregion

        #region Insert Objects

        private void btnInsertTable_Click(object sender, RoutedEventArgs e)
        {
            InsertTableTool tableTool = new InsertTableTool();
            tableTool.RichTextBox = rtb;
            tableTool.Popup.Placement = FlyoutPlacementMode.Top;
            tableTool.Show(sender as FrameworkElement);
        }

        private void btnInsertHyperlink_Click(object sender, RoutedEventArgs e)
        {
            InsertHyperlinkTool hyperlinkTool = new InsertHyperlinkTool();
            hyperlinkTool.RichTextBox = rtb;
            hyperlinkTool.Popup.Placement = FlyoutPlacementMode.Top;
            hyperlinkTool.Show(sender as FrameworkElement);
        }

        private void btnInsertPicture_Click(object sender, RoutedEventArgs e)
        {
            InsertImageTool imageTool = new InsertImageTool();
            imageTool.RichTextBox = rtb;
            imageTool.Popup.Placement = FlyoutPlacementMode.Top;
            imageTool.Show(sender as FrameworkElement);
        }

        private async void btnPicture_Click(object sender, RoutedEventArgs e)
        {
            FileOpenPicker openPicker = new FileOpenPicker();
            openPicker.ViewMode = PickerViewMode.Thumbnail;
            openPicker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;
            openPicker.FileTypeFilter.Add(".jpg");
            openPicker.FileTypeFilter.Add(".jpeg");
            openPicker.FileTypeFilter.Add(".png");
            StorageFile file = await openPicker.PickSingleFileAsync();
            if (file != null)
            {
                var source = new BitmapImage();
                var newElement = new C1InlineUIContainer { Content = source, ContentTemplate = ImageAttach.ImageTemplate };
                ExceptionRoutedEventHandler failed = delegate
                {
                    newElement.Remove();
                };
                source.ImageFailed += failed;
                var stream = await file.OpenAsync(FileAccessMode.Read);
                source.SetSource(stream);

                using (var dataReader = new DataReader(stream))
                {
                    stream.Seek(0);
                    var bytes = new byte[stream.Size];
                    await dataReader.LoadAsync((uint)stream.Size);
                    dataReader.ReadBytes(bytes);
                    ImageAttach.SetStream(source, bytes);
                }
                using (new DocumentHistoryGroup(rtb.DocumentHistory))
                {
                    rtb.Selection.Delete();
                    rtb.Selection.Start.InsertInline(newElement);
                }
                rtb.Focus(FocusState.Programmatic);
            }
        }

        #endregion

        #region clear formatting
        void btnClear_Click(object sender, RoutedEventArgs e)
        {
            using (new DocumentHistoryGroup(rtb.DocumentHistory))
            {
                // clear foreground and background colors
                rtb.Selection.InlineBackground = null;
                rtb.Selection.Foreground = rtb.Foreground;

                // clear font
                rtb.Selection.FontWeight = FontWeights.Normal;
                rtb.Selection.FontStyle = FontStyle.Normal;
                rtb.Selection.TextDecorations = null;
                if (dicFontSize.Count > 0)
                {
                    foreach (var el in dicFontSize.Keys)
                    {
                        el.FontSize = dicFontSize[el];
                    }
                }
            }
        }
        #endregion

        #region sub/super script
        void btnSubscript_Click(object sender, RoutedEventArgs e)
        {
            // subscript
            using (new DocumentHistoryGroup(rtb.DocumentHistory))
            {
                var range = rtb.Selection;
                range.TrimRuns();
                rtb.Selection = range;
                foreach (var ran in range.EditRanges)
                {
                    var isSub = ran.EditRanges.All(r => (C1VerticalAlignment.Sub).Equals(r.InlineAlignment));
                    var isSuper = ran.EditRanges.All(r => (C1VerticalAlignment.Super).Equals(r.InlineAlignment));
                    if (isSub)
                    {
                        ran.InlineAlignment = C1VerticalAlignment.Baseline;
                        GrowFont(4);
                    }
                    else
                    {
                        ran.InlineAlignment = C1VerticalAlignment.Sub;
                        if (!isSuper)
                            ShrinkFont(4);
                    }
                }
            }
            morePopUp.Hide();
        }

        void btnSuperscript_Click(object sender, RoutedEventArgs e)
        {
            // superscript
            using (new DocumentHistoryGroup(rtb.DocumentHistory))
            {
                var range = rtb.Selection;
                range.TrimRuns();
                rtb.Selection = range;
                foreach (var ran in range.EditRanges)
                {
                    var isSub = ran.EditRanges.All(r => (C1VerticalAlignment.Sub).Equals(r.InlineAlignment));
                    var isSuper = ran.EditRanges.All(r => (C1VerticalAlignment.Super).Equals(r.InlineAlignment));
                    if (isSuper)
                    {
                        ran.InlineAlignment = C1VerticalAlignment.Baseline;
                        GrowFont(4);
                    }
                    else
                    {
                        ran.InlineAlignment = C1VerticalAlignment.Super;
                        if (!isSub)
                            ShrinkFont(4);
                    }
                }
            }

            morePopUp.Hide();
        }

        private void GrowFont(int size)
        {
            // grow font
            rtb.Selection.TrimRuns();
            foreach (var run in rtb.Selection.Runs)
            {
                run.FontSize += size;
            }
        }

        private void ShrinkFont(int size)
        {
            // shrink font
            rtb.Selection.TrimRuns();
            foreach (var run in rtb.Selection.Runs)
            {
                run.FontSize -= size;
            }
        }
        #endregion

        #region align

        Flyout alignPopUp = new Flyout();
        private void btnAlign_Click(object sender, RoutedEventArgs e)
        {
            alignPopUp.Placement = FlyoutPlacementMode.Top;
            alignPopUp.ShowAt(sender as FrameworkElement);
        }

        void InitAlignPopup()
        {
            Border menuBorder = new Border();
            menuBorder.Height = 140;
            menuBorder.Width = 150;
            StackPanel panel = new StackPanel();
            panel.VerticalAlignment = Windows.UI.Xaml.VerticalAlignment.Center;

            // align left
            Button btnLeftAlign = new Button();
            btnLeftAlign.Content = "Align Left";
            btnLeftAlign.Style = Application.Current.Resources["MenuTextButtonStyle"] as Style;
            btnLeftAlign.Margin = new Thickness(20, 5, 20, 5);
            btnLeftAlign.Click += btnLeftAlign_Click;

            // align center
            Button btnCenterAlign = new Button();
            btnCenterAlign.Content = "Align Center";
            btnCenterAlign.Style = Application.Current.Resources["MenuTextButtonStyle"] as Style;
            btnCenterAlign.Margin = new Thickness(20, 5, 20, 5);
            btnCenterAlign.Click += btnCenterAlign_Click;

            // align right
            Button btnRightAlign = new Button();
            btnRightAlign.Content = "Align Right";
            btnRightAlign.Style = Application.Current.Resources["MenuTextButtonStyle"] as Style;
            btnRightAlign.Margin = new Thickness(20, 5, 20, 5);
            btnRightAlign.Click += btnRightAlign_Click;

            // justify
            Button btnJustify = new Button();
            btnJustify.Content = "Justify";
            btnJustify.Style = Application.Current.Resources["MenuTextButtonStyle"] as Style;
            btnJustify.Margin = new Thickness(20, 5, 20, 5);
            btnJustify.Click += btnJustify_Click;

            panel.Children.Add(btnLeftAlign);
            panel.Children.Add(btnCenterAlign);
            panel.Children.Add(btnRightAlign);
            panel.Children.Add(btnJustify);
            menuBorder.Child = panel;
            alignPopUp.Content = menuBorder;
        }

        void btnJustify_Click(object sender, RoutedEventArgs e)
        {
            rtb.Selection.TextAlignment = C1TextAlignment.Justify;
            alignPopUp.Hide();
        }

        void btnRightAlign_Click(object sender, RoutedEventArgs e)
        {
            rtb.Selection.TextAlignment = C1TextAlignment.Right;
            alignPopUp.Hide();
        }

        void btnCenterAlign_Click(object sender, RoutedEventArgs e)
        {
            rtb.Selection.TextAlignment = C1TextAlignment.Center;
            alignPopUp.Hide();
        }

        void btnLeftAlign_Click(object sender, RoutedEventArgs e)
        {
            rtb.Selection.TextAlignment = C1TextAlignment.Left;
            alignPopUp.Hide();
        }

        #endregion

        #region More
        Flyout morePopUp = new Flyout();
        private void btnMore_Click(object sender, RoutedEventArgs e)
        {
            morePopUp.Placement = FlyoutPlacementMode.Top;
            morePopUp.ShowAt(sender as FrameworkElement);
        }

        void InitMorePopup()
        {
            Border menuBorder = new Border();
            menuBorder.Height = 320;
            menuBorder.Width = 170;
            StackPanel panel = new StackPanel();
            panel.VerticalAlignment = Windows.UI.Xaml.VerticalAlignment.Center;

            // bulleted list
            Button btnBulletedList = new Button();
            btnBulletedList.Content = "Bulleted List";
            btnBulletedList.Style = Application.Current.Resources["MenuTextButtonStyle"] as Style;
            btnBulletedList.Margin = new Thickness(20, 5, 20, 5);
            btnBulletedList.Click += btnBulletedList_Click;

            // numbered list
            Button btnNumberedList = new Button();
            btnNumberedList.Content = "Numbered List";
            btnNumberedList.Style = Application.Current.Resources["MenuTextButtonStyle"] as Style;
            btnNumberedList.Margin = new Thickness(20, 5, 20, 5);
            btnNumberedList.Click += btnNumberedList_Click;

            // undo
            Button btnUndo = new Button();
            btnUndo.Content = "Undo";
            btnUndo.Style = Application.Current.Resources["MenuTextButtonStyle"] as Style;
            btnUndo.Margin = new Thickness(20, 5, 20, 5);
            btnUndo.Click += btnUndo_Click;

            // undo
            Button btnRedo = new Button();
            btnRedo.Content = "Redo";
            btnRedo.Style = Application.Current.Resources["MenuTextButtonStyle"] as Style;
            btnRedo.Margin = new Thickness(20, 5, 20, 5);
            btnRedo.Click += btnRedo_Click;

            // clear formatting
            Button btnClear = new Button();
            btnClear.Content = "Clear Formatting";
            btnClear.Style = Application.Current.Resources["MenuTextButtonStyle"] as Style;
            btnClear.Margin = new Thickness(20, 5, 20, 5);
            btnClear.Click += btnClear_Click;

            // superscript
            Button btnSuperscript = new Button();
            btnSuperscript.Content = "Superscript";
            btnSuperscript.Style = Application.Current.Resources["MenuTextButtonStyle"] as Style;
            btnSuperscript.Margin = new Thickness(20, 5, 20, 5);
            btnSuperscript.Click += btnSuperscript_Click;

            // subscript
            Button btnSubscript = new Button();
            btnSubscript.Content = "Subscript";
            btnSubscript.Style = Application.Current.Resources["MenuTextButtonStyle"] as Style;
            btnSubscript.Margin = new Thickness(20, 5, 20, 5);
            btnSubscript.Click += btnSubscript_Click;

            // strikethrough
            Button btnStrikethrough = new Button();
            btnStrikethrough.Content = "Strikethrough";
            btnStrikethrough.Style = Application.Current.Resources["MenuTextButtonStyle"] as Style;
            btnStrikethrough.Margin = new Thickness(20, 5, 20, 5);
            btnStrikethrough.Click += btnStrikethrough_Click;

            // increase font
            C1IncreaseFontSizeTool btnIncreaseFontSize = new C1IncreaseFontSizeTool();
            btnIncreaseFontSize.RichTextBox = rtb;
            btnIncreaseFontSize.Content = "Increase Font Size";
            btnIncreaseFontSize.Style = Application.Current.Resources["MenuTextButtonStyle"] as Style;
            btnIncreaseFontSize.Margin = new Thickness(20, 5, 20, 5);
            btnIncreaseFontSize.Click += btnIncreaseFontSize_Click;

            // decrease font
            C1DecreaseFontSizeTool btnDecreaseFontSize = new C1DecreaseFontSizeTool();
            btnDecreaseFontSize.RichTextBox = rtb;
            btnDecreaseFontSize.Content = "Decrease Font Size";
            btnDecreaseFontSize.Style = Application.Current.Resources["MenuTextButtonStyle"] as Style;
            btnDecreaseFontSize.Margin = new Thickness(20, 5, 20, 5);
            btnDecreaseFontSize.Click += btnDecreaseFontSize_Click;

            panel.Children.Add(btnBulletedList);
            panel.Children.Add(btnNumberedList);
            panel.Children.Add(btnSubscript);
            panel.Children.Add(btnSuperscript);
            panel.Children.Add(btnStrikethrough);
            panel.Children.Add(btnIncreaseFontSize);
            panel.Children.Add(btnDecreaseFontSize);
            panel.Children.Add(btnUndo);
            panel.Children.Add(btnRedo);
            panel.Children.Add(btnClear);
            menuBorder.Child = panel;
            morePopUp.Content = menuBorder;

        }

        void btnDecreaseFontSize_Click(object sender, RoutedEventArgs e)
        {
            morePopUp.Hide();
        }

        void btnIncreaseFontSize_Click(object sender, RoutedEventArgs e)
        {
            morePopUp.Hide();
        }

        void btnStrikethrough_Click(object sender, RoutedEventArgs e)
        {
            // strikethrough
            var isMenuStrikeChecked = rtb.Selection.Runs.Any() &&
                rtb.Selection.Runs.All(r => r.TextDecorations != null && r.TextDecorations.Contains(C1TextDecorations.Strikethrough[0]));

            var value = isMenuStrikeChecked ? null : C1TextDecorations.Strikethrough;

            using (new DocumentHistoryGroup(rtb.DocumentHistory))
            {
                var range = rtb.Selection;
                range.TrimRuns();
                rtb.Selection = range;
                foreach (var r in range.EditRanges)
                {
                    foreach (var run in range.Runs)
                    {
                        var collection = new C1TextDecorationCollection();
                        if (value == null)
                        {
                            foreach (var decoration in run.TextDecorations)
                                collection.Add(decoration);

                            collection.Remove(C1TextDecorations.Strikethrough[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;

                    }
                }
            }
            morePopUp.Hide();
        }

        #endregion
        /// <summary>
        /// Invoked when this page is about to be displayed in a Frame.
        /// </summary>
        /// <param name="e">Event data that describes how this page was reached.  The Parameter
        /// property is typically used to configure the page.</param>
        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
        }
    }
}
