using System;
using System.Collections.ObjectModel;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Linq;
using Aurora.Design.Components.NavigationMenu;
using Aurora.Design.Views.MainView;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
using Aurora.Models.Media;
using Aurora.Design.Components.MediaPlayer;
using Aurora.Services.PlayerService;
using System.Threading;

namespace Aurora.Design.Views.Main
{
    public enum PlayAction
    {
        Play,
        Pause,
        Resume,
        Stop
    }

    /// <summary>
    /// Delegate for updating player metadata
    /// </summary>
    /// <param name="media"></param>
    public delegate Task SetPlayerDelegate(BaseMedia media, PlayAction action);
    public delegate bool GetIsPlayingDelegate();
    public delegate void ShowModalDelegate(Type view, BaseDialogViewModel viewModel);
    public delegate void HideModalDelegate();
    public delegate void FinishDialogDelegate();

    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class MainView : ContentPage//, IDisposable
    {
        private Dictionary<int, BaseViewModel> _viewModels;
        private BaseViewModel _lastViewModel;
        private Player _playerComponent;
        private PlayerService _playerService;
        private ContentPresenter _viewContent;

        public MainView()
        {
            InitializeComponent();
            BindingContext = new MainViewModel();
            _viewModels = new Dictionary<int, BaseViewModel>();

            _playerComponent = Player;

            _viewContent = (ContentPresenter)Content.FindByName("ViewContent");
            _playerService = PlayerService.Instance;

            MasterPage.ListView.ItemSelected += OnNavItemSelected;

            Appearing += OnAppearing;
        }

        public void Dispose()
        {
            Appearing -= OnAppearing;
        }

        /// <summary>
        /// Event handler for side bar items being selected
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnNavItemSelected(object sender, SelectedItemChangedEventArgs e)
        {
            var item = e.SelectedItem as NavigationItem;
            if (item == null)
                return;

            var view = (View)Activator.CreateInstance(item.TargetType);

            //Check if we have an instantiated viewModel
            BaseViewModel vm = new BaseViewModel();
            if (_viewModels.ContainsKey(item.Id))
            {
                _viewModels.TryGetValue(item.Id, out vm);
            }
            else
            {
                if (item.TargetViewModelType.BaseType != typeof(BaseViewModel))
                {
                    throw new InvalidOperationException("TargetViewModel field must be of type BaseViewModel");
                }

                //Instantiate new view model
                vm = (BaseViewModel)Activator.CreateInstance(item.TargetViewModelType);
                _viewModels.Add(item.Id, vm);

            }

            //Assign player controls to viewmodel
            AssignPlayerControls(vm);
            ChangeModalVisiblity(false);

            //Activate viewmodel
            vm.OnActive();

            //Deactivate last viewModel
            _lastViewModel.OnInactive();
            //Unasign deactivating vm
            UnassignPlayerControls(_lastViewModel);

            //Assign viewModel
            _lastViewModel = vm;
            view.BindingContext = vm;

            _viewContent.Content = view;
        }

        /// <summary>
        /// Event handler for page appearing.
        /// </summary>
        /// <param name="sender">The object that fired the event.</param>
        /// <param name="args">The event arguments</param>
        private void OnAppearing(object sender, EventArgs args)
        {
            //Set initial view from first item in list
            ObservableCollection<NavigationGroupItem> screenList = (ObservableCollection<NavigationGroupItem>)MasterPage.ListView.ItemsSource;

            //Assign viewModel
            NavigationItem firstNavItem = screenList.FirstOrDefault().FirstOrDefault();
            var view = (View)Activator.CreateInstance(firstNavItem.TargetType);

            BaseViewModel vm = new BaseViewModel();
            if (_viewModels.ContainsKey(firstNavItem.Id))
            {
                _viewModels.TryGetValue(firstNavItem.Id, out vm);
            }
            else
            {
                //Instantiate new view model
                vm = (BaseViewModel)Activator.CreateInstance(firstNavItem.TargetViewModelType);
                _viewModels.Add(firstNavItem.Id, vm);
            }

            view.BindingContext = vm;
            _lastViewModel = vm;
            AssignPlayerControls(vm);
            ChangeModalVisiblity(false);
            vm.OnActive();

            _viewContent.Content = view;
            MasterPage.ListView.SelectedItem = firstNavItem;

        }

        /// <summary>
        /// Unassign setplayer delegate to prevent vms from changing player info when inactive
        /// </summary>
        /// <param name="vm"></param>
        private void UnassignPlayerControls(BaseViewModel vm)
        {
            vm.ChangePlayerState = null;
            vm.IsPlaying = null;
            vm.ShowModal = null;
        }

        /// <summary>
        /// Assign main views music player controls to a view model
        /// </summary>
        /// <param name="vm">BaseViewModel to assign controls to</param>
        private void AssignPlayerControls(BaseViewModel vm)
        {
            _playerComponent.PlayButtonCommand = new Command(vm.OnPlayButtonExecute, vm.CanPlayButtonExecute);
            _playerComponent.NextButtonCommand = new Command(vm.OnNextButtonExecute, vm.CanNextButtonExecute);
            _playerComponent.PreviousButtonCommand = new Command(vm.OnPreviousButtonExecute, vm.CanPreviousButtonExecute);

            vm.ChangePlayerState = ChangePlayerState;
            vm.IsPlaying = () =>
            {
                return _playerService.PlaybackState == PlaybackState.Playing;
            };
            vm.ShowModal = this.ShowModal;
            vm.HideModal = this.HideModal;

        }

        /// <summary>
        /// Delegate handler for a view model controling music playback
        /// </summary>
        /// <param name="media"></param>
        /// <param name="action"></param>
        /// <returns></returns>
        private async Task ChangePlayerState(BaseMedia media, PlayAction action)
        {
            if (media != null && media.Metadata is AudioMetadata)
            {
                AudioMetadata meta = (AudioMetadata)media.Metadata;
                _playerComponent.ArtistName = meta.Artist;
                _playerComponent.SongTitle = meta.Title;
            }

            switch (action)
            {
                case PlayAction.Pause:
                    {
                        _playerService.Pause();
                        _playerComponent.IsPlaying = false;
                        break;
                    }
                case PlayAction.Play:
                    {
                        if (media == null)
                        {
                            break;
                        }

                        if (!_playerService.IsMediaLoaded(media))
                        {
                            await _playerService.LoadMedia(media).ConfigureAwait(true);
                        }

                        _playerService.Play();
                        _playerComponent.IsPlaying = true;
                        break;
                    }
                case PlayAction.Resume:
                    {
                        _playerService.Play();
                        _playerComponent.IsPlaying = true;
                        break;
                    }
                case PlayAction.Stop:
                    {
                        _playerService.Stop();
                        _playerComponent.IsPlaying = false;
                        break;
                    }
            }

        }

        private void ShowModal(Type view, BaseDialogViewModel viewModel)
        {
            ContentPresenter modalContainer = (ContentPresenter)Modal.FindByName("ViewContent");
            var vw = (View)Activator.CreateInstance(view);

            vw.BindingContext = viewModel;

            //Set modal container content
            modalContainer.Content = vw;

            //Set visibility
            ChangeModalVisiblity(true);
        }

        private void HideModal()
        {
            ChangeModalVisiblity(false);
        }

        private void ChangeModalVisiblity(bool isVisible)
        {
            Modal.IsVisible = isVisible;
            Content.IsVisible = !isVisible;
        }
    }
}