using System;
using System.Collections.ObjectModel;
using System.Collections.Generic;
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;

namespace Aurora.Design.Views.Main
{
    /// <summary>
    /// Delegate for updating player metadata
    /// </summary>
    /// <param name="media"></param>
    public delegate void SetPlayerMetadataDelegate(BaseMedia media);

    public delegate void SetPlayerVisibleDelegate(Boolean visible);

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

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

            _player = (Player)ContentPage.FindByName("Player");
            _viewContent = (ContentPresenter)ContentPage.Content.FindByName("ViewContent");

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

            }

            //Activate viewmodel
            vm.OnActive();

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

            //Assign viewModel
            _lastViewModel = vm;
            view.BindingContext = vm;
            //Assign player controls to viewmodel
            AssignPlayerControls(vm);

            _viewContent.Content = view;

            MasterPage.ListView.SelectedItem = null;
        }

        /// <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);
            vm.OnActive();

            _viewContent.Content = view;

            MasterPage.ListView.SelectedItem = screenList.FirstOrDefault();
        }

        /// <summary>
        /// Unassign setplayer delegate to prevent vms from changing player info when inactive
        /// </summary>
        /// <param name="vm"></param>
        private void UnassignPlayerControls(BaseViewModel vm)
        {
            vm.SetPlayerMetadata = null;
            vm.SetPlayerVisible = 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)
        {
            _player.PlayButtonCommand = new Command(vm.OnPlayButtonExecute, vm.CanPlayButtonExecute);
            _player.NextButtonCommand = new Command(vm.OnNextButtonExecute, vm.CanNextButtonExecute);
            _player.PreviousButtonCommand = new Command(vm.OnPreviousButtonExecute, vm.CanPreviousButtonExecute);

            //Assign SetPlayer delegate
            vm.SetPlayerMetadata = SetPlayer;
            vm.SetPlayerVisible = SetPlayerVisible;
        }

        /// <summary>
        /// SetPlayerDelegate implementation
        /// </summary>
        /// <param name="media"></param>
        private void SetPlayer(BaseMedia media)
        {
            if (media.Metadata is AudioMetadata)
            {
                AudioMetadata meta = (AudioMetadata)media.Metadata;
                _player.ArtistName = meta.Artist;
                _player.SongTitle = meta.Title;
            }
        }

        /// <summary>
        /// SetPlayerVisibleDelegate implementation
        /// </summary>
        /// <param name="visible"></param>
        private void SetPlayerVisible(Boolean visible)
        {
            _player.IsVisible = visible;
        }
    }

}
