using System;
using System.IO;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media.Imaging;
using Windows.System;
using YoutubeExplode.Models.MediaStreams;
using YoutubeExplode;
using Windows.Storage;
using Google.Apis.YouTube.v3.Data;
using System.Threading;
using Windows.UI.Popups;
using Windows.UI.Notifications;
using Microsoft.Toolkit.Uwp.Notifications;
using System.Threading.Tasks;
using Windows.ApplicationModel.Resources;
using Microsoft.AppCenter.Analytics;
using System.Collections.Generic;
using Newtonsoft.Json;

namespace FoxTube.Controls
{
    public class DownloadItemContainer
    {
        public string Title { get; set; }
        public string Channel { get; set; }
        public string Id { get; set; }
        public string Name { get; set; }
        public string Extension { get; set; }
        public Uri Thumbnail { get; set; }
        public string Quality { get; set; }
        public TimeSpan Duration { get; set; }
        public bool IsDownloaded { get; set; }
    }

    public sealed partial class DownloadItem : UserControl
    {
        ResourceLoader resources = ResourceLoader.GetForCurrentView("Downloads");

        public DownloadItemContainer Container { get; private set; }
        public StorageFile File { get; private set; }
        
        CancellationTokenSource cts;
        Progress<double> progress;
        double percentage;
        DispatcherTimer timer;
        NotificationData data;
        
        public DownloadItem(MediaStreamInfo info, Video meta, string q)
        {
            InitializeComponent();

            Container = new DownloadItemContainer()
            {
                Channel = meta.Snippet.ChannelTitle,
                Duration = Methods.GetDuration(meta.ContentDetails.Duration),
                Extension = info.Container.GetFileExtension(),
                Id = meta.Id,
                IsDownloaded = false,
                Quality = q,
                Thumbnail = meta.Snippet.Thumbnails.Medium.Url.ToUri(),
                Title = meta.Snippet.Title
            };

            Download(info);
        }

        public DownloadItem(DownloadItemContainer container)
        {
            InitializeComponent();

            Container = container;
            Load();
        }

        async void Load()
        {
            File = await DownloadAgent.Downloads.TryGetItemAsync(Container.Name) as StorageFile;
            if (File == null)
                DownloadAgent.Remove(this);

            donePanel.Visibility = Visibility.Visible;
            progressPanel.Visibility = Visibility.Collapsed;

            SetMeta();
        }

        async void Download(MediaStreamInfo info)
        {
            File = await DownloadAgent.Downloads.CreateFileAsync($"{Container.Title.ReplaceInvalidChars('_')}.{Container.Extension}", CreationCollisionOption.GenerateUniqueName);
            Container.Name = File.Name;

            donePanel.Visibility = Visibility.Collapsed;
            progressPanel.Visibility = Visibility.Visible;
            SetMeta();

            cts = new CancellationTokenSource();
            progress = new Progress<double>();
            progress.ProgressChanged += (s, e) => percentage = e;

            timer = new DispatcherTimer() { Interval = TimeSpan.FromSeconds(1) };
            timer.Tick += (s, e) => UpdateInfo();

            #region Polling notification
            ToastContent toastContent = new ToastContent()
            {
                Visual = new ToastVisual()
                {
                    BindingGeneric = new ToastBindingGeneric()
                    {
                        Children =
                        {
                            new AdaptiveText() { Text = resources.GetString("/Downloads/toastStartHeader") },
                            new AdaptiveProgressBar()
                            {
                                Title = Container.Title,
                                Status = resources.GetString("/Downloads/downloading/Text"),
                                Value = new BindableProgressBarValue("value")
                            }
                        }
                    }
                },

                Actions = new ToastActionsCustom()
                {
                    Buttons =
                    {
                        new ToastButton(resources.GetString("/Downloads/cancel/Content"), $"dcancel|{Container.Id}")
                    }
                },
                Launch = "download",
                ActivationType = ToastActivationType.Foreground
            };

            data = new NotificationData();
            data.Values["value"] = "0";
            ToastNotificationManager.CreateToastNotifier().Show(new ToastNotification(toastContent.GetXml()) { Tag = $"download|{Container.Id}", Data = data });
            #endregion

            timer.Start();

            try
            {
                await new YoutubeClient().DownloadMediaStreamAsync(info, await File.OpenStreamForWriteAsync(), progress, cts.Token);

                Container.IsDownloaded = true;
                timer.Stop();

                if (cts.IsCancellationRequested)
                    throw new TaskCanceledException();
                else
                    DownloadCompleted();
            }
            catch (TaskCanceledException)
            {
                Windows.Data.Xml.Dom.XmlDocument template = new Windows.Data.Xml.Dom.XmlDocument();
                template.LoadXml($@"<toast>
                                        <visual>
                                            <binding template='ToastGeneric'>
                                                <text>{resources.GetString("/Downloads/toastCanceledHeader")}</text>
                                                <text>{Container.Title}</text>
                                            </binding>
                                        </visual>
                                    </toast>");
                ToastNotificationManager.CreateToastNotifier().Show(new ToastNotification(template) { Tag = $"download|{Container.Id}" });

                try { await File.DeleteAsync(StorageDeleteOption.PermanentDelete); }
                catch { }

                DownloadAgent.Remove(this);
            }
            catch (Exception e)
            {
                Windows.Data.Xml.Dom.XmlDocument template = new Windows.Data.Xml.Dom.XmlDocument();
                template.LoadXml($@"<toast>
                                        <visual>
                                            <binding template='ToastGeneric'>
                                                <text>{resources.GetString("/Downloads/failedHead")}</text>
                                                <text>{resources.GetString("/Downloads/failedBody")}</text>
                                            </binding>
                                        </visual>
                                    </toast>");
                ToastNotificationManager.CreateToastNotifier().Show(new ToastNotification(template) { Tag = $"download|{Container.Id}" });

                Analytics.TrackEvent("Failed to download video", new Dictionary<string, string>()
                {
                    { "Exception", e.GetType().ToString() },
                    { "Message", e.Message },
                    { "Container", JsonConvert.SerializeObject(Container) },
                    { "StackTrace", e.StackTrace }
                });

                try { await File.DeleteAsync(StorageDeleteOption.PermanentDelete); }
                catch { }

                DownloadAgent.Remove(this);
            }
        }

        private void UpdateInfo()
        {
            progressBar.Value = percentage;
            progressText.Text = Math.Round(percentage * 100, 1) + "%";

            data.Values["value"] = percentage.ToString();
            ToastNotificationManager.CreateToastNotifier().Update(data, $"download|{Container.Id}");
        }

        private void DownloadCompleted()
        {
            Windows.Data.Xml.Dom.XmlDocument template = new Windows.Data.Xml.Dom.XmlDocument();
            template.LoadXml($@"<toast activationType='foreground' launch='download'>
                                    <visual>
                                        <binding template='ToastGeneric'>
                                            <text>{resources.GetString("/Downloads/toastCompleteHeader")}</text>
                                            <text>{Container.Title}</text>
                                        </binding>
                                    </visual>

                                    <actions>
                                        <action content='{resources.GetString("/Downloads/gotoOrign/Text")}'
                                            activationType='foreground'
                                            arguments='video|{Container.Id}'/>
                                    </actions>
                                </toast>");
            ToastNotificationManager.CreateToastNotifier().Show(new ToastNotification(template) { Tag = $"download|{Container.Id}" });

            donePanel.Visibility = Visibility.Visible;
            progressPanel.Visibility = Visibility.Collapsed;
        }

        private async void open_Click(object sender, RoutedEventArgs e)
        {
            await Launcher.LaunchFileAsync(File);
        }

        private void gotoOriginal_Click(object sender, RoutedEventArgs e)
        {
            Methods.MainPage.GoToVideo(Container.Id);
        }

        public async void Cancel(bool prompt = true)
        {
            cancel.IsEnabled = false;
            if(prompt)
            {
                MessageDialog dialog = new MessageDialog(resources.GetString("/Downloads/prompt"), resources.GetString("/Downloads/cancellingHeader"));

                dialog.Commands.Add(new UICommand(resources.GetString("/Downloads/yes"), null, false));
                dialog.Commands.Add(new UICommand(resources.GetString("/Downloads/no"), null, true));

                dialog.DefaultCommandIndex = 1;
                if ((bool)(await dialog.ShowAsync()).Id)
                {
                    cancel.IsEnabled = true;
                    return;
                }
            }

            cts.Cancel();
            DownloadAgent.Remove(this);
            status.Text = resources.GetString("/Downloads/cancelling");
            progressBar.IsIndeterminate = true;
        }
        
        void SetMeta()
        {
            try
            {
                thumbnail.Source = new BitmapImage(Container.Thumbnail) { DecodePixelHeight = (int)thumbnail.ActualHeight, DecodePixelWidth = (int)thumbnail.ActualWidth };

                title.Text = Container.Title;
                path.Text = File.Path;

                meta.Text = $@"{resources.GetString("/Downloads/ext")}: {Container.Extension}
{resources.GetString("/Downloads/quality")}: {Container.Quality}
{resources.GetString("/Downloads/duration")}: {Container.Duration}
{resources.GetString("/Downloads/author")}: {Container.Channel}";
            }
            catch
            {
                DownloadAgent.Remove(this);
            }
        }

        private void Cancel_Click(object sender, RoutedEventArgs e)
        {
            Cancel();
        }
    }
}
