using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.Storage;
using Windows.Storage.Pickers;
using Windows.Storage.Streams;
using Windows.UI.Popups;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Imaging;
using Windows.UI.Xaml.Navigation;

using Windows.Graphics.Printing;
using Windows.UI.Xaml.Printing;
using Windows.Graphics.Printing.OptionDetails;
using System.Threading.Tasks;


// Die Elementvorlage "Leere Seite" ist unter http://go.microsoft.com/fwlink/?LinkId=234238 dokumentiert.

namespace FilePickerDemo
{
    public enum Scaling : byte
    {
        scaleNo,
        scaleShrinkToFit
    }

    /// <summary>
    /// Eine leere Seite, die eigenständig verwendet werden kann oder auf die innerhalb eines Rahmens navigiert werden kann.
    /// </summary>
    public partial class MainPage : Page
    {
        StorageFile imgFile = null;
        PrintDocument printDocument = null;
        IPrintDocumentSource printDocumentSource = null;
        Scaling scale;
        PrintPageDescription printPageDescription;

        public MainPage()
        {
            this.InitializeComponent();
        }

        /// <summary>
        /// Wird aufgerufen, wenn diese Seite in einem Rahmen angezeigt werden soll.
        /// </summary>
        /// <param name="e">Ereignisdaten, die beschreiben, wie diese Seite erreicht wurde. Die
        /// Parametereigenschaft wird normalerweise zum Konfigurieren der Seite verwendet.</param>
        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            RegisterForPrinting();
        }

        protected override void OnNavigatedFrom(NavigationEventArgs e)
        {
            UnregisterForPrinting();
        }

        private async void Image_Tapped(object sender, TappedRoutedEventArgs e)
        {
            FileOpenPicker openPicker = new FileOpenPicker();
            openPicker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;
            openPicker.ViewMode = PickerViewMode.Thumbnail;
            openPicker.FileTypeFilter.Add(".jpg");
            openPicker.FileTypeFilter.Add(".jpeg");
            openPicker.FileTypeFilter.Add(".png");

            imgFile = await openPicker.PickSingleFileAsync();

            if (imgFile != null)
            {
                using (IRandomAccessStream fileStream = await imgFile.OpenAsync(Windows.Storage.FileAccessMode.Read))
                {
                        BitmapImage bitmapImage = new BitmapImage();

                        await bitmapImage.SetSourceAsync(fileStream);
                        theImage.Source = bitmapImage;
                }
            }
        }

        private async void theImage_RightTapped(object sender, RightTappedRoutedEventArgs e)
        {
            try
            {
                FileSavePicker savePicker = new FileSavePicker();
                savePicker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;
                savePicker.FileTypeChoices.Add("Bilder", new List<string>() { ".jpg", ".png" }); 

                StorageFile file = await savePicker.PickSaveFileAsync();

                if (file != null)
                {
                    String folderName = file.Path.Substring(0,file.Path.LastIndexOf('\\')+1);
                    StorageFolder sf = await StorageFolder.GetFolderFromPathAsync(folderName);
                    await imgFile.CopyAndReplaceAsync(file);
                }
            }
            catch (Exception ex)
            {
                MessageDialog messageDialog = new MessageDialog(ex.Message, "Fehler");
                messageDialog.ShowAsync();
            }
        }

        protected virtual void RegisterForPrinting()
        {
            // PrintDocument erzeugen.
            printDocument = new PrintDocument();

            // Das zu druckende Dokument zuweisen.
            printDocumentSource = printDocument.DocumentSource;

            // Add an event handler which creates preview pages.
            printDocument.Paginate += CreatePrintPreviewPages;

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

        protected virtual void UnregisterForPrinting()
        {
            if (printDocument == null)
                return;

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

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

        protected virtual void CreatePrintPreviewPages(object sender, Windows.UI.Xaml.Printing.PaginateEventArgs e)
        {

            PrintDocument printDoc = (PrintDocument)sender;

            // A new "session" starts with each paginate event.
            //System.Threading.Interlocked.Increment(ref requestCount);

            //PageDescription pageDescription = new PageDescription();

            //// Get printer's page description.
            ////PrintTaskOptionDetails printDetailedOptions = PrintTaskOptionDetails.GetFromPrintTaskOptions(e.PrintTaskOptions);
            printPageDescription = e.PrintTaskOptions.GetPageDescription(0);

            //if (printPageDescription.ImageableRect.Width > theImage.ActualWidth ||
            //    printPageDescription.ImageableRect.Height > theImage.ActualHeight)
            //{
            //    theImage.Width = printPageDescription.ImageableRect.Width;

            //}


            //// Reset the error state
            ////printDetailedOptions.Options["photoSize"].ErrorText = string.Empty;

            //// Compute the printing page description (page size & center printable area)
            //pageDescription.PageSize = printPageDescription.PageSize;

            //pageDescription.Margin.Width = Math.Max(printPageDescription.ImageableRect.Left,
            //                                        printPageDescription.ImageableRect.Right - printPageDescription.PageSize.Width);

            //pageDescription.Margin.Height = Math.Max(printPageDescription.ImageableRect.Top,
            //                                         printPageDescription.ImageableRect.Bottom - printPageDescription.PageSize.Height);

            //pageDescription.ViewablePageSize.Width = printPageDescription.PageSize.Width - pageDescription.Margin.Width * 2;
            //pageDescription.ViewablePageSize.Height = printPageDescription.PageSize.Height - pageDescription.Margin.Height * 2;


            //// Try to maximize photo-size based on it's aspect-ratio
            //if ((pageDescription.ViewablePageSize.Width > pageDescription.ViewablePageSize.Height) && (photoSize != PhotoSize.psFullPage))
            //{
            //    var swap = pageDescription.PictureViewSize.Width;
            //    pageDescription.PictureViewSize.Width = pageDescription.PictureViewSize.Height;
            //    pageDescription.PictureViewSize.Height = swap;
            //}

            //pageDescription.IsContentCropped = photoScale == Scaling.sCrop;

            //// Recreate content only when : 
            //// - there is no current page description
            //// - the current page description doesn't match the new one
            //if (currentPageDescription == null || !currentPageDescription.Equals(pageDescription))
            //{
            //    ClearPageCollection();

            //    if (pageDescription.PictureViewSize.Width > pageDescription.ViewablePageSize.Width ||
            //        pageDescription.PictureViewSize.Height > pageDescription.ViewablePageSize.Height)
            //    {
            //        printDetailedOptions.Options["photoSize"].ErrorText = "Photo doesn’t fit on the selected paper";

            //        // Inform preview that it has only 1 page to show.
            //        printDoc.SetPreviewPageCount(1, PreviewPageCountType.Intermediate);

            //        // Add a custom "preview" unavailable page
            //        lock (printSync)
            //        {
            //            pageCollection[0] = new PreviewUnavailable(pageDescription.PageSize, pageDescription.ViewablePageSize);
            //        }
            //    }
            //    else
            //    {
            //        // Inform preview that is has #NumberOfPhotos pages to show.
            //        printDoc.SetPreviewPageCount(NumberOfPhotos, PreviewPageCountType.Intermediate);
            //    }

            //    currentPageDescription = pageDescription;
            //}
        }



        protected async virtual void GetPrintPreviewPage(object sender, GetPreviewPageEventArgs e)
        {
            //// Store a local copy of the request count to use later to determine if the computed page is out of date.
            //// If the page preview is unavailable an async operation will generate the content.
            //// When the operation completes there is a chance that a pagination request was already made therefore making this page obsolete.
            //// If the page is obsolete throw away the result (don't call SetPreviewPage) since a new GetPrintPreviewPage will server that request.
            //long requestNumber = 0;
            //Interlocked.Exchange(ref requestNumber, requestCount);
            //int pageNumber = e.PageNumber;

            //UIElement page;

            //bool pageReady = false;

            //// Try to get the page if it was previously generated.
            //lock (printSync)
            //{
            //    pageReady = pageCollection.TryGetValue(pageNumber - 1, out page);
            //}

            //if (!pageReady)
            //{
            //    // The page is not available yet.
            //    page = await GeneratePage(pageNumber, currentPageDescription);

            //    // If the ticket changed discard the result since the content is outdated.
            //    if (Interlocked.CompareExchange(ref requestNumber, requestNumber, requestCount) != requestCount)
            //    {
            //        return;
            //    }

            //    // Store the page in the list in case an invalidate happens but the content doesn't need to be regenerated.

            //    lock (printSync)
            //    {
            //        pageCollection[pageNumber - 1] = page;

            //        // Add the newly created page to the printing root which is part of the visual tree and force it to go
            //        // through layout so that the linked containers correctly distribute the content inside them.
            //        PrintingRoot.Children.Add(page);
            //        PrintingRoot.InvalidateMeasure();
            //        PrintingRoot.UpdateLayout();
            //    }
            //}

            PrintDocument printDoc = (PrintDocument)sender;

            // Send the page to preview.
            UIElement page = await GeneratePage(printPageDescription);
            printDoc.SetPreviewPage(1, page);
        }

        protected virtual async void AddPrintPages(object sender, AddPagesEventArgs e)
        {
            PrintDocument printDoc = (PrintDocument)sender;
            //printDoc.AddPage(theImage);
            UIElement page = await GeneratePage(printPageDescription);
            printDoc.AddPage(page);

            //// Loop over all of the preview pages
            //for (int i = 0; i < NumberOfPhotos; i++)
            //{
            //    UIElement page = null;
            //    bool pageReady = false;

            //    lock (printSync)
            //    {
            //        pageReady = pageCollection.TryGetValue(i, out page);
            //    }

            //    if (!pageReady)
            //    {
            //        // If the page is not ready create a task that will generate its content.
            //        page = await GeneratePage(i + 1, currentPageDescription);
            //    }

            //    printDoc.AddPage(page);
            //}

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

            // Reset the current page description as soon as possible since the PrintTask.Completed event might fire later (long running job)
            //currentPageDescription = null;
        }



        protected virtual void PrintTaskRequested(PrintManager sender, PrintTaskRequestedEventArgs e)
        {
            PrintTask printTask = null;
            printTask = e.Request.CreatePrintTask("Druckauftrag", sourceRequested =>
            {
                PrintTaskOptionDetails printDetailedOptions = PrintTaskOptionDetails.GetFromPrintTaskOptions(printTask.Options);

                // 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
                printDetailedOptions.DisplayedOptions.Clear();
                printDetailedOptions.DisplayedOptions.Add(Windows.Graphics.Printing.StandardPrintTaskOptions.MediaSize);
                //printDetailedOptions.DisplayedOptions.Add(Windows.Graphics.Printing.StandardPrintTaskOptions.Copies);


                PrintCustomItemListOptionDetails scaling = printDetailedOptions.CreateItemListOption("scaling", "Skalierung");
                scaling.AddItem("scaleNo", "keine");
                scaling.AddItem("scaleShrinkToFit", "anpassen");

                // Add the custom option to the option list.
                printDetailedOptions.DisplayedOptions.Add("scaling");

                // Set default orientation to landscape.
                printTask.Options.Orientation = PrintOrientation.Landscape;

                // Register for print task option changed notifications.
                printDetailedOptions.OptionChanged += PrintDetailedOptionsOptionChanged;
                // 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)
                    {
                        scale = Scaling.scaleNo;

                        await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
                        {
                            MessageDialog messageDialog = new MessageDialog("Druck nicht möglich");
                            messageDialog.ShowAsync();
                        });
                    }
                };

                sourceRequested.SetSource(printDocumentSource);
            });
        }


        private async void PrintDetailedOptionsOptionChanged(PrintTaskOptionDetails sender, PrintTaskOptionChangedEventArgs args)
        {
            bool invalidatePreview = false;

            // For this scenario we are interested only when the 2 custom options change (photoSize & scaling) in order to trigger a preview refresh.
            // Default options that change page aspect will trigger preview invalidation (refresh) automatically.
            // It is safe to ignore verifying other options and(or) combinations here because during Paginate event(CreatePrintPreviewPages) we check if the PageDescription changed.
            if (args.OptionId == null)
                return;

            string optionId = args.OptionId.ToString();


            if (optionId == "scaling")
            {
                IPrintOptionDetails scalingOption = sender.Options[optionId];
                string scalingValue = scalingOption.Value as string;

                if (!string.IsNullOrEmpty(scalingValue))
                {
                    switch (scalingValue)
                    {
                        case "scaleNo":
                            scale = Scaling.scaleNo;
                            break;
                        case "scaleShrinkToFit":
                            scale = Scaling.scaleShrinkToFit;
                            break;
                    }
                    invalidatePreview = true;
                }
            }

            // Invalidate preview if one of the 2 options (photoSize, scaling) changed.
            if (invalidatePreview)
            {
                await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, printDocument.InvalidatePreview);
            }
        }


        private async Task<UIElement> GeneratePage(PrintPageDescription printPageDescription)
        {
            Image image = new Image();

            if (theImage.Source != null)
            {
                image.Source = theImage.Source;

                if (scale == Scaling.scaleNo)
                {
                    image.Stretch = Stretch.None;
                    image.Width = theImage.ActualWidth;
                    image.Height = theImage.ActualHeight;
                }

                // Use the real image size when croping or if the image is smaller then the target area (prevent a scale-up).
                if (scale == Scaling.scaleShrinkToFit)
                {
                    image.Stretch = Stretch.Uniform;
                    image.Width = theImage.ActualWidth;
                    image.Height = theImage.ActualHeight;

                    image.Width = printPageDescription.PageSize.Width;
                    image.Height = printPageDescription.PageSize.Height;
                }

            }

            return image;
        }

    }
}
