using C1.Xaml.FlexGrid;
using System;
using System.Linq;
using System.Collections.Generic;
using Windows.Foundation;
using Windows.Graphics.Printing;
using Windows.UI.Core;
using Windows.UI.Popups;
using Windows.UI.ViewManagement;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Navigation;
using Windows.UI.Xaml.Printing;
using Windows.Graphics.Printing.OptionDetails;

namespace FlexGridSamples
{
    /// <summary>
    /// An empty page that can be used on its own or navigated to within a Frame.
    /// </summary>
    public sealed partial class Printing : Page
    {
        ICollectionView _data = Product.GetProducts(100);
        List<FrameworkElement> _pages;
        double _margin;
        ScaleMode _scaleMode;

        /// <summary>
        /// PrintDocument is used to prepare the pages for printing. 
        /// Prepare the pages to print in the handlers for the Paginate, GetPreviewPage, and AddPages events.
        /// </summary>
        private PrintDocument printDocument = null;

        /// <summary>
        /// Marker interface for document source
        /// </summary>
        private IPrintDocumentSource printDocumentSource = null;

        public Printing()
        {
            this.InitializeComponent();

            // populate grid
            _flex.ItemsSource = _data;
            _flex.Columns["Line"].AllowMerging = true;
            _flex.Columns["Color"].AllowMerging = true;

            // for testing
            _flex.AllowResizing = AllowResizing.Both;

            Loaded += (s, e) => RegisterForPrinting();
            Unloaded += (s, e) => UnregisterForPrinting();
        }

        /// <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)
        {
        }

        #region ** printing

        /// <summary>
        /// This method registers the app for printing with Windows and sets up the necessary event handlers for the print process.
        /// </summary>
        private void RegisterForPrinting()
        {
            // Create the PrintDocument.
            printDocument = new PrintDocument();

            // Save the DocumentSource.
            printDocumentSource = printDocument.DocumentSource;

            // Add an event handler which sets up print preview.
            printDocument.Paginate += Paginate;

            // Add an event handler which provides a specified preview page.
            printDocument.GetPreviewPage += GetPrintPreviewPage;

            // Add an event handler which provides all final print pages.
            printDocument.AddPages += AddPrintPages;

            // Create a PrintManager and add a handler for printing initialization.
            PrintManager printMan = PrintManager.GetForCurrentView();
            printMan.PrintTaskRequested += PrintTaskRequested;
        }

        /// <summary>
        /// This method unregisters the app for printing with Windows.
        /// </summary>
        private void UnregisterForPrinting()
        {
            if (printDocument == null)
                return;

            printDocument.Paginate -= Paginate;
            printDocument.GetPreviewPage -= GetPrintPreviewPage;
            printDocument.AddPages -= AddPrintPages;

            // Remove the handler for printing initialization.
            PrintManager printMan = PrintManager.GetForCurrentView();
            printMan.PrintTaskRequested -= PrintTaskRequested;

        }

        /// <summary>
        /// This is the event handler for PrintManager.PrintTaskRequested.
        /// </summary>
        /// <param name="sender">PrintManager</param>
        /// <param name="e">PrintTaskRequestedEventArgs </param>
        private async void PrintTaskRequested(PrintManager sender, PrintTaskRequestedEventArgs e)
        {
            PrintTask printTask = null;
            printTask = e.Request.CreatePrintTask("SamplePDF", sourceRequested =>
            {
                var printDetailedOptions = PrintTaskOptionDetails.GetFromPrintTaskOptions(printTask.Options);

                IList<string> displayedOptions = printTask.Options.DisplayedOptions;

                var zoomItem = printDetailedOptions.CreateItemListOption("Zoom", "Zoom");
                zoomItem.AddItem("ActualSize", "Actual Size");
                zoomItem.AddItem("PageWidth", "Page Width");
                zoomItem.AddItem("SinglePage", "Single Page");
                zoomItem.TrySetValue("PageWidth");

                var marginItem = printDetailedOptions.CreateItemListOption("Margin", "Margin");
                marginItem.AddItem("None", "None");
                marginItem.AddItem("HalfInch", "0.5 inch");
                marginItem.AddItem("OneInch", "1 inch");
                marginItem.TrySetValue("OneInch");

                printDetailedOptions.OptionChanged += OnPrintOptionsChanged;

                // Choose the printer options to be shown.
                // The order in which the options are appended determines the order in which they appear in the UI
                displayedOptions.Clear();
                displayedOptions.Add(StandardPrintTaskOptions.Copies);
                displayedOptions.Add(StandardPrintTaskOptions.Orientation);
                displayedOptions.Add(StandardPrintTaskOptions.MediaSize);
                displayedOptions.Add(StandardPrintTaskOptions.Collation);
                displayedOptions.Add(StandardPrintTaskOptions.Duplex);
                // Add the custom option to the option list
                displayedOptions.Add("Zoom");
                displayedOptions.Add("Margin");

                // Preset the default value of the printer option
                printTask.Options.MediaSize = PrintMediaSize.IsoA4;

                // Print Task event handler is invoked when the print job is completed.
                printTask.Completed += async (s, args) =>
                {
                    // Notify the user when the print operation fails.
                    if (args.Completion == PrintTaskCompletion.Failed)
                    {
                        await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
                        {
                            MessageDialog dialog = new MessageDialog("Failed to print.");
                            dialog.ShowAsync();
                        });
                    }
                };

                sourceRequested.SetSource(printDocumentSource);
            });

        }

        async void OnPrintOptionsChanged(PrintTaskOptionDetails sender, PrintTaskOptionChangedEventArgs args)
        {
            // Listen for PageContent changes
            string optionId = args.OptionId as string;
            if (string.IsNullOrEmpty(optionId))
                return;

            if (optionId == "Zoom" || optionId == "Margin")
            {
                await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal,
                    () =>
                    {
                        printDocument.InvalidatePreview();
                    });
            }
        }

        /// <summary>
        /// This is the event handler for PrintDocument.Paginate. 
        /// It fires when the PrintManager requests print preview
        /// </summary>
        /// <param name="sender">PrintDocument</param>
        /// <param name="e">Paginate Event Arguments</param>
        private void Paginate(object sender, PaginateEventArgs e)
        {
            PrintDocument printDoc = (PrintDocument)sender;

            // Get PageContent property
            var printDetailedOptions = PrintTaskOptionDetails.GetFromPrintTaskOptions(e.PrintTaskOptions);
            string zoom = printDetailedOptions.Options["Zoom"].Value as string;
            string margin = printDetailedOptions.Options["Margin"].Value as string;
            // get scale mode and margin
            _scaleMode =
                zoom == "ActualSize" ? ScaleMode.ActualSize :
                zoom == "PageWidth" ? ScaleMode.PageWidth :
                ScaleMode.SinglePage;
            _margin =
                margin == "None" ? 0 :
                margin == "HalfInch" ? 96.0 / 2 :
                96.0;

            // calculate page size, discounting margins
            var pageDescription = e.PrintTaskOptions.GetPageDescription(0);
            var pagesize = pageDescription.PageSize;
            pagesize.Width -= 2 * _margin;
            pagesize.Height -= 2 * _margin;
            _pages = _flex.GetPageImages(_scaleMode, pagesize, 100).Select(
                page => (FrameworkElement)new ContentControl
                {
                    Content = page,
                    Padding = new Thickness(_margin)
                }).ToList();

            // Report the number of preview pages
            printDoc.SetPreviewPageCount(_pages.Count, PreviewPageCountType.Final);
        }

        /// <summary>
        /// This is the event handler for PrintDocument.GetPrintPreviewPage. It provides a specific print preview page,
        /// in the form of an UIElement, to an instance of PrintDocument. PrintDocument subsequently converts the UIElement
        /// into a page that the Windows print system can deal with.
        /// </summary>
        /// <param name="sender">PrintDocument</param>
        /// <param name="e">Arguments containing the preview requested page</param>
        private void GetPrintPreviewPage(object sender, GetPreviewPageEventArgs e)
        {
            PrintDocument printDoc = (PrintDocument)sender;

            if (_pages != null && e.PageNumber > 0 && e.PageNumber - 1 < _pages.Count)
                printDoc.SetPreviewPage(e.PageNumber, _pages[e.PageNumber - 1]);
        }

        /// <summary>
        /// This is the event handler for PrintDocument.AddPages. It provides all pages to be printed, in the form of
        /// UIElements, to an instance of PrintDocument. PrintDocument subsequently converts the UIElements
        /// into a pages that the Windows print system can deal with.
        /// </summary>
        /// <param name="sender">PrintDocument</param>
        /// <param name="e">Add page event arguments containing a print task options reference</param>
        private void AddPrintPages(object sender, AddPagesEventArgs e)
        {
            // Loop over all of the pages and add each one to be printed
            foreach (FrameworkElement page in _pages)
            {
                printDocument.AddPage(page);
            }

            PrintDocument printDoc = (PrintDocument)sender;

            // Indicate that all of the print pages have been provided
            printDoc.AddPagesComplete();
        }

        void _btnPrint_Click(object sender, RoutedEventArgs e)
        {
            PrintManager.ShowPrintUIAsync();
        }

        #endregion
    }
}
