using System.Collections.ObjectModel;
using Valg.EVA.Skanning.Contracts.CommonEnums;
using Valg.EVA.Skanning.Entity.SerializedEntities.CountResultModel;
using Valg.EVA.Skanning.Repositories.BallotPaper;
using Valg.EVA.Skanning.Repositories.Counting;
using Valg.EVA.Skanning.ServicesModel.ServiceContracts.CountResult.Write;
using Valg.EVA.Skanning.Tools;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using Valg.EVA.Skanning.Services.Count;
using Valg.EVA.Skanning.Services.CountResult.XmlBuilders;

namespace Valg.EVA.Skanning.Services.CountResult
{
    public class CountingEmlGenerator
    {
        private readonly ICountingResultRepository countingResultRepository;
        private readonly IBallotPaperRepository ballotPaperRepository;
        private readonly ITiffConverter tiffConverter;
        private readonly ICountingService countingService;

        public CountingEmlGenerator(ICountingResultRepository countingResultRepository, IBallotPaperRepository ballotPaperRepository, ICountingService countingService, ITiffConverter tiffConverter)
        {
            this.countingResultRepository = countingResultRepository;
            this.ballotPaperRepository = ballotPaperRepository;
            this.countingService = countingService;
            this.tiffConverter = tiffConverter;
        }

        public GenerateCountingEmlResult CreateEmlForResult(GenerateCountingEmlParameters parameters)
        {
            var castVotes = countingResultRepository.GetCastVotes(parameters.CountingId);
            var result = new GenerateCountingEmlResult();

            var countEml = CreateCountResult(castVotes, parameters.ReportingUnitIdentifier);
            result.CountXml = Serialize(countEml);

            if (IsFinalCounting(parameters.CountingId))
            {
                var votesEml = CreateVoteResult(castVotes, parameters.ReportingUnitIdentifier);
                result.VoteXml = Serialize(votesEml);
                result.Images = GetImages(votesEml);
            }

            return result;
        }

        private bool IsFinalCounting(int countingId)
        {
            var counting = countingService.GetCounting(countingId);
            return counting.CountingType == CountingType.Final;
        }

        private Dictionary<string, byte[]> GetImages(Oasis.EMLVote.EML votesEml)
        {
            if (votesEml != null)
            {
                var votesWithImage = votesEml.Votes.CastVote.Where(p => p.BallotIdentifier.BallotName != null);
                return votesWithImage.ToDictionary(castVote => castVote.BallotIdentifier.BallotName, GetImage);
            }
            else
            {
                return new Dictionary<string, byte[]>();
            }
        }

        private byte[] GetImage(Oasis.EMLVote.VotesCastVote castVote)
        {
            byte[] combinedImage;
            var image = ballotPaperRepository.GetBallotPaperImages(int.Parse(castVote.BallotIdentifier.Id));

            if (image.ImageCombined != null)
            {
                combinedImage = image.ImageCombined;
            }
            else
            {
                using (var outputSteam = new MemoryStream())
                {
                    var imageFrontStream = new MemoryStream(image.ImageFront);
                    var imageBackStream = new MemoryStream(image.ImageBack);
                    tiffConverter.CombineImages(new Stream[] { imageFrontStream, imageBackStream }, outputSteam);
                    combinedImage = outputSteam.ToArray();
                }
            }
            return combinedImage;
        }

        private static string Serialize(object instance)
        {
            if (instance != null)
            {
                var serializer = new XmlSerializer(instance.GetType());

                using (StringWriter sw = new EncodingStringWriter(Encoding.UTF8))
                {
                    serializer.Serialize(sw, instance);
                    return sw.ToString();
                }
            }
            else
            {
                return null;
            }
        }

        private static Oasis.EMLCount.EML CreateCountResult(ReadOnlyCollection<CastVote> castVotes, string reportingUnitIdentifierOfCaller)
        {
            var builder = new CountXmlBuilder();
            return builder.CreateCountResult(castVotes, reportingUnitIdentifierOfCaller);
        }

        private Oasis.EMLVote.EML CreateVoteResult(ReadOnlyCollection<CastVote> castVotes, string reportingUnitIdentifierOfCaller)
        {
            var builder = new VoteXmlBuilder();
            return builder.CreateCountResult(castVotes, reportingUnitIdentifierOfCaller);
        }

        private class EncodingStringWriter : StringWriter
        {
            private readonly Encoding _encoding;

            public EncodingStringWriter(Encoding encoding)
            {
                _encoding = encoding;
            }

            public override Encoding Encoding
            {
                get { return _encoding; }
            }
        }
    }
}
