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;
        PrintPageDescription printPageDescription;
        Scaling scale;

        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)
        {
            // Felder initialisieren und Ereignisbehandlung einrichten
            printDocument = new PrintDocument();
            printDocumentSource = printDocument.DocumentSource;

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

            // PrintManager erzeugen und dabei gleich für die Druckfunktion registrieren.
            PrintManager printMan = PrintManager.GetForCurrentView();
            printMan.PrintTaskRequested += PrintTaskRequested;
        }

        protected override void OnNavigatedFrom(NavigationEventArgs e)
        {
            if (printDocument == null)
                return;

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

            PrintManager printMan = PrintManager.GetForCurrentView();
            printMan.PrintTaskRequested -= PrintTaskRequested;
        }

        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 CreatePrintPreviewPages(object sender, Windows.UI.Xaml.Printing.PaginateEventArgs e)
        {
            PrintDocument printDoc = (PrintDocument)sender;
            printDoc.SetPreviewPageCount(1, PreviewPageCountType.Intermediate);

            // Die Seitenbeschreibung des Druckers abfragen ...
            printPageDescription = e.PrintTaskOptions.GetPageDescription(0);
        }

        protected async virtual void GetPrintPreviewPage(object sender, GetPreviewPageEventArgs e)
        {
            PrintDocument printDoc = (PrintDocument)sender;

            // Die Druckseite an die Druckvorschau senden
            UIElement page = await GeneratePage(printPageDescription);
            printDoc.SetPreviewPage(e.PageNumber, page);
        }

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

            printDoc.AddPagesComplete();
        }

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

                // Die anzuzeigenden Druckoptionen auswählen 
                printDetailedOptions.DisplayedOptions.Clear();
                printDetailedOptions.DisplayedOptions.Add(StandardPrintTaskOptions.MediaSize);
                PrintCustomItemListOptionDetails scaling = printDetailedOptions.CreateItemListOption("scaling", "Skalierung");
                scaling.AddItem("scaleNo", "keine");
                scaling.AddItem("scaleShrinkToFit", "anpassen");
                printDetailedOptions.DisplayedOptions.Add("scaling");

                // Orientierung auf Querformat setzen.
                printTask.Options.Orientation = PrintOrientation.Landscape;

                // Ereignisbehandlung für geänderte Optionen
                printDetailedOptions.OptionChanged += PrintDetailedOptionsOptionChanged;

                // Ereignisbehandlung für Druck-Ende
                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;

            if (args.OptionId == null)
                return;

            string optionId = args.OptionId.ToString();

            // Selbst definierte Druckoption zur Skalierung
            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;
                }
            }

            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)
            {
                // Das zu druckende Bild kopieren
                image.Source = theImage.Source;

                // Die Kopie gemäß der Druckeinstellungen skalieren
                if (scale == Scaling.scaleNo)
                {
                    image.Stretch = theImage.Stretch;
                    image.Width = theImage.ActualWidth;
                    image.Height = theImage.ActualHeight;
                }

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