using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
using Ion.Sdk.DemoApp.Framework;
using Ion.Sdk.Ici;
using System.Windows.Threading;
using System.Timers;
using System.Windows.Controls;
using Autofac;
using Timer = System.Timers.Timer;

namespace Ion.Sdk.DemoApp.Views
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window, IExceptionHandler, IMessagingService
    {
        #region -=Instance members=-

        private readonly ViewModel viewModel;
        private ILifetimeScope scope;

        public ExceptionHandlerViewModel ExceptionHandler { get; }

        public MessagingServiceViewModel MessagingService { get; }

        public MainWindow(ViewModel viewModel, ExceptionHandlerViewModel exceptionHandler, MessagingServiceViewModel messasgingService, ILifetimeScope scope)
        {
            this.scope = scope;
            ExceptionHandler = exceptionHandler;
            MessagingService = messasgingService;
            InitializeComponent();

            DataContext = this.viewModel = viewModel;
        }

        #region -=IExceptionHandler members=-

        void IExceptionHandler.Handle(Exception ex) => ExceptionHandler.Handle(ex);

        #endregion

        #region -=IMessagingService members=-

        void IMessagingService.Show(Framework.Message message) => MessagingService.Show(message);

        #endregion

        private void ViewsTabControl_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            try
            {
                if (e.AddedItems.Count > 0 && e.AddedItems[0] is IAppViewInfo viewInfo)
                {
                    viewModel.ActiveView = scope.ResolveNamed<IAppView>(viewInfo.Id);
                }
            }
            catch (Exception ex)
            {
                ExceptionHandler.Handle(ex);
            }
        }

        #endregion

        #region -=Definitions=-

        public sealed class ViewModel : BindableObject
        {
            private IAppView activeView = null;
            public IAppView ActiveView
            {
                get => activeView;
                set => Set(ref activeView, value);
            }

            public IEnumerable<IAppViewInfo> AvailableViews { get; }

            public ViewModel(IEnumerable<IAppViewInfo> availableViews)
            {
                AvailableViews = availableViews;
            }
        }

        public class ExceptionHandlerViewModel : BindableObject, IExceptionHandler
        {
            #region -=Properties/Fields=-
            private readonly Dispatcher dispatcher;
            private readonly Timer autoCloseTimer = new Timer { AutoReset = false, Enabled = false, Interval = 15000 };
            private Exception model;

            public Exception Model
            {
                get => model;
                private set
                {
                    Set(ref model, value);
                    OnPropertyChanged(nameof(DetailsText));

                }
            }
            public string DetailsText
            {
                get
                {
                    if (model == null) return string.Empty;

                    var innerExceptionPart = model.InnerException?.Message != null ? $"\n\nInner exception:\n\n{model.InnerException.Message}" : string.Empty;

                    return $"{model.Message}{innerExceptionPart}\n\nStackTrace:\n\n{Model.StackTrace}";
                }
            }

            private bool isShowingException = false;
            public bool IsShowingException
            {
                get => isShowingException;
                set => Set(ref isShowingException, value);
            }

            private bool isShowingDetails = false;

            public bool IsShowingDetails
            {
                get => isShowingDetails;
                set
                {
                    if (Set(ref isShowingDetails, value, () => isShowingDetails != value) && value)
                        autoCloseTimer.Stop();
                }
            }

            public Command CloseCommand { get; }
            #endregion

            #region -=Constructors=-
            public ExceptionHandlerViewModel(Dispatcher dispatcher)
            {
                this.dispatcher = dispatcher;
                CloseCommand = Command.Create(Close);
                autoCloseTimer.Elapsed += AutoCloseTimer_Elapsed;
            }
            #endregion

            #region -=Public methods=-
            public void Handle(Exception ex)
            {
                App.Log(ex);
                Model = ex;
                IsShowingException = model != null;
                IsShowingDetails = false;
                if (isShowingException)
                {
                    autoCloseTimer.Start();
                }
            }
            #endregion

            #region -=Private methods=-

            private void Close(object parameter)
            {
                autoCloseTimer.Stop();
                IsShowingException = false;
            }
            private void AutoCloseTimer_Elapsed(object sender, ElapsedEventArgs e)
            {
                dispatcher.BeginInvoke(new Action(() =>
                {
                    Close(null);
                }));
            }
            #endregion
        }

        public class MessagingServiceViewModel : BindableObject
        {
            private bool isShowingMessage = false;

            private readonly List<Framework.Message> messages = new List<Framework.Message>();
            private int index = 0;

            public Framework.Message Model => messages.Count > 0 ? messages[index] : null;

            public int Index => index + 1;
            public int MessageCount => messages.Count;

            public bool IsShowingMessage
            {
                get => isShowingMessage;
                private set
                {
                    Set(ref isShowingMessage, value);
                    if (!isShowingMessage)
                    {
                        messages.Clear();
                        index = 0;
                    }
                }
            }

            public Command CloseCommand { get; }
            public Command ShowPreviousMessageCommand { get; }
            public Command ShowNextMessageCommand { get; }

            #region -=Constructors=-
            public MessagingServiceViewModel()
            {
                CloseCommand = Command.Create(parameter => IsShowingMessage = false);
                ShowNextMessageCommand = Command.Create(parameter => ShowMessage(() => index++), CanShowNextMessage);
                ShowPreviousMessageCommand = Command.Create(parameter => ShowMessage(() => index--), CanShowPreviousMessage);
            }
            #endregion

            public void Show(Framework.Message message)
            {
                if (message == null) return;
                messages.Add(message);
                index = messages.Count - 1;

                IsShowingMessage = true;

                RefreshProperties();
            }

            private bool CanShowNextMessage(object parameter)
                => messages.Count > 0 && index < (messages.Count - 1);

            private bool CanShowPreviousMessage(object parameter)
                => messages.Count > 0 && index > 0;

            private void ShowMessage(Action alterIndex)
            {
                alterIndex();
                RefreshProperties();
            }

            private void RefreshProperties()
            {
                OnPropertyChanged(nameof(Model));
                OnPropertyChanged(nameof(Index));
                OnPropertyChanged(nameof(MessageCount));

                ShowNextMessageCommand.RaiseCanExecuteChanged();
                ShowPreviousMessageCommand.RaiseCanExecuteChanged();
            }
        }

        #endregion
    }

    public class ProgressOperationModeToFriendlyNameConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value is ProgressOperationMode)
            {
                switch ((ProgressOperationMode)value)
                {
                    case ProgressOperationMode.Working:
                        return "Please wait...";
                    case ProgressOperationMode.LoadingData:
                        return "Loading data...";
                    case ProgressOperationMode.RetrievingData:
                        return "Retrieving data...";
                    case ProgressOperationMode.TransferingData:
                        return "Transfering data...";
                }
            }
            return value;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
}
