using System.Collections.Generic;
using System.IO.Abstractions;
using System.Linq;
using Valg.Eva.Skanning.Scan.Presentation.ReadSoftModel.FieldValidators;
using Valg.EVA.Skanning.Scan.ReadSoftContracts.Model;

namespace Valg.Eva.Skanning.Scan.Presentation.ReadSoftModel
{
    public class ScannedBallotPaperMapper : IScannedBallotPaperMapper
    {
        private readonly IFileSystem fileSystem;
        private readonly Dictionary<ScannableFieldType, IFieldValidator> allFieldValidators;

        public ScannedBallotPaperMapper(IFileSystem fileSystem, IFieldValidator[] fieldValidators)
        {
            this.fileSystem = fileSystem;

            allFieldValidators = fieldValidators.ToDictionary(k => k.FieldType);
        }

        public ScannedBallotPaper MapScannedBallotPaper(ScannedForm form, ScannableBallotPaperType scannableBallotPaperType)
        {
            var paper = new ScannedBallotPaper
            {
                BallotPaperType = scannableBallotPaperType.BallotPaperType,
                ScannedImages = new ScannedImages
                {
                    Front = form.ArchiveFrontImageBytes,
                    Back = form.ArchiveBackImageBytes,
                    FrontRotationDegrees = form.FrontRotatedDegrees,
                    BackRotationDegrees = form.BackRotatedDegrees,
                },
                ScannedFields = MapScannedFields(form.FormFields, scannableBallotPaperType),
                ScannedOrder = form.ScannedOrder
            };

            return paper;
        }

        private List<IFieldValidator> ResolveFieldValidators(ScannableBallotPaperType scannableBallotPaperType)
        {
            return scannableBallotPaperType.FieldTypesForThisBallotPaperType.Select(scannableFieldType => allFieldValidators[scannableFieldType]).ToList();
        }

        private ScannedField[] MapScannedFields(Dictionary<ScannableFieldType, List<ScannedField>> formFieldsDictionary, ScannableBallotPaperType scannableBallotPaperType)
        {
            var fieldValidators = ResolveFieldValidators(scannableBallotPaperType);

            var mappedFields = new List<ScannedField>();

            var dictEnum = formFieldsDictionary.GetEnumerator();
            while (dictEnum.MoveNext())
            {
                var scannableFieldType = dictEnum.Current.Key;
                var formFields = dictEnum.Current.Value;

                if (formFields.Count == 0 && scannableFieldType.IsRequired)
                {
                    var scannedField = new ScannedField
                    {
                        FieldType = scannableFieldType.FieldType,
                        NeedsVerification = true,
                        Value = scannableFieldType.DefaultNullValue
                    };

                    if (scannableFieldType.AlwaysRequireValidation)
                    {
                        ValidateField(fieldValidators, scannableFieldType, scannedField, mappedFields);
                    }

                    mappedFields.Add(scannedField);
                }
                else
                {
                    if (scannableFieldType.TreatAllIndexedFieldsAsOne)
                    {
                        var formfield = scannableFieldType.PickIndexedFieldToUse(formFields);
                        ValidateField(fieldValidators, scannableFieldType, formfield, mappedFields);
                        mappedFields.Add(formfield);
                    }
                    else
                    {
                        foreach (var formField in formFields)
                        {
                            ValidateField(fieldValidators, scannableFieldType, formField, mappedFields);
                            mappedFields.Add(formField);
                        }
                    }
                }
            }

            return mappedFields.ToArray();
        }

        private static void ValidateField(List<IFieldValidator> fieldValidators, ScannableFieldType scannableFieldType, ScannedField formField, List<ScannedField> fields)
        {
            var fieldValidator = fieldValidators.SingleOrDefault(f => f.FieldType == scannableFieldType);
            if (fieldValidator != null)
                formField.NeedsVerification = !fieldValidator.IsFieldValid(formField, fields);
        }
    }
}

