using System;
using Valg.EVA.Skanning.ContainerExtensions;
using Valg.EVA.Skanning.UserInterfaceElements.LightBox;
using Valg.EVA.Skanning.UserInterfaceElements.MainWindowProvider;
using Valg.EVA.Skanning.Scan.ReadSoftContracts.Events;
using System.ComponentModel;
using System.Threading;
using System.Windows.Input;
using Prism.Commands;
using Valg.Eva.Skanning.Scan.Presentation.MainWindow;
using Valg.EVA.Skanning.UserInterfaceElements;

namespace Valg.Eva.Skanning.Scan.Presentation.ScanInProgress
{
    public class ScanInProgressViewModel : ViewModelBase
    {
        private BoxScanningSession boxScanningSession;
        private readonly BatchIsCompleteViewModel batchIsCompleteViewModel;
        private readonly IMainWindowProvider mainWindowProvider;
        private INotificationDuringScanningViewModel notificationViewModel;
        private readonly ICommand cancelScanningCommand;
        private readonly ITypedFactory typedFactory;
        private bool scanMoreBallotsWasRequested;
        private readonly SynchronizationContext _syncContext;

        public ScanInProgressViewModel(BatchIsCompleteViewModel batchIsCompleteViewModel, IMainWindowProvider mainWindowProvider, ITypedFactory typedFactory)
        {
            this.batchIsCompleteViewModel = batchIsCompleteViewModel;
            this.mainWindowProvider = mainWindowProvider;
            this.typedFactory = typedFactory;
            _syncContext = SynchronizationContext.Current;

            cancelScanningCommand = new DelegateCommand(CancelScanning);
        }

        protected override void SubscribeToViewModelSpecificEvents()
        {
            SubscribeToEvent<SaveDataFailedEvent, EventArgs>(SetErrorSituationSaveDataFailed);
            SubscribeToEvent<BoxIsDeletedDuringScanEvent, EventArgs>(SetErrorBoxIsDeletedDuringScan);
            SubscribeToEvent<ScanReportedBatchInUseForJobErrorEvent, ScanReportedBatchInUseForJobErrorEventArgs>(SetErrorSituationBatchInUseForJob);
        }

        private void CancelScanning()
        {
            PublishEvent<ScanJobRequestedStoppedEvent, ScanJobRequestedStoppedEventArgs>(new ScanJobRequestedStoppedEventArgs());
            var viewModel = new ScanIsCanceledWindowViewModel();
            var eventArgs = new LightBoxEventArgs(typeof(ScanIsCanceledWindow), viewModel, true);
            viewModel.PrepareForShow();
            PublishEvent<ShowLightBoxEvent, LightBoxEventArgs>(eventArgs);
            PublishEvent<DeleteAllInBoxAndScanAgainEvent, DeleteAllInBoxAndScanAgainEventArgs>(new DeleteAllInBoxAndScanAgainEventArgs(false));
        }

        private void SetErrorBoxIsDeletedDuringScan(EventArgs eventArgs)
        {
            NotificationViewModel = typedFactory.Create<BoxIsDeletedDuringScanViewModel>();
            NotificationViewModel.SetDefaultFocus();
            PublishEvent<ScanJobRequestedStoppedEvent, ScanJobRequestedStoppedEventArgs>(new ScanJobRequestedStoppedEventArgs());
        }

        private void SetErrorSituationSaveDataFailed(EventArgs eventArgs)
        {
            var viewModel = typedFactory.Create<SaveDataFailedViewModel>();
            viewModel.Initialize(boxScanningSession);

            NotificationViewModel = viewModel;
            NotificationViewModel.SetDefaultFocus();
        }

        public void HandleScanReportedNoMorePaperInFeeder(ScanReportedNoMorePaperInFeederEventArgs scanReportedNoMorePaperInFeederEventArgs)
        {
            scanMoreBallotsWasRequested = scanReportedNoMorePaperInFeederEventArgs.ScanMoreBallotsWasRequested;
            if (boxScanningSession.LastDatabaseCallFailed)
            {
                SetErrorSituationSaveDataFailed(EventArgs.Empty);
            }
            else
            {
                if (boxScanningSession.HasScannedBallots && !scanMoreBallotsWasRequested)
                {
                    _syncContext.Post(o => DisplayBatchCompleteWindow(), null);
                    
                }
                else
                {
                    _syncContext.Post(o =>SetErrorSituationNoPaperInFeeder(), null);
                }
            }
            scanMoreBallotsWasRequested = false;
        }

        public void HandleScanReportedPaperJam(ScanReportedPaperJamEventArgs scanReportedPaperJamEventArgs)
        {
            if (boxScanningSession.LastDatabaseCallFailed)
            {
                _syncContext.Post(o =>SetErrorSituationSaveDataFailed(EventArgs.Empty), null);
            }
            else
            {
                _syncContext.Post(o =>
                {
                    var viewModel = typedFactory.Create<PaperJamViewModel>();
                    NotificationViewModel = viewModel;
                    NotificationViewModel.SetDefaultFocus();
                }, null);
            }
        }

        public void HandleScanReportedUnspecificReadsoftErrorCode(ScanReportedUnspecificReadsoftErrorCodeEventArgs scanReportedUnspecificReadsoftErrorCodeEventArgs)
        {
            _syncContext.Post(o =>
            {
                if (boxScanningSession.LastDatabaseCallFailed)
                {
                    SetErrorSituationSaveDataFailed(EventArgs.Empty);
                }
                else
                {
                    var viewModel = typedFactory.Create<UnspecificReadsoftErrorViewModel>();
                    viewModel.Initialize(boxScanningSession.HasScannedBallots);
                    NotificationViewModel = viewModel;
                    NotificationViewModel.SetDefaultFocus();
                }
            }, null);
        }

        private void SetErrorSituationBatchInUseForJob(ScanReportedBatchInUseForJobErrorEventArgs eventArgs)
        {
            var viewModel = typedFactory.Create<BatchInUseForJobViewModel>();
            NotificationViewModel = viewModel;
            NotificationViewModel.SetDefaultFocus();
        }

        private void SetErrorSituationNoPaperInFeeder()
        {
            var viewModel = typedFactory.Create<NoPaperInFeederViewModel>();
            viewModel.Initialize(scanMoreBallotsWasRequested);
            NotificationViewModel = viewModel;
            NotificationViewModel.SetDefaultFocus();
        }

        private void DisplayBatchCompleteWindow()
        {
            PublishEvent<UserWillBePromptedForBatchCompleteEvent, UserWillBePromptedForBatchCompleteEventArgs>(new UserWillBePromptedForBatchCompleteEventArgs());

            var batchIsCompleteWindow = new BatchIsCompleteWindow();
            var mainWindow = mainWindowProvider.GetMainWindow();
            batchIsCompleteWindow.Owner = mainWindow;
            batchIsCompleteViewModel.NumberOfBallotsScanned = boxScanningSession.NumberOfBallotsScanned;
            batchIsCompleteWindow.DataContext = batchIsCompleteViewModel;

            batchIsCompleteViewModel.PrepareForShow();

            var eventArgs = new LightBoxEventArgs(batchIsCompleteWindow, false);
            PublishEvent<ShowLightBoxEvent, LightBoxEventArgs>(eventArgs);
        }

        private void BoxScanningSessionOnPropertyChanged(object sender, PropertyChangedEventArgs propertyChangedEventArgs)
        {
            if (propertyChangedEventArgs.PropertyName == PropertyHelper.GetName<BoxScanningSession>(vm => vm.LastScannedImage))
            {
                OnPropertyChanged(PropertyHelper.GetName<ScanInProgressViewModel>(vm => vm.LastScannedImage));
            }
            else if (propertyChangedEventArgs.PropertyName == PropertyHelper.GetName<BoxScanningSession>(vm => vm.LastImageFrontRotation))
            {
                OnPropertyChanged(PropertyHelper.GetName<ScanInProgressViewModel>(vm => vm.CompensatingRotationAngle));
            }
            else if (propertyChangedEventArgs.PropertyName == PropertyHelper.GetName<BoxScanningSession>(vm => vm.NumberOfBallotsScanned))
            {
                OnPropertyChanged(PropertyHelper.GetName<ScanInProgressViewModel>(vm => vm.BallotsScannedCounterText));
            }
        }

        public int CompensatingRotationAngle => 360 - boxScanningSession.LastImageFrontRotation;
        public byte[] LastScannedImage => boxScanningSession.LastScannedImage;

        public string ScanInProgressText
        {
            get { return Valg.EVA.Skanning.Resources.Language.ScanLanguageString.ScanInProgress; }
        }

        public string BallotsScannedCounterText
        {
            get { return string.Format("{0}: {1}", Valg.EVA.Skanning.Resources.Language.ScanLanguageString.NumberOfBallotsScanned, boxScanningSession.NumberOfBallotsScanned); }
        }

        public string CancelScanningText
        {
            get { return Valg.EVA.Skanning.Resources.Language.ScanLanguageString.Button_CancelScanning; }
        }

        public INotificationDuringScanningViewModel NotificationViewModel
        {
            get { return notificationViewModel; }
            set
            {
                if (notificationViewModel != null)
                {
                    notificationViewModel.PropertyChanged -= NotificationViewModelOnPropertyChanged;
                }

                notificationViewModel = value;

                if (notificationViewModel != null)
                {
                    notificationViewModel.PropertyChanged += NotificationViewModelOnPropertyChanged;
                }

                OnPropertyChanged();
                OnPropertyChanged(PropertyHelper.GetName<ScanInProgressViewModel>(v => v.ThereAreNoErrorsOrNotifications));
            }
        }

        private void NotificationViewModelOnPropertyChanged(object sender, PropertyChangedEventArgs eventArgs)
        {
            if (eventArgs.PropertyName == PropertyHelper.GetName<INotificationDuringScanningViewModel>(v => v.ErrorIsResolved))
            {
                OnPropertyChanged(PropertyHelper.GetName<ScanInProgressViewModel>(v => v.ThereAreNoErrorsOrNotifications));
            }
        }

        public bool ThereAreNoErrorsOrNotifications
        {
            get
            {
                return NotificationViewModel == null || NotificationViewModel.ErrorIsResolved;
            }
        }

        public ICommand CancelScanningCommand
        {
            get { return cancelScanningCommand; }
        }

        public void AttachToModel(BoxScanningSession currentScanningSession)
        {
            if (boxScanningSession != null)
            {
                boxScanningSession.PropertyChanged -= BoxScanningSessionOnPropertyChanged;
            }

            boxScanningSession = currentScanningSession;

            if (boxScanningSession != null)
            {
                boxScanningSession.PropertyChanged += BoxScanningSessionOnPropertyChanged;
            }

            NotificationViewModel = null;

            OnPropertyChanged(null);
        }
    }
}
