using System;
using System.Collections.Generic;
using System.Linq;
using Windows.Foundation;
using Windows.UI.Core;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Input;
using Google.Apis.YouTube.v3.Data;
using Windows.UI.Xaml.Media.Imaging;
using Windows.Media;
using Windows.Storage.Streams;
using Windows.UI.ViewManagement;
using System.Xml;
using Windows.ApplicationModel.Core;
using Windows.UI;
using Windows.Media.Casting;
using YoutubeExplode.Models.MediaStreams;
using YoutubeExplode;
using YoutubeExplode.Models.ClosedCaptions;
using System.Globalization;
using FoxTube.Controls;
using Windows.System;
using Windows.Media.Core;
using Windows.Media.Playback;
using System.Net.Http;
using System.Diagnostics;
using Windows.UI.Popups;

namespace FoxTube
{
    public sealed partial class VideoPlayer : UserControl
    {
        public Video item;
        public string avatar;

        public event Event NextClicked;
        public event ObjectEventHandler MiniMode;
        public PlayerControls Controls => videoSource.TransportControls as PlayerControls;
        public TimeSpan Position => videoSource.Position;

        bool audioLoaded = false;
        bool videoLoaded = false;
        bool isMuxed = false;
        DispatcherTimer muxedTimer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(100) };

        TimeSpan timecodeBackup;
        bool needUpdateTimecode = false;

        SystemMediaTransportControls systemControls;

        public VideoPlayer()
        {
            InitializeComponent();
        }

        public async void Initialize(Video meta, string channelAvatar, bool privateMode = false)
        {
            item = meta;
            avatar = channelAvatar;

            videoSource.PosterSource = new BitmapImage((meta.Snippet.Thumbnails.Maxres ?? meta.Snippet.Thumbnails.Medium).Url.ToUri());
            Controls.SetMeta(meta.Snippet.Title, meta.Snippet.ChannelTitle);

            if (item.Snippet.LiveBroadcastContent == "none")
            {
                InitializeContols();
                Controls.SetQualities(await new YoutubeClient().GetVideoMediaStreamInfosAsync(item.Id));
                Controls.SetCaptions(await new YoutubeClient().GetVideoClosedCaptionTrackInfosAsync(item.Id));
            }
            else if (item.Snippet.LiveBroadcastContent == "live")
            {
                InitializeContols();
                Controls.SetStream((await new YoutubeClient().GetVideoMediaStreamInfosAsync(item.Id)).HlsLiveStreamUrl);
            }
            else
                videoSource.AreTransportControlsEnabled = false;

            if (!privateMode)
                Debug.WriteLine("TODO: history entry creation");
                // TODO: Create history entry

            Visibility = Visibility.Visible;
        }

        public void InitializeContols()
        {
            videoSource.Volume = SettingsStorage.Volume;

            muxedTimer.Tick += (s, e) =>
            {
                if (!Enumerable.Range(-100, 100).Contains((int)(videoSource.Position - audioSource.Position).TotalMilliseconds))
                    audioSource.Position = videoSource.Position;
            };

            Controls.CloseRequested += Controls_CloseRequested;
            Controls.NextRequested += (s, e) => NextClicked?.Invoke();
            Controls.QualityChanged += Controls_QualityChanged;
            Controls.MiniModeChanged += Controls_MiniModeChanged;
            Controls.Player = videoSource;

            #region System Media Transport Controls
            systemControls = SystemMediaTransportControls.GetForCurrentView();
            systemControls.IsNextEnabled = true;
            systemControls.IsPauseEnabled = true;
            systemControls.IsPlayEnabled = true;

            systemControls.DisplayUpdater.Type = MediaPlaybackType.Video;
            systemControls.DisplayUpdater.VideoProperties.Title = item.Snippet.Title;
            systemControls.DisplayUpdater.VideoProperties.Subtitle = item.Snippet.ChannelTitle;
            systemControls.DisplayUpdater.Thumbnail = RandomAccessStreamReference.CreateFromUri(avatar.ToUri());
            systemControls.DisplayUpdater.Update();

            systemControls.ButtonPressed += SystemControls_Engaged;
            systemControls.IsEnabled = true;
            #endregion
        }

        public void Controls_MiniModeChanged(object sender, bool e)
        {
            videoSource.IsFullWindow = false;

            MiniMode?.Invoke(this, e);
        }

        private void Controls_QualityChanged(object sender, MediaStreamInfo requestedQuality, MediaStreamInfoSet list)
        {
            videoSource.Pause();

            timecodeBackup = videoSource.Position;
            needUpdateTimecode = true;

            audioLoaded = false;
            videoLoaded = false;
            muxedTimer.Stop();

            videoSource.Source = requestedQuality.Url.ToUri();
            if(requestedQuality is MuxedStreamInfo)
            {
                isMuxed = true;
                audioSource.Source = null;
            }
            else
                audioSource.Source = list.Audio.First().Url.ToUri();
        }

        public void Controls_CloseRequested(object sender, RoutedEventArgs e)
        {
            systemControls.IsEnabled = false;

            videoSource.Stop();
            audioSource.Stop();

            Methods.MainPage.CloseVideo();
        }

        private async void SystemControls_Engaged(SystemMediaTransportControls sender, SystemMediaTransportControlsButtonPressedEventArgs args)
        {
            await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
            {
                if (args.Button == SystemMediaTransportControlsButton.Next)
                    NextClicked?.Invoke();

                switch (args.Button)
                {
                    case SystemMediaTransportControlsButton.Play:
                        videoSource.Play();
                        break;
                    case SystemMediaTransportControlsButton.Pause:
                        videoSource.Pause();
                        break;
                    case SystemMediaTransportControlsButton.Next:
                        NextClicked.Invoke();
                        break;
                }
            });
        }

        public void Pause()
        {
            videoSource.Pause();
        }

        private void VideoSource_CurrentStateChanged(object sender, RoutedEventArgs e)
        {
            if (audioSource.Source == null)
                return;

            switch(videoSource.CurrentState)
            {
                case Windows.UI.Xaml.Media.MediaElementState.Buffering:
                case Windows.UI.Xaml.Media.MediaElementState.Paused:
                    systemControls.PlaybackStatus = MediaPlaybackStatus.Paused;
                    // TODO: Create history entry
                    audioSource.Pause();
                    break;
                case Windows.UI.Xaml.Media.MediaElementState.Playing:
                    systemControls.PlaybackStatus = MediaPlaybackStatus.Playing;
                    audioSource.Play();
                    break;
            }
        }

        private void AudioSource_CurrentStateChanged(object sender, RoutedEventArgs e)
        {
            if(audioSource.CurrentState == Windows.UI.Xaml.Media.MediaElementState.Playing)
                muxedTimer.Start();
            else
                muxedTimer.Stop();
        }

        private void VideoSource_MediaOpened(object sender, RoutedEventArgs e)
        {
            videoLoaded = true;
            if (needUpdateTimecode)
            {
                videoSource.Position = timecodeBackup;
                needUpdateTimecode = false;
            }
            if (audioLoaded || isMuxed)
                videoSource.Play();
        }

        private void AudioSource_MediaOpened(object sender, RoutedEventArgs e)
        {
            audioLoaded = true;
            if (needUpdateTimecode)
                audioSource.Position = timecodeBackup;
            if (videoLoaded)
                videoSource.Play();
        }

        private void VideoSource_VolumeChanged(object sender, RoutedEventArgs e)
        {
            audioSource.Volume = videoSource.Volume;
        }
    }
}
