using System;
using System.Linq;
using System.Collections.Generic;
using Windows.UI;
using Windows.UI.ViewManagement;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Navigation;
using Windows.UI.Xaml.Media.Imaging;
using Google.Apis.YouTube.v3.Data;
using Windows.ApplicationModel.Core;
using Windows.System;
using FoxTube.Pages;
using Windows.UI.Popups;
using Windows.ApplicationModel.Resources;
using Microsoft.Services.Store.Engagement;
using Windows.UI.Xaml.Shapes;
using Windows.UI.Xaml.Media;
using FoxTube.Controls;
using Microsoft.AppCenter.Analytics;
using Windows.ApplicationModel.DataTransfer;
using Windows.UI.Notifications;
using System.Xml;

namespace FoxTube
{
    /// <summary>
    /// Main app's layout
    /// </summary>
    public sealed partial class MainPage : Page
    {
        bool wasInvoked = false;
        readonly ResourceLoader resources = ResourceLoader.GetForCurrentView("Main");
        Dictionary<Type, string> headers;

        public ContentFrame PageContent => content;
        public ContentFrame VideoContent => videoPlaceholder;

        public MainPage()
        {
            headers = new Dictionary<Type, string>()
            {
                { typeof(Settings), resources.GetString("/Main/settings/Content") },
                { typeof(ChannelPage), resources.GetString("/Main/channel") },
                { typeof(PlaylistPage), resources.GetString("/Main/playlist") },
                { typeof(Search), resources.GetString("/Main/searchPlaceholder/PlaceholderText") },
                { typeof(Subscriptions), resources.GetString("/Main/subscriptions/Content") },
                { typeof(History), resources.GetString("/Main/history/Content") },
                { typeof(Home), resources.GetString("/Main/home/Content") },
                { typeof(Downloads), resources.GetString("/Main/downloads/Content") }
            };

            InitializeComponent();

            Window.Current.SetTitleBar(AppTitleBar);
            CoreApplication.GetCurrentView().TitleBar.ExtendViewIntoTitleBar = true;
            CoreApplication.GetCurrentView().TitleBar.LayoutMetricsChanged += (s, e) => SetTitleBar(s);

            SecretsVault.AuthorizationStateChanged += AuthorizationStateChanged;
            SecretsVault.SubscriptionsChanged += SecretsVault_SubscriptionsChanged;
            SecretsVault.Purchased += async (sender, e) =>
            {
                removeAds.Visibility = (e[0] as bool?).Value ? Visibility.Collapsed : Visibility.Visible;

                if (!(bool)e[0])
                {
                    removeAds.Content = $"{resources.GetString("/Main/adsFree/Content")} ({e[1]})";
                    return;
                }

                MessageDialog dialog = new MessageDialog(resources.GetString("/Main/purchaseSuccess"));
                dialog.Commands.Add(new UICommand(resources.GetString("/Main/close"), (command) => Methods.CloseApp()));
                dialog.Commands.Add(new UICommand(resources.GetString("/Main/delay")));
                dialog.CancelCommandIndex = 1;
                dialog.DefaultCommandIndex = 0;
                await dialog.ShowAsync();
            };
            SecretsVault.Initialize();

            if(StoreServicesFeedbackLauncher.IsSupported())
                feedback.Visibility = Visibility.Visible;

            if(SettingsStorage.ProcessClipboard)
            {
                Clipboard.ContentChanged += ParseClipboard;
                ParseClipboard(this);
            }

            PromptFeedback();
        }

        async void ParseClipboard(object sender = null, object e = null)
        {
            if (sender == null && Window.Current.CoreWindow.ActivationMode != Windows.UI.Core.CoreWindowActivationMode.Deactivated)
                return;
            try
            {
                string link = await Clipboard.GetContent().GetTextAsync();

                if (!link.Contains("youtube") && !link.Contains("youtu.be"))
                    return;

                string id;
                string type;
                string name;

                if (YoutubeExplode.YoutubeClient.TryParseChannelId(link, out id))
                {
                    type = "channel";
                    name = (await new YoutubeExplode.YoutubeClient().GetChannelAsync(id)).Title;
                    goto Complete;
                }
                else if (YoutubeExplode.YoutubeClient.TryParsePlaylistId(link, out id))
                {
                    type = "playlist";
                    name = (await new YoutubeExplode.YoutubeClient().GetPlaylistAsync(id)).Title;
                    goto Complete;
                }
                else if (YoutubeExplode.YoutubeClient.TryParseUsername(link, out id))
                {
                    id = await new YoutubeExplode.YoutubeClient().GetChannelIdAsync(id);
                    type = "channel";
                    name = (await new YoutubeExplode.YoutubeClient().GetChannelAsync(id)).Title;
                    goto Complete;
                }
                else if (YoutubeExplode.YoutubeClient.TryParseVideoId(link, out id))
                {
                    type = "video";
                    name = (await new YoutubeExplode.YoutubeClient().GetVideoAsync(id)).Title;
                    goto Complete;
                }
                return;

            Complete:
                Windows.Data.Xml.Dom.XmlDocument toastXml = new Windows.Data.Xml.Dom.XmlDocument();
                toastXml.LoadXml($@"<toast launch='clipboard|{type}|{id}'>
                                        <visual>
                                            <binding template='ToastGeneric'>
                                                <text>{resources.GetString("/Main/clipboardHead")}</text>
                                                <text>{name}</text>
                                                <text>{resources.GetString($"/Main/{type}")}</text>
                                            </binding>
                                        </visual>
                                        <actions>
                                            <action content='{resources.GetString("/Main/clipboardOpen")}' arguments='clipboard|{type}|{id}'/>
                                        </actions>
                                    </toast>");
                ToastNotificationManager.CreateToastNotifier().Show(new ToastNotification(toastXml));
                return;
            }
            catch { }
        }

        async void PromptFeedback()
        {
            if (SettingsStorage.Uptime.TotalHours >= 12 && SettingsStorage.PromptFeedback)
            {
                MessageDialog dialog = new MessageDialog(resources.GetString("/Main/feedbackMessage"));
                dialog.Commands.Add(new UICommand(resources.GetString("/Main/dontAsk"), (command) => SettingsStorage.PromptFeedback = false));
                dialog.Commands.Add(new UICommand(resources.GetString("/Main/promptLater")));
                dialog.Commands.Add(new UICommand(resources.GetString("/Main/sure"), async (command) =>
                {
                    SettingsStorage.PromptFeedback = false;
                    if (StoreServicesFeedbackLauncher.IsSupported())
                        await StoreServicesFeedbackLauncher.GetDefault().LaunchAsync();
                    else
                    {
                        MessageDialog message = new MessageDialog(resources.GetString("/Main/feedbackFail"));
                        message.Commands.Add(new UICommand(resources.GetString("/Main/sendEmail"), async (c) => await Launcher.LaunchUriAsync("mailto:michael.xfox@outlook.com".ToUri())));
                        message.Commands.Add(new UICommand(resources.GetString("/Main/goBack")));
                        message.CancelCommandIndex = 1;
                        message.DefaultCommandIndex = 0;
                        await message.ShowAsync();
                    }
                }));
                dialog.DefaultCommandIndex = 2;
                dialog.CancelCommandIndex = 1;
                await dialog.ShowAsync();
            }

            if (SettingsStorage.Uptime.TotalHours >= 24 && SettingsStorage.PromptReview)
            {
                MessageDialog dialog = new MessageDialog(resources.GetString("/Main/rate"));
                dialog.Commands.Add(new UICommand(resources.GetString("/Main/dontAsk"), (command) => SettingsStorage.PromptReview = false));
                dialog.Commands.Add(new UICommand(resources.GetString("/Main/promptLater")));
                dialog.Commands.Add(new UICommand(resources.GetString("/Main/sure"), async (command) =>
                {
                    SettingsStorage.PromptReview = false;
                    await Launcher.LaunchUriAsync("ms-windows-store://review/?ProductId=9NCQQXJTDLFH".ToUri());
                }));
                dialog.DefaultCommandIndex = 2;
                dialog.CancelCommandIndex = 1;
                await dialog.ShowAsync();
            }
        }

        public void SetTitleBar(CoreApplicationViewTitleBar coreTitleBar = null)
        {
            if (coreTitleBar != null)
            {
                bool full = ApplicationView.GetForCurrentView().IsFullScreenMode;
                double left = 12 + (full ? 0 : coreTitleBar.SystemOverlayLeftInset);
                AppTitle.Margin = new Thickness(left, 8, 0, 0);
                AppTitleBar.Height = coreTitleBar.Height;
            }

            var titleBar = ApplicationView.GetForCurrentView().TitleBar;
            
            titleBar.ButtonBackgroundColor = Colors.Transparent;
            titleBar.ButtonHoverBackgroundColor = Colors.IndianRed;
            titleBar.ButtonPressedBackgroundColor = Colors.DarkRed;
            titleBar.ButtonInactiveBackgroundColor = Colors.Transparent;
            titleBar.ButtonInactiveForegroundColor = Colors.Gray;

            if(RequestedTheme == ElementTheme.Dark || (RequestedTheme == ElementTheme.Default && Application.Current.RequestedTheme == ApplicationTheme.Dark))
                titleBar.ButtonForegroundColor = Colors.White;
            else
                titleBar.ButtonForegroundColor = Colors.Black;
        }
        
        private void SecretsVault_SubscriptionsChanged(object sender, params object[] args)
        {
            switch((string)args[0])
            {
                case "add":
                    if (nav.MenuItems.Count < 19)
                        nav.MenuItems.Add(args[1] as Subscription);
                    break;

                case "remove":
                    if(nav.MenuItems.Contains((Subscription)args[1]))
                    {
                        nav.MenuItems.Remove(args[1]);
                        if (SecretsVault.Subscriptions.Count >= 10)
                            nav.MenuItems.Add(SecretsVault.Subscriptions[9]);
                    }
                    break;
            }
        }
               
        private async void AuthorizationStateChanged(object sender, params object[] e)
        {
            wasInvoked = false;

            switch (e[0] as bool?)
            {
                case true:
                    account.Visibility = Visibility.Collapsed;

                    ToolTipService.SetToolTip(avatar, $"{SecretsVault.UserInfo.Name} ({SecretsVault.UserInfo.Email})");
                    myNameFlyout.Text = SecretsVault.UserInfo.Name;
                    myEmail.Text = SecretsVault.UserInfo.Email;
                    avatarFlyout.ProfilePicture = new BitmapImage(SecretsVault.UserInfo.Picture.ToUri()) { DecodePixelHeight = 65, DecodePixelWidth = 65 };
                    ((avatar.Content as Ellipse).Fill as ImageBrush).ImageSource = new BitmapImage(SecretsVault.UserInfo.Picture.ToUri()) { DecodePixelHeight = 25, DecodePixelWidth = 25 };

                    avatar.Visibility = Visibility.Visible;

                    toChannel.Visibility = Visibility.Visible;
                    toSubscriptions.Visibility = Visibility.Visible;
                    libHeader.Visibility = Visibility.Visible;
                    toHistory.Visibility = Visibility.Visible;
                    toLiked.Visibility = Visibility.Visible;
                    toLater.Visibility = Visibility.Visible;

                    if (SecretsVault.Subscriptions.Count > 0)
                    {
                        subsHeader.Visibility = Visibility.Visible;
                        for (int k = 0; k < SecretsVault.Subscriptions.Count && k < 10; k++)
                            nav.MenuItems.Add(SecretsVault.Subscriptions[k]);
                    }
                    HistorySet.Load();

                    if (content.Frame.Content != null)
                        content.Refresh();
                    else
                        content.Frame.Navigate(typeof(Home));
                    break;

                case false:
                    content.Frame.Navigate(typeof(Home));

                    for (int k = nav.MenuItems.Count - 1; k > 8; k--)
                        nav.MenuItems.RemoveAt(k);

                    account.Visibility = Visibility.Visible;
                    avatar.Visibility = Visibility.Collapsed;

                    toChannel.Visibility = Visibility.Collapsed;
                    toSubscriptions.Visibility = Visibility.Collapsed;
                    libHeader.Visibility = Visibility.Collapsed;
                    toHistory.Visibility = Visibility.Collapsed;
                    toLiked.Visibility = Visibility.Collapsed;
                    toLater.Visibility = Visibility.Collapsed;
                    subsHeader.Visibility = Visibility.Collapsed;

                    subsHeader.Visibility = Visibility.Collapsed;

                    content.Frame.BackStack.Clear();
                    content.Frame.ForwardStack.Clear();
                    break;

                default:
                    MessageDialog dialog = new MessageDialog(resources.GetString("/Main/connectErrContent"), resources.GetString("/Main/connectErrHeader"));

                    if (StoreServicesFeedbackLauncher.IsSupported())
                        dialog.Commands.Add(new UICommand(resources.GetString("/About/feedback/Content"), async (command) => await StoreServicesFeedbackLauncher.GetDefault().LaunchAsync()));
                    dialog.Commands.Add(new UICommand(resources.GetString("/Main/tryAgain"), (command) =>
                    {
                        SecretsVault.Authorize();
                    }));
                    dialog.Commands.Add(new UICommand(resources.GetString("/Main/quit"), (command) =>
                    {
                        Methods.CloseApp();
                    }));

                    dialog.CancelCommandIndex = 1;
                    dialog.DefaultCommandIndex = 0;

                    await dialog.ShowAsync();
                    break;
            }

            if (videoPlaceholder.Frame.Content != null)
            {
                MaximizeVideo();
                videoPlaceholder.Refresh();
            }

            DownloadAgent.Initialize();
        }

        private async void Feedback_Click(object sender, RoutedEventArgs e)
        {
            await StoreServicesFeedbackLauncher.GetDefault().LaunchAsync();
        }

        private void SignIn_Click(object sender, RoutedEventArgs e)
        {
            SecretsVault.Authorize();
            Analytics.TrackEvent("Initialized authorization sequence");
        }

        private void Logout_Click(object sender, RoutedEventArgs e)
        {
            avatar.Flyout.Hide();
            SecretsVault.Deauthenticate();
        }

        public void GoToSearch(SearchParameters args)
        {
            content.Frame.Navigate(typeof(Search), new object[] { args, content });
        }

        public void GoToChannel(string id)
        {
            content.Frame.Navigate(typeof(ChannelPage), id);
        }

        public void GoToHome()
        {
            content.Frame.Navigate(typeof(Home));
        }

        public void GoToVideo(string id, string playlistId = null, bool incognito = false)
        {
            MaximizeVideo();

            nav.IsBackEnabled = true;
            nav.ExpandedModeThresholdWidth = short.MaxValue;
            nav.IsPaneOpen = false;

            videoPlaceholder.Frame.Navigate(typeof(VideoPage), new object[3] { id, playlistId, incognito });

            Title.Text = resources.GetString("/Main/video");
        }

        public void GoToDeveloper(string id)
        {
            content.Frame.Navigate(typeof(Settings), id);
        }

        public void GoToPlaylist(string id)
        {
            content.Frame.Navigate(typeof(PlaylistPage), id);
        }

        public void GoToHistory()
        {
            content.Frame.Navigate(typeof(History));
        }

        public void GoToDownloads()
        {
            content.Frame.Navigate(typeof(Downloads));
        }

        public void MinimizeAsInitializer()
        {
            if (videoPlaceholder.Frame.Content == null)
                return;

            if (videoPlaceholder.LoadingPage.State != LoadingState.Loaded)
                CloseVideo();
            else
                (videoPlaceholder.Frame.Content as VideoPage).Player.Minimize();

            Title.Text = headers[content.Frame.SourcePageType];
        }

        public void MinimizeVideo()
        {
            videoPlaceholder.Width = 432;
            videoPlaceholder.Height = 432 * (videoPlaceholder.Frame.Content as VideoPage).Player.ActualHeight / (videoPlaceholder.Frame.Content as VideoPage).Player.ActualWidth;
            videoPlaceholder.VerticalAlignment = VerticalAlignment.Bottom;
            videoPlaceholder.HorizontalAlignment = HorizontalAlignment.Right;
            videoPlaceholder.Margin = new Thickness(0, 0, 25, 50);

            if (content.Frame.CanGoBack)
                nav.IsBackEnabled = true;
            else
                nav.IsBackEnabled = false;

            SetNavigationMenu();

            Title.Text = headers[content.Frame.SourcePageType];
        }

        void SetNavigationMenu()
        {
            if (content.Frame.SourcePageType == typeof(Home) || content.Frame.SourcePageType == typeof(Settings) || content.Frame.SourcePageType == typeof(Subscriptions))
            {
                nav.ExpandedModeThresholdWidth = 1008;
                nav.IsPaneOpen = nav.DisplayMode == Microsoft.UI.Xaml.Controls.NavigationViewDisplayMode.Expanded ? true : false;
            }
            else
            {
                nav.ExpandedModeThresholdWidth = short.MaxValue;
                nav.IsPaneOpen = false;
            }
        }

        public void MaximizeVideo()
        {
            videoPlaceholder.Width = double.NaN;
            videoPlaceholder.Height = double.NaN;
            videoPlaceholder.VerticalAlignment = VerticalAlignment.Stretch;
            videoPlaceholder.HorizontalAlignment = HorizontalAlignment.Stretch;
            videoPlaceholder.Margin = new Thickness(0);

            if (videoPlaceholder.Frame.Content == null)
                return;

            nav.IsBackEnabled = true;
            Title.Text = resources.GetString("/Main/video");
            nav.ExpandedModeThresholdWidth = short.MaxValue;
            nav.IsPaneOpen = false;
        }

        public void CloseVideo()
        {
            if (ApplicationView.GetForCurrentView().IsFullScreenMode)
                ApplicationView.GetForCurrentView().ExitFullScreenMode();

            videoPlaceholder.Frame.Content = null;
            GC.Collect();
            MaximizeVideo();

            nav.IsBackEnabled = content.Frame.CanGoBack;

            SetNavigationMenu();

            Title.Text = headers[content.Frame.SourcePageType];
        }

        private void Search_QuerySubmitted(AutoSuggestBox sender, AutoSuggestBoxQuerySubmittedEventArgs args)
        {
            if(!string.IsNullOrWhiteSpace(search.Text))
                GoToSearch(new SearchParameters(search.Text));
        }

        private async void Search_TextChanged(AutoSuggestBox sender, AutoSuggestBoxTextChangedEventArgs args)
        {
            await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
            {
                if (search.Text.Length > 2 && args.Reason == AutoSuggestionBoxTextChangeReason.UserInput)
                {
                    try
                    {
                        XmlDocument doc = new XmlDocument();
                        doc.Load($"http://suggestqueries.google.com/complete/search?ds=yt&client=toolbar&q={search.Text}&hl={SettingsStorage.RelevanceLanguage}");

                        List<string> suggestions = new List<string>();

                        for (int i = 0; i < doc["toplevel"].ChildNodes.Count && i < 5; i++)
                            suggestions.Add(doc["toplevel"].ChildNodes[i]["suggestion"].GetAttribute("data"));

                        search.ItemsSource = suggestions;
                    }
                    catch { search.ItemsSource = new List<string>(); }
                }
            });
        }

        void SetNavigationItem(object item)
        {
            try
            {
                if (nav.SelectedItem != item)
                    nav.SelectedItem = item;
                else
                    wasInvoked = false;
            }
            catch { }
        }

        public void Content_Navigated(object sender, NavigationEventArgs e)
        {
            Title.Text = headers[content.Frame.SourcePageType];

            if (!wasInvoked)
            {
                wasInvoked = true;

                if (e.SourcePageType == typeof(Settings))
                    SetNavigationItem(nav.SettingsItem);
                else if (e.SourcePageType == typeof(Subscriptions))
                    SetNavigationItem(toSubscriptions);
                else if (e.SourcePageType == typeof(Downloads))
                    SetNavigationItem(toDownloads);
                else if (e.SourcePageType == typeof(Home))
                    SetNavigationItem(toHome);
                else if (e.SourcePageType == typeof(Search))
                    SetNavigationItem(null);
                else if(e.SourcePageType == typeof(ChannelPage))
                {
                    if (SecretsVault.IsAuthorized)
                    {
                        if (e.Parameter.ToString() == SecretsVault.AccountId)
                            SetNavigationItem(toChannel);
                        else if (nav.MenuItems.Contains(SecretsVault.Subscriptions.Find(i => i.Snippet.ResourceId.ChannelId == e.Parameter.ToString())))
                            SetNavigationItem(SecretsVault.Subscriptions.Find(i => i.Snippet.ResourceId.ChannelId == e.Parameter.ToString()));
                        else
                            SetNavigationItem(null);
                    }
                    else
                        SetNavigationItem(null);
                }
                else if(e.SourcePageType == typeof(History))
                    SetNavigationItem(toHistory);
                else if(e.SourcePageType == typeof(PlaylistPage))
                {
                    if (SecretsVault.IsAuthorized)
                    {
                        if (e.Parameter.ToString() == SecretsVault.UserChannel.ContentDetails.RelatedPlaylists.Likes)
                            SetNavigationItem(toLiked);
                        else if (e.Parameter.Equals("WL"))
                            SetNavigationItem(toLater);
                    }
                    else
                        SetNavigationItem(null);
                }
            }
            else
                wasInvoked = false;

            nav.IsBackEnabled = content.Frame.CanGoBack;

            SetNavigationMenu();

            if (videoPlaceholder.Frame.Content != null && videoPlaceholder.HorizontalAlignment == HorizontalAlignment.Stretch)
                MinimizeAsInitializer();
        }

        private void RemoveAds_Tapped(object sender, TappedRoutedEventArgs e)
        {
            SecretsVault.GetAdblock();
        }

        private void Nav_SelectionChanged(Microsoft.UI.Xaml.Controls.NavigationView sender, Microsoft.UI.Xaml.Controls.NavigationViewSelectionChangedEventArgs args)
        {
            if (!wasInvoked)
            {
                if (content == null)
                    return;
                wasInvoked = true;
                if (args.IsSettingsSelected)
                    content.Frame.Navigate(typeof(Settings));
                else
                {
                    if (args.SelectedItem == toHome)
                        content.Frame.Navigate(typeof(Home));
                    else if (args.SelectedItem == toHistory)
                        content.Frame.Navigate(typeof(History));
                    else if (args.SelectedItem == toLiked)
                        content.Frame.Navigate(typeof(PlaylistPage), SecretsVault.UserChannel.ContentDetails.RelatedPlaylists.Likes);
                    else if (args.SelectedItem == toLater)
                        content.Frame.Navigate(typeof(PlaylistPage), "WL");
                    else if (args.SelectedItem == toSubscriptions)
                        content.Frame.Navigate(typeof(Subscriptions));
                    else if (args.SelectedItem == toDownloads)
                        content.Frame.Navigate(typeof(Downloads));
                    else if (args.SelectedItem == toChannel)
                        content.Frame.Navigate(typeof(ChannelPage), SecretsVault.UserChannel.Id);
                    else
                        content.Frame.Navigate(typeof(ChannelPage), (args.SelectedItem as Subscription).Snippet.ResourceId.ChannelId);
                }
            }
            else
                wasInvoked = false;
        }

        private void Nav_BackRequested(Microsoft.UI.Xaml.Controls.NavigationView sender, Microsoft.UI.Xaml.Controls.NavigationViewBackRequestedEventArgs args)
        {
            if (videoPlaceholder.Frame.Content != null && double.IsNaN(videoPlaceholder.Width))
            {
                if (videoPlaceholder.Frame.CanGoBack)
                    videoPlaceholder.Frame.GoBack();
                else if (videoPlaceholder.LoadingPage.State != LoadingState.Loaded)
                    CloseVideo();
                else
                    MinimizeAsInitializer();
            }
            else
                content.Frame.GoBack();
        }

        private void Nav_PaneClosing(Microsoft.UI.Xaml.Controls.NavigationView sender, Microsoft.UI.Xaml.Controls.NavigationViewPaneClosingEventArgs args)
        {
            AppTitle.Visibility = Visibility.Collapsed;
        }

        private void Nav_PaneOpened(Microsoft.UI.Xaml.Controls.NavigationView sender, object args)
        {
            AppTitle.Visibility = Visibility.Visible;
        }
    }
}
