using System;
using System.Collections.Specialized;
using Valg.EVA.Skanning.Contracts.CommonEnums;
using Valg.EVA.Skanning.ServicesModel.ServiceContracts.Count.Read;
using Valg.EVA.Skanning.ServicesModel.ServiceContracts.Verification.Read;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;

using Unity.Interception.Utilities;
using Valg.EVA.Skanning.ContainerExtensions;
using Valg.EVA.Skanning.UserInterfaceElements;
using Valg.EVA.Skanning.Verify.Startup.Preview;
using Valg.EVA.Skanning.Verify.Startup.VerifyBallotPaper.BallotNumber;
using Valg.EVA.Skanning.Verify.Startup.VerifyBallotPaper.BallotPaperStatus;
using Valg.EVA.Skanning.Verify.Startup.VerifyBallotPaper.BallotPaperXOfY;
using Valg.EVA.Skanning.Verify.Startup.VerifyBallotPaper.Candidate;
using Valg.EVA.Skanning.Verify.Startup.VerifyBallotPaper.Header;
using Valg.EVA.Skanning.Verify.Startup.VerifyBallotPaper.VerifiableViewModelsCollection;
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.PageXOfY;
using Valg.EVA.Skanning.Verify.Startup.VerifyBallotPaper.RenumberStrikeout;
using Valg.EVA.Skanning.Verify.Startup.VerifyBallotPaper.Stamp;
using Valg.EVA.Skanning.Verify.Startup.VerifyBallotPaper.WriteIns;
using Valg.EVA.Skanning.Verify.Startup.VerifyBallotPaper.WriteModel;

namespace Valg.EVA.Skanning.Verify.Startup.VerifyBallotPaper
{
    public class VerifyBallotPaperViewModel : ViewModelBase, IVerifyBallotPaperViewModel
    {
        private readonly IVerifyBallotPaperSubPagesViewModelsBuilder verifyBallotPaperSubPagesViewModelsBuilder;
        private readonly ITypedFactory typedFactory;
        private readonly AutoVerifyHandler autoVerifyHandler;
        private readonly BallotPageCollection ballotPages = new BallotPageCollection();
        private readonly ObservableCollection<IPagedVerificationViewModel> pagesViewModels;
        
        private readonly Type[] ballotNumberDependentViewModels = { typeof(CandidatePageViewModel), typeof(WriteInPageViewModel), typeof(RenumberStrikeoutPageViewModel) };
        private readonly Type[] stampRejectionReasonDepdendentViewModels = { typeof (BallotNumberViewModel)};
        private readonly IAutoVerifyInfoViewModel autoVerifyInfoViewModel;

        public VerifyBallotPaperViewModel(IWriteModelHub writeModelHub, IPageXOfYViewModel xofYViewModel,
            IStampVerificationPageViewModel stampVerificationPageViewModel, 
            IPreviewViewModel preview, IBallotNumberViewModel ballotNumberViewModel, 
            IAffiliationHeaderViewModel affiliationHeaderViewModel, 
            IVerifiableViewModelsCollectionVisitor verifiableViewModelsCollectionVisitor, 
            IVerifyBallotPaperSubPagesViewModelsBuilder verifyBallotPaperSubPagesViewModelsBuilder,
            ITypedFactory typedFactory, IAutoVerifyInfoViewModel autoVerifyInfoViewModel,
            AutoVerifyHandler autoVerifyHandler)
        {
            this.verifyBallotPaperSubPagesViewModelsBuilder = verifyBallotPaperSubPagesViewModelsBuilder;
            this.typedFactory = typedFactory;
            this.autoVerifyHandler = autoVerifyHandler;
            this.autoVerifyInfoViewModel = autoVerifyInfoViewModel;
            WriteModelHub = writeModelHub;
            PageXOfYViewModel = xofYViewModel;
            StampVerificationPageViewModel = stampVerificationPageViewModel;
            BallotNumberViewModel = ballotNumberViewModel;
            AffiliationHeaderViewModel = affiliationHeaderViewModel;
            VerifiableViewModelsCollectionVisitor = verifiableViewModelsCollectionVisitor;
            Preview = preview;            

            pagesViewModels = new ObservableCollection<IPagedVerificationViewModel>();
            pagesViewModels.CollectionChanged += PagesViewModelCollectionChanged;
        }

        protected override void SubscribeToViewModelSpecificEvents()
        {
            SubscribeToEvent<PageChangedEvent, PageChangedEventArgs>((args) =>
            {
                foreach (var candidateViewModel in AllCandidates.Where(p => p.IsContextMenuOpen))
                {
                    candidateViewModel.IsContextMenuOpen = false;
                }
            });

            SubscribeToEvent<BallotNumberUpdatedEvent, BallotNumberUpdatedEventArgs>(args =>
            {
                if (args.BallotNumberUpdate.IgnoreCandidates)
                {
                    RemoveBallotNumberDependentBallotPages();
                }
                UpdateViewModel(args.BallotNumberUpdate);
                var autoVerifyHasBeenPerformed = autoVerifyHandler.AutoVerifyFieldsIfApplicable(args.BallotNumberUpdate, VerifiableViewModelsCollectionVisitor);
                if (autoVerifyHasBeenPerformed)
                {
                    autoVerifyInfoViewModel.ShowTipIfNotHiddenByUser();
                    this.VerifiableViewModelsCollectionVisitor.GotoNextVerifiableViewModel(false);
                }

            });

            SubscribeToEvent<RejectionReasonChangedEvent, RejectionReasonChangedEventArgs>(args =>
            {
                if (RejectionReasonsCollections.BallotNumberRejectionReasons.Contains(args.CurrentRejectionReason))
                {
                    RemoveBallotNumberDependentBallotPages();
                }
                else if (args.CurrentRejectionReason == RejectionReasonType.NoOfficialStamp)
                {
                    RemovePagesByType(stampRejectionReasonDepdendentViewModels.Union(ballotNumberDependentViewModels).ToArray());
                }

                UpdateViewModel(null);
            });

            SubscribeToEvent<RenumberSelectedEvent, RenumberSelectedEventArgs>(args =>
            {
                foreach (var renumberStrikeoutViewModel in RenumberStrikeouts)
                {
                    var possibleRenumberValues = GetPossibleRenumberValues();
                    possibleRenumberValues.Remove(renumberStrikeoutViewModel.CandidateRank);
                    renumberStrikeoutViewModel.SetPossibleRenumberValues(possibleRenumberValues);
                }
            });

        }

        private void PagesViewModelCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            PublishEvent<PagesCollectionChangedEvent, PagesCollectionChangedEventArgs>(new PagesCollectionChangedEventArgs(pagesViewModels.Count));
        }

        public ObservableCollection<CandidateViewModel> AllCandidates { get; } = new ObservableCollection<CandidateViewModel>();

        public ObservableCollection<RenumberStrikeoutViewModel> RenumberStrikeouts { get; } = new ObservableCollection<RenumberStrikeoutViewModel>();

        public IVerifiableViewModelsCollectionVisitor VerifiableViewModelsCollectionVisitor { get; }

        public IPageXOfYViewModel PageXOfYViewModel { get; }

        public IBallotPaperStatusViewModel BallotPaperStatusViewModel { get; private set;}

        public IBallotPaperXOfYViewModel BallotPaperXOfYViewModel { get; private set; }

        public IAffiliationHeaderViewModel AffiliationHeaderViewModel { get; }

        public BallotPaper BallotPaper { get; set; }

        public ObservableCollection<IPagedVerificationViewModel> PagesViewModels => pagesViewModels;

        public IPreviewViewModel Preview { get; }

        public IStampVerificationPageViewModel StampVerificationPageViewModel { get; }

        public IBallotNumberViewModel BallotNumberViewModel { get; }


        public Counting Counting { get; set; }

        public BallotPageCollection BallotPages => ballotPages;

        public bool HasBallotCompleteTipBeenShown { get; set; }

        public IWriteModelHub WriteModelHub { get; }

        public void AddBallotPage(IPagedVerificationViewModel viewModel, PageXOfYNavigationTooltip tooltip, PreviewBallotPaperImageViewModel previewViewModel)
        {
            ballotPages.Add(viewModel, tooltip, previewViewModel);
            PagesViewModels.Add(viewModel);
            Preview.AddPreviewImageViewModel(previewViewModel);
            PageXOfYViewModel.AddPageNavigationTooltip(tooltip);
        }

        private void BallotPagesAreBuilt(bool shouldGoToBallotNumberPage)
        {
            var ballotNumberPage = PagesViewModels.SingleOrDefault(p => p is IBallotNumberViewModel);
            if (ballotNumberPage != null && shouldGoToBallotNumberPage)
            {
                PageXOfYViewModel.CurrentPage = ballotNumberPage.PageIndex + 1;
            }
            else
            {
                PageXOfYViewModel.SetFirstOrCurrentPage();
            }

            VerifiableViewModelsCollectionVisitor.SetViewModelsWithVerifiableContent(BallotPages.GetVerificationFields());
        }

        private void ResetModelHubs(BallotPaper ballotPaper, Counting counting)
        {
            BallotPaper = ballotPaper;
            Counting = counting;
            WriteModelHub.Clear();
            StampVerificationPageViewModel.SetModel(ballotPaper.Stamp);
        }

        public void UpdateViewModel(BallotNumberUpdate ballotNumberUpdate)
        {
            verifyBallotPaperSubPagesViewModelsBuilder.UpdateSubPagesOnViewModel(this, ballotNumberUpdate);
            BallotPagesAreBuilt(shouldGoToBallotNumberPage: false);
        }

        private void CreatePagesAndPreviewViewModels(int totalToVerification)
        {
            verifyBallotPaperSubPagesViewModelsBuilder.AddSubPagesToViewModel(this);
            BallotPagesAreBuilt(ShouldGoToDefaultPage(BallotPaper));
            CreateBallotPaperXOfYViewModel(totalToVerification);
            CreateBallotPaperStatusViewModel();
        }

        public void Initialize(BallotPaper ballotPaperToVerify, Counting counting, int totalToVerification)
        {
            ResetModelHubs(ballotPaperToVerify, counting);
            CreatePagesAndPreviewViewModels(totalToVerification);
            SetRejectionReasonInitiallyWithNoModificationTracking();
        }

        private bool ShouldGoToDefaultPage(BallotPaper ballotPaperToVerify)
        {
            return ballotPaperToVerify.BallotNo.IsBlank || ballotPaperToVerify.IsValid;
        }

        private void CreateBallotPaperXOfYViewModel(int totalToVerification)
        { 
            BallotPaperXOfYViewModel = typedFactory.Create<IBallotPaperXOfYViewModel>();
            BallotPaperXOfYViewModel.Total = totalToVerification;
            BallotPaperXOfYViewModel.Current = BallotPaper.VerificationIndex.GetValueOrDefault();
        }

        private void CreateBallotPaperStatusViewModel()
        {
            var status = BallotPaper.RejectionReason != RejectionReasonType.NotRejected
                ? BallotStatus.SuggestedRejected
                : BallotPaper.IsValid ? BallotStatus.Verified : BallotStatus.Started;

            BallotPaperStatusViewModel = typedFactory.Create<IBallotPaperStatusViewModel>();
            BallotPaperStatusViewModel.Status = status;
        }

        private void SetRejectionReasonInitiallyWithNoModificationTracking()
        {
            WriteModelHub.SetRejectionReason(BallotPaper.RejectionReason,
               GetPageIndexForRejection(BallotPaper.RejectionReason), isModification: false);
        }

        private int? GetPageIndexForRejection(RejectionReasonType rejectionReason)
        {
            if (rejectionReason == RejectionReasonType.NotRejected)
            {
                return null;
            }

            if (rejectionReason == RejectionReasonType.NoOfficialStamp)
            {
                return PagesViewModels.Single(p => p is IStampVerificationPageViewModel).PageIndex;
            }

            return PagesViewModels.Single(p => p is IBallotNumberViewModel).PageIndex;
        }

        public void ClearRenumberStrikeouts()
        {
            RenumberStrikeouts.ForEach(vm => vm.Dispose());
            RenumberStrikeouts.Clear();
        }

        public void ClearCandidates()
        {
            AllCandidates.ForEach(vm => vm.Dispose());
            AllCandidates.Clear();
        }

        public void AddCandidateViewModel(CandidateViewModel candidateViewModel)
        {
            AllCandidates.Add(candidateViewModel);
        }

        public void AddRenumberStrikeoutViewModel(RenumberStrikeoutViewModel renumberStrikeoutViewModel)
        {
            RenumberStrikeouts.Add(renumberStrikeoutViewModel);
        }

        public void RemoveBallotNumberDependentBallotPages()
        {
            RemovePagesByType(ballotNumberDependentViewModels);
        }

        private void RemovePagesByType(Type[] types)
        {
            var removedPages = new List<BallotPage>();
            foreach (var t in types)
            {
                removedPages.AddRange(BallotPages.RemoveType(t));                
            }

            removedPages.ForEach(RemoveBallotPages);
            VerifiableViewModelsCollectionVisitor.RemoveViewModelsWithVerifiableContent(BallotPageCollection.GetVerificationFields(removedPages));
        }

        private void RemoveBallotPages(BallotPage bp)
        {
            BallotPages.Remove(bp);
            PagesViewModels.Remove(bp.ViewModel);
            Preview.ImageViewModels.Remove(bp.Preview);
            PageXOfYViewModel.PageXOfYNavigationTooltips.Remove(bp.Tooltip);
        }

        private List<int> GetPossibleRenumberValues()
        {
            var possibleRenumberValues = Enumerable.Range(1, RenumberStrikeouts.Count).ToList();

            foreach (var renumberStrikeoutViewModel in RenumberStrikeouts)
            {
                if (renumberStrikeoutViewModel.Renumber.HasValue)
                {
                    possibleRenumberValues.Remove(renumberStrikeoutViewModel.Renumber.Value);
                }
            }

            return possibleRenumberValues;
        }

 
    }
}
