using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Input;

using Prism.Commands;
using Unity.Interception.Utilities;
using Valg.EVA.Skanning.UserInterfaceElements;
using Valg.EVA.Skanning.Verify.Startup.VerifyBallotPaper.Messaging.Commands;
using Valg.EVA.Skanning.Verify.Startup.VerifyBallotPaper.Messaging.Events;
using Valg.EVA.Skanning.Verify.Startup.VerifyBallotPaper.RenumberStrikeout;
using Valg.EVA.Skanning.Verify.Startup.VerifyBallotPaper.WriteModel;

namespace Valg.EVA.Skanning.Verify.Startup.VerifyBallotPaper.VerifiableViewModelsCollection
{
    public class VerifiableViewModelsCollectionVisitor : ViewModelBase, IVerifiableViewModelsCollectionVisitor
    {
        private bool focusMoveIsSuspended;

        private readonly IWriteModelHub writeModelHub;

        private readonly DelegateCommand resumeVisitationOfVerifiableViewModelsCommand;

        private IViewModelWithVerifiableContent latestViewModelToReceiveFocus;

        private readonly List<IViewModelWithVerifiableContent> viewModelsWithVerifiableContent;

        public VerifiableViewModelsCollectionVisitor(IEndOfVisitationCycleViewModel endOfCycle,
            IWriteModelHub writeModelHub)
        {
            this.writeModelHub = writeModelHub;
            EndOfCycle = endOfCycle;
            viewModelsWithVerifiableContent = new List<IViewModelWithVerifiableContent>();
            resumeVisitationOfVerifiableViewModelsCommand = new DelegateCommand(() => GotoNextVerifiableViewModel(false));
        }

        protected override void SubscribeToViewModelSpecificEvents()
        {
            SubscribeToEvent<VerificationResolvedOnVerifiableViewModelEvent, VerificationWasResolvedEventArgs>(VerificationWasResolvedHandler);
        }

        public IEndOfVisitationCycleViewModel EndOfCycle { get; }

        public ICommand ResumeVisitationOfVerifiableViewModelsCommand => resumeVisitationOfVerifiableViewModelsCommand;

        public bool NoMoreViewModelsToVerifyOrRejected => NumberOfViewModelsToVerify == NumberVerifiedViewModels || !IsEnabled;

        private bool IsEnabled => !writeModelHub.IsRejected;

        private int NumberOfViewModelsToVerify => viewModelsWithVerifiableContent.Count;

        private int NumberVerifiedViewModels
        {
            get { return viewModelsWithVerifiableContent.Count(vf => !vf.NeedsVerification); }
        }

        private IViewModelWithVerifiableContent[] ViewModelsWithVerifiableContentsThatNeedsVerification
        {
            get { return viewModelsWithVerifiableContent.Where(f => f.NeedsVerification).ToArray(); }
        }

        public void GotoNextVerifiableViewModel(bool verifyActionWasCommited)
        {
            if (IsEnabled)
            {
                IViewModelWithVerifiableContent nextViewModel = FindNextViewModel(verifyActionWasCommited);

                if (nextViewModel != null)
                {
                    PublishEvent<FocusRequestedByVerifiableViewModelVisitationEvent, FocusRequestedByVerifiableViewModelVisitationEventArgs>(new FocusRequestedByVerifiableViewModelVisitationEventArgs
                    {
                        ViewModelThatRequestedFocus = nextViewModel
                    });

                    if (!focusMoveIsSuspended)
                    {
                        nextViewModel.BringToFocus();
                    }
                }
                else
                {
                    GotoEndOfVisitationCycle();
                }
            }
            else
            {
                GotoEndOfVisitationCycle();
            }
        }

        public void RemoveViewModelsWithVerifiableContent(IEnumerable<IViewModelWithVerifiableContent> viewModelsToRemove)
        {
            viewModelsToRemove.ForEach(f => viewModelsWithVerifiableContent.Remove(f));
            if (viewModelsToRemove.Contains(latestViewModelToReceiveFocus))
            {
                latestViewModelToReceiveFocus = null;
            }

            OnPropertyChanged(null);
        }

        public bool HasViewModelsWithUnverifiedContent => ViewModelsWithVerifiableContentsThatNeedsVerification.Any();


        public RenumberStrikeoutViewModel[] GetTargetedViewModelsForAutoVerify()
        {
            return viewModelsWithVerifiableContent.Where(v => v is RenumberStrikeoutViewModel && v.NeedsVerification).Cast<RenumberStrikeoutViewModel>().ToArray();
        }

        public void SetViewModelsWithVerifiableContent(IEnumerable<IViewModelWithVerifiableContent> viewModels)
        {
            viewModelsWithVerifiableContent.Clear();

            if (viewModels != null)
            {
                viewModelsWithVerifiableContent.AddRange(viewModels);
                viewModelsWithVerifiableContent.Sort(new VerificationOrder());
                if (!viewModelsWithVerifiableContent.Contains(latestViewModelToReceiveFocus))
                {
                    latestViewModelToReceiveFocus = GetNextViewModelToVerify(null);
                }
            }

            OnPropertyChanged(null);
        }

        private void VerificationWasResolvedHandler(VerificationWasResolvedEventArgs args)
        {
            if (!args.ForceGotoNextViewModel && !args.NeededVerification && !writeModelHub.IsRejected)
            {
                RemainFocusAtViewModelRecentlyCommitted(args.ViewModel);
            }
            else
            {
                GotoNextVerifiableViewModel(true);
            }
            OnPropertyChanged(null);
        }

        private IViewModelWithVerifiableContent FindNextViewModel(bool verificationWasResolvedInViewModel)
        {
            IViewModelWithVerifiableContent nextViewModel;
            if ((!verificationWasResolvedInViewModel && latestViewModelToReceiveFocus != null) || latestViewModelToReceiveFocus != null && latestViewModelToReceiveFocus.NeedsVerification)
            {
                nextViewModel = latestViewModelToReceiveFocus;
            }
            else
            {
                nextViewModel = GetNextViewModelToVerify(latestViewModelToReceiveFocus);
                latestViewModelToReceiveFocus = nextViewModel;
            }
            return nextViewModel;
        }

        private IViewModelWithVerifiableContent GetNextViewModelToVerify(IViewModelWithVerifiableContent currentViewModel)
        {
            if (!ViewModelsWithVerifiableContentsThatNeedsVerification.Any())
            {
                return null;
            }

            IViewModelWithVerifiableContent nextViewModel;

            if (currentViewModel == null)
            {
                nextViewModel = ViewModelsWithVerifiableContentsThatNeedsVerification.FirstOrDefault();
            }
            else
            {
                var indexOfCurrentViewModel = Array.IndexOf(ViewModelsWithVerifiableContentsThatNeedsVerification, currentViewModel);
                var indexOfLastElement = ViewModelsWithVerifiableContentsThatNeedsVerification.Length - 1;

                if (indexOfLastElement > indexOfCurrentViewModel)
                {
                    nextViewModel = ViewModelsWithVerifiableContentsThatNeedsVerification[indexOfCurrentViewModel + 1];
                }
                else
                {
                    nextViewModel = ViewModelsWithVerifiableContentsThatNeedsVerification.FirstOrDefault();
                }
            }

            return nextViewModel;
        }

        private void GotoEndOfVisitationCycle()
        {
            OnPropertyChanged(null);
            PublishEvent<BallotVerificationCompletedEvent, BallotVerificationCompletedEventArgs>(new BallotVerificationCompletedEventArgs());
            EndOfCycle.BringToFocus();
        }

        private void RemainFocusAtViewModelRecentlyCommitted(IViewModelWithVerifiableContent viewModelToRemainAt)
        {
            viewModelToRemainAt.RemainFocusAtThisViewModel();
        }

        public void SuspendFocusMove()
        {
            focusMoveIsSuspended = true;
        }

        public void ResumeFocusMove()
        {
            focusMoveIsSuspended = false;
        }
    }
}
