using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Windows.Input;
using System.Windows.Media.Imaging;
using Prism.Commands;
using Valg.Eva.Skanning.Scan.Presentation.ReadSoftModel;
using Valg.EVA.Skanning.Contracts.CommonEnums;
using Valg.EVA.Skanning.Diagnostics;
using Valg.EVA.Skanning.Scan.ReadSoftContracts.Events;
using Valg.EVA.Skanning.Scan.ReadSoftContracts.Model;
using Valg.EVA.Skanning.UserInterfaceElements;
using Valg.EVA.Skanning.UserInterfaceElements.BallotPaperImage;

namespace Valg.EVA.Skanning.Scan.Presentation.ScannerQuality
{
    public class ScannerQualityViewModel : ViewModelBase, IScannerQualityViewModel
    {
        private readonly IScannedBallotPaperMapper scannedBallotPaperMapper;
        private readonly IBallotPaperImageWindowProvider ballotPaperImageWindowProvider;
        private readonly string expectedBallotNumberData = "1234567890123456789012";
        private readonly string pdfFileToPrint = "Valg.EVA.Skanning.Scan.Presentation.ScannerQuality.ScannerQuality.pdf";
        private readonly string savedImageFilesLocation = Path.GetTempPath();
        private bool stampIsOk;
        private bool renumberFieldsAreOk;
        private bool ballotNumberFieldIsOk;
        private bool scannerPassedAllQualityTests;
        private string ballotNumberFieldValue;
        private bool scanInProgress;
        private IEnumerable<Renumber> renumbersFirstGroup;
        private IEnumerable<Renumber> renumbersSecondGroup;
        private IEnumerable<Renumber> renumbersThirdGroup;
        private IEnumerable<Renumber> renumbersFourthGroup;
        private IEnumerable<Renumber> renumbersFifthGroup;
        private bool isDataAvailable;
        private bool ballotTypeWasRecognized;
        private bool errorOccured;
        private bool scanningHasOccured;
        private string errorMessage;
        private ScannerQualityBallotPaperImageViewModel ballotPaperImageViewModel;

        public ScannerQualityViewModel(IScannedBallotPaperMapper scannedBallotPaperMapper,
            IBallotPaperImageWindowProvider ballotPaperImageWindowProvider)
        {
            this.scannedBallotPaperMapper = scannedBallotPaperMapper;
            this.ballotPaperImageWindowProvider = ballotPaperImageWindowProvider;
            StartScanningCommand = new DelegateCommand(Start);
            ShowPdfToPrintCommand = new DelegateCommand(ShowPdfDocumentToPrint);
            ShowBallotPaperImageCommand = new DelegateCommand(ShowBallotPaperImage);
        }

        public void HandleScanReportedUnspecificReadsoftErrorCode(ScanReportedUnspecificReadsoftErrorCodeEventArgs obj)
        {
            ShowError(Resources.Language.ScanLanguageString.ScannerQualityCheck_ErrorOccuredText);
        }

        public void HandleScanReportedPaperJam(ScanReportedPaperJamEventArgs obj)
        {
            ShowError(Resources.Language.ScanLanguageString.PleaseVerifyThatThePaperFeederMechanismIsWorking);
        }

        public void HandleScanReportedNoMorePaperInFeeder(ScanReportedNoMorePaperInFeederEventArgs obj)
        {
            if (!scanningHasOccured)
            {
                ShowError(Resources.Language.ScanLanguageString.NoPaper);
            }
        }

        private void ShowBallotPaperImage()
        {
            if (ballotPaperImageViewModel != null)
            {
                ballotPaperImageWindowProvider.Show(ballotPaperImageViewModel);
            }
        }

        public ICommand StartScanningCommand { get; }

        public ICommand ShowPdfToPrintCommand { get; }

        public ICommand ShowBallotPaperImageCommand { get; }

        public bool StampIsOK
        {
            get
            {
                return stampIsOk;
            }
            private set
            {
                stampIsOk = value;
                OnPropertyChanged();
            }
        }

        public bool RenumberFieldsAreOK
        {
            get
            {
                return renumberFieldsAreOk;
            }
            private set
            {
                renumberFieldsAreOk = value;
                OnPropertyChanged();
            }
        }

        public bool BallotNumberFieldIsOK
        {
            get
            {
                return ballotNumberFieldIsOk;
            }
            private set
            {
                ballotNumberFieldIsOk = value;
                OnPropertyChanged();
            }
        }

        public string BallotNumberFieldValue
        {
            get
            {
                return ballotNumberFieldValue;
            }
            set
            {
                ballotNumberFieldValue = value;
                OnPropertyChanged();
            }
        }

        public bool ScannerPassedAllQualityTests
        {
            get
            {
                return scannerPassedAllQualityTests;
            }
            private set
            {
                scannerPassedAllQualityTests = value;
                OnPropertyChanged();
            }
        }

        public bool BallotTypeWasRecognized
        {
            get
            {
                return ballotTypeWasRecognized;
            }
            set
            {
                ballotTypeWasRecognized = value;
                OnPropertyChanged();
            }
        }

        public bool ScanInProgress
        {
            get
            {
                return scanInProgress;
            }
            set
            {
                scanInProgress = value;
                OnPropertyChanged();
            }
        }

        public bool ErrorOccured
        {
            get
            {
                return errorOccured;
            }
            set
            {
                errorOccured = value;
                OnPropertyChanged();
            }
        }

        public string ErrorMessage
        {
            get
            {
                return errorMessage;
            }
            set
            {
                errorMessage = value;
                OnPropertyChanged();
            }
        }

        public bool IsDataAvailable
        {
            get
            {
                return isDataAvailable;
            }
            set
            {
                isDataAvailable = value;
                OnPropertyChanged();
            }
        }

        public IEnumerable<Renumber> RenumbersFirstGroup
        {
            get
            {
                return renumbersFirstGroup;
            }
            set
            {
                renumbersFirstGroup = value;
                OnPropertyChanged();
            }
        }

        public IEnumerable<Renumber> RenumbersSecondGroup
        {
            get
            {
                return renumbersSecondGroup;
            }
            set
            {
                renumbersSecondGroup = value;
                OnPropertyChanged();
            }
        }

        public IEnumerable<Renumber> RenumbersThirdGroup
        {
            get
            {
                return renumbersThirdGroup;
            }
            set
            {
                renumbersThirdGroup = value;
                OnPropertyChanged();
            }
        }

        public IEnumerable<Renumber> RenumbersFourthGroup
        {
            get
            {
                return renumbersFourthGroup;
            }
            set
            {
                renumbersFourthGroup = value;
                OnPropertyChanged();
            }
        }

        public IEnumerable<Renumber> RenumbersFifthGroup
        {
            get
            {
                return renumbersFifthGroup;
            }
            set
            {
                renumbersFifthGroup = value;
                OnPropertyChanged();
            }
        }

        private void ShowError(string errorMsg)
        {
            ErrorMessage = errorMsg;
            ErrorOccured = true;
            ScanInProgress = false;
        }

        private void Start()
        {
            ResetAllResultProperties();
            ScanInProgress = true;
            PublishEvent<ScanJobRequestedStartedEvent, ScanJobRequestedStartedEventArgs>(new ScanJobRequestedStartedEventArgs());
        }

        private void ShowPdfDocumentToPrint()
        {
            var tempFile = @"c:\temp\ScannerQuality.pdf";
            ExtractResource(pdfFileToPrint, tempFile);
            Process.Start(tempFile);
        }

        private void ExtractResource(string resource, string path)
        {
            var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resource);
            if (stream != null)
            {
                byte[] bytes = new byte[(int)stream.Length];
                stream.Read(bytes, 0, bytes.Length);
                File.WriteAllBytes(path, bytes);
            }
        }

        public void RunQualityCheck(ScanFormFinishedEventArgs scannedForm)
        {
            scanningHasOccured = true;

            var processedForm = ProcessForm(scannedForm.Form);
            if (processedForm.BallotPaperType == BallotPaperType.QualityCheck)
            {
                BallotTypeWasRecognized = true;
            }
            if (StampCheck(processedForm))
            {
                StampIsOK = true;
            }

            var renumbers = GetRenumberFields(processedForm);

            RenumbersFirstGroup = renumbers.Take(5);
            RenumbersSecondGroup = renumbers.Skip(5).Take(5);
            RenumbersThirdGroup = renumbers.Skip(10).Take(5);
            RenumbersFourthGroup = renumbers.Skip(15).Take(5);
            RenumbersFifthGroup = renumbers.Skip(20).Take(5);

            if (renumbers.All(r => !r.NeedsVerification) && renumbers.Count() == 25)
            {
                RenumberFieldsAreOK = true;
            }

            GetBallotNumberField(processedForm);

            ScannerPassedAllQualityTests = StampIsOK && BallotNumberFieldIsOK && RenumberFieldsAreOK;
            ScanInProgress = false;
            IsDataAvailable = true;

            LogTheResult(renumbers);
        }

        private void SaveImageFiles(ScannedBallotPaper scannedballotPaper)
        {
            if (scannedballotPaper != null)
            {
                var created = DateTime.Now.ToString("yyyy-MM-dd-HH-mm-ss");
                var frontImageFileLocation = $"{savedImageFilesLocation}scanner-quality-front-{created}.TIF";
                var backImageFileLocation = $"{savedImageFilesLocation}scanner-quality-back-{created}.TIF";
                try
                {
                    File.WriteAllBytes(frontImageFileLocation, scannedballotPaper.ScannedImages.Front);
                    File.WriteAllBytes(backImageFileLocation, scannedballotPaper.ScannedImages.Back);
                    EvaSkanningEventSource.Log.ScannerQualityCheckImagesSaved(frontImageFileLocation, backImageFileLocation);
                }
                catch (Exception e)
                {
                    EvaSkanningEventSource.Log.ErrorSavingScannerQualityCheckImages(frontImageFileLocation,backImageFileLocation, e.Message);
                }
            }
        }

        private void LogTheResult(IEnumerable<Renumber> renumbers)
        {
            var result = new StringBuilder();
            result.Append($"Ballot type was recognized: {BallotTypeWasRecognized}");
            result.Append($", Ballot number was recognized: {BallotNumberFieldIsOK}, value:{BallotNumberFieldValue}");
            result.Append($", Stamp was recognized: {StampIsOK}");
            result.Append($", All RenumberFields was recognized: {RenumberFieldsAreOK}, values:");
            foreach (var renumber in renumbers)
            {
                result.Append($"[{renumber.Index}]=({renumber.Value}), ");
            }
            result.Append($", Scanner passed all quality tests: {ScannerPassedAllQualityTests}");

            EvaSkanningEventSource.Log.ScannerQualityCheckCompleted(result.ToString());
        }

        private void ResetAllResultProperties()
        {
            BallotTypeWasRecognized = false;
            StampIsOK = false;
            RenumberFieldsAreOK = false;
            BallotNumberFieldIsOK = false;
            ScannerPassedAllQualityTests = false;
            ErrorOccured = false;
            IsDataAvailable = false;
            scanningHasOccured = false;
        }

        private void GetBallotNumberField(ScannedBallotPaper ballotPaper)
        {
            if (ballotPaper.ScannedFields.Any(s => s.FieldType == FieldType.BallotNo))
            {
                var ballotNumber = ballotPaper.ScannedFields.First(b => b.FieldType == FieldType.BallotNo);
                BallotNumberFieldValue = ballotNumber.Value;
                if (ballotNumber.Value == expectedBallotNumberData)
                {
                    BallotNumberFieldIsOK = true;
                }
            }
        }

        private List<Renumber> GetRenumberFields(ScannedBallotPaper ballotPaper)
        {
            var renumbersInBallotPaper = new List<Renumber>();
            var renumberFields = ballotPaper.ScannedFields.Where(f => f.FieldType == FieldType.Ca_renumber);
            foreach (var renumber in renumberFields)
            {
                renumbersInBallotPaper.Add(new Renumber(renumber.Value, renumber.NeedsVerification, renumber.FieldIndex));
            }
            return renumbersInBallotPaper;
        }

        private ScannedBallotPaper ProcessForm(ScannedForm form)
        {
            var scannableBallotPaperType = ScannableBallotPaperType.GetFromFormDefinition(form.Name);
            var mappedBallotPaper = scannedBallotPaperMapper.MapScannedBallotPaper(form, scannableBallotPaperType);

            ballotPaperImageViewModel = new ScannerQualityBallotPaperImageViewModel(
                GetBitmapFromTif(mappedBallotPaper.ScannedImages.Front),
                GetBitmapFromTif(mappedBallotPaper.ScannedImages.Back));

            SaveImageFiles(mappedBallotPaper);

            return mappedBallotPaper;
        }

        private BitmapFrame GetBitmapFromTif(byte[] imageBytes)
        {
            var tifStream = new MemoryStream(imageBytes);
            var decoder = new TiffBitmapDecoder(tifStream, BitmapCreateOptions.None, BitmapCacheOption.Default);
            var bitmapFrame = decoder.Frames[0];
            return bitmapFrame;
        }

        private bool StampCheck(ScannedBallotPaper ballotPaper)
        {
            if (ballotPaper.ScannedFields.All(s => s.FieldType != FieldType.Stamp))
            {
                return false;
            }
            var stampField = ballotPaper.ScannedFields.First(b => b.FieldType == FieldType.Stamp);
            return !stampField.NeedsVerification;
        }
    }
}
