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

namespace Valg.EVA.Skanning.ServicesModel.Internal.Verification
{
    public static class RenumberStrikeoutMapper
    {
        private static RenumberStrikeoutReadOnly MapCandidateWithBothRenumberAndStrikeout(BallotPaperFieldValueAggregate renumber, BallotPaperFieldValueAggregate strikeout, int strikeoutFillPercentageMinimum, int strikeoutFillPercentageMaximum)
        {
            var isCreatedFirstTimeByVerifier =
                (renumber.OriginalValue == null && (renumber.CurrentValue.ValueSourceType == ValueSourceType.Verify)) ||
                (strikeout.OriginalValue == null && (strikeout.CurrentValue.ValueSourceType == ValueSourceType.Verify));

            var didOriginalValueNeedVerification = renumber.DidOriginalValueNeedVerification() || strikeout.DidOriginalValueNeedVerification();

            var renumberStrikeout = new RenumberStrikeoutReadOnly(renumber.CurrentValue.FieldIndex.GetValueOrDefault(),
                didOriginalValueNeedVerification,
                renumber.CurrentValue.NeedsVerification || strikeout.CurrentValue.NeedsVerification,
                RenumberAsInterpretedByScanner(renumber),
                RenumberAsGivenByCorrection(renumber),
                StrikeoutAsInterpretedByScanner(strikeout, strikeoutFillPercentageMinimum, strikeoutFillPercentageMaximum),
                StrikeoutAsGivenByCorrection(strikeout),
                isCreatedFirstTimeByVerifier,
                ResolveIsIgnored(new[] { renumber, strikeout }));
            return renumberStrikeout;
        }

        private static bool ResolveIsIgnored(BallotPaperFieldValueAggregate[] fieldValuesToCheck)
        {
            return fieldValuesToCheck.All(f => f.CurrentValue.Value == ScannerFieldValueTemplateValues.FieldIsIgnored);
        }

        private static RenumberStrikeoutReadOnly MapSingleRenumberStrikeout(BallotPaperFieldValueAggregate renumberOrStrikeout, int strikeoutFillPercentageMinimum, int strikeoutFillPercentageMaximum)
        {
            var isCreatedFirstTimeByVerifier = (renumberOrStrikeout.OriginalValue == null && (renumberOrStrikeout.CurrentValue.ValueSourceType == ValueSourceType.Verify));

            int? fieldId = renumberOrStrikeout.CurrentValue.BallotPaperFieldId;
            var isRenumberField = renumberOrStrikeout.CurrentValue.IsRenumberField();

            return new RenumberStrikeoutReadOnly(renumberOrStrikeout.CurrentValue.FieldIndex.GetValueOrDefault(),
                renumberOrStrikeout.DidOriginalValueNeedVerification(),
                renumberOrStrikeout.CurrentValue.NeedsVerification,
                isRenumberField ? RenumberAsInterpretedByScanner(renumberOrStrikeout) : null,
                isRenumberField ? RenumberAsGivenByCorrection(renumberOrStrikeout) : null,
                !isRenumberField ? StrikeoutAsInterpretedByScanner(renumberOrStrikeout, strikeoutFillPercentageMinimum, strikeoutFillPercentageMaximum) : null,
                !isRenumberField ? StrikeoutAsGivenByCorrection(renumberOrStrikeout) : null,
                isCreatedFirstTimeByVerifier,
                false);
        }

        private static int? RenumberAsInterpretedByScanner(BallotPaperFieldValueAggregate renumber)
        {
            if (renumber.OriginalValue != null)
            {
                int result;
                var success = int.TryParse(renumber.OriginalValue.Value, out result);
                if (success)
                {
                    return result;
                }
            }

            return null;
        }

        private static int? RenumberAsGivenByCorrection(BallotPaperFieldValueAggregate renumber)
        {
            if (!renumber.CurrentValue.NeedsVerification
                && renumber.CurrentValue.ValueSourceType == ValueSourceType.Verify
                && !ResolveIsIgnored(new[] { renumber }))
            {
                int result;
                if (int.TryParse(renumber.CurrentValue.Value, out result))
                {
                    return result;
                }
            }

            return null;
        }

        private static bool? StrikeoutAsInterpretedByScanner(BallotPaperFieldValueAggregate strikeout, int strikeoutFillPercentageMinimum, int strikeoutFillPercentageMaximum)
        {
            if (strikeout.OriginalValue != null)
            {
                var originalValueInt = Convert.ToInt32(strikeout.OriginalValue.Value);
                return originalValueInt >= strikeoutFillPercentageMinimum && originalValueInt <= strikeoutFillPercentageMaximum;
            }

            return null;
        }

        private static bool? StrikeoutAsGivenByCorrection(BallotPaperFieldValueAggregate strikeout)
        {
            if (strikeout.CurrentValue.ValueSourceType == ValueSourceType.Verify
                && !ResolveIsIgnored(new[] { strikeout }))
            {
                return strikeout.CurrentValue.Value == ScannerFieldValueTemplateValues.StrikeoutIsGiven.ToString();
            }

            return null;
        }

        public static IEnumerable<RenumberStrikeoutReadOnly> MapRenumberStrikeout(List<BallotPaperFieldValueAggregate> fieldValueAggregates, int percentageMinimum, int percentageMaximum)
        {
            var groupedByFieldIndex = fieldValueAggregates.Where(f => f.CurrentValue.IsStrikeoutField() || f.CurrentValue.IsRenumberField())
                .GroupBy(f => f.CurrentValue.FieldIndex);

            var allToReturn = new List<RenumberStrikeoutReadOnly>();

            foreach (var group in groupedByFieldIndex)
            {
                if (group.Count() == 2)
                {
                    var renumberStrikeout = MapCandidateWithBothRenumberAndStrikeout(
                        group.Single(r => r.CurrentValue.IsRenumberField()),
                        group.Single(r => r.CurrentValue.IsStrikeoutField()),
                        percentageMinimum,
                        percentageMaximum);

                    allToReturn.Add(renumberStrikeout);
                }
                else
                {
                    var renumberStrikeout = MapSingleRenumberStrikeout(
                        group.Single(),
                        percentageMinimum,
                        percentageMaximum);

                    allToReturn.Add(renumberStrikeout);
                }
            }

            return allToReturn;
        }
    }
}
