using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;

namespace InterpreterLibrary.Models
{
    public class BallotPaperOutput
    {
        private readonly IList<ListSelection> _listSelections = new List<ListSelection>();
        private readonly IList<PersonVote> _personVotes = new List<PersonVote>();
        private readonly IList<Renumbering> _renumberings = new List<Renumbering>();
        private readonly IList<float> _stampFillPercentages = new List<float>();
        private readonly IList<Strikeout> _strikeouts = new List<Strikeout>();
        private readonly IList<WriteIn> _writeIns = new List<WriteIn>();
        private readonly IList<BallotHeaderResult> _ballotHeaders = new List<BallotHeaderResult>();

        public BallotType Type { get; internal set; }
		public float TypeConfidence { get;internal set;}
        public OcrResult BallotNo { get; internal set; }
        public OcrResult BallotId { get; internal set; }
        public OcrResult UnlistedAffiliationName { get; internal set; }
        public IReadOnlyList<float> StampFillPercentages => new ReadOnlyCollection<float>(_stampFillPercentages);
        public float BlankFillPercentage { get; internal set; }
        public IEnumerable<Strikeout> Strikeouts => new ReadOnlyCollection<Strikeout>(_strikeouts.OrderBy(e=>e.CandidateNumber).ToList());
        public IEnumerable<ListSelection> ListSelections => new ReadOnlyCollection<ListSelection>(_listSelections.OrderBy(e => e.CandidateNumber).ToList());
        public IEnumerable<Renumbering> Renumberings => new ReadOnlyCollection<Renumbering>(_renumberings.OrderBy(e => e.CandidateNumber).ToList());
        public IEnumerable<PersonVote> PersonVotes => new ReadOnlyCollection<PersonVote>(_personVotes.OrderBy(e => e.CandidateNumber).ToList());
        public IReadOnlyList<WriteIn> WriteIns => new ReadOnlyCollection<WriteIn>(_writeIns);
        public IReadOnlyCollection<BallotHeaderResult> BallotHeaders => new ReadOnlyCollection<BallotHeaderResult>(_ballotHeaders);

        private Func<bool> _dirtLineChecker;

        internal void SetDirtLineChecker(Func<bool> function)
        {
            _dirtLineChecker = function;
        }
        public bool HasDirtLines => _dirtLineChecker();

        internal void AddStrikeout(Strikeout strikeout)
        {
            _strikeouts.Add(strikeout);
        }

        internal void AddListSelection(ListSelection listSelection)
        {
            _listSelections.Add(listSelection);
        }

        internal void AddRenumbering(Renumbering renumbering)
        {
            _renumberings.Add(renumbering);
        }

        internal void AddPersonVote(PersonVote personVote)
        {
            _personVotes.Add(personVote);
        }

        internal void AddWriteIn(WriteIn writeIn)
        {
            _writeIns.Add(writeIn);
        }

        internal void AddAffiliationName(BallotHeaderResult ballotHeader)
        {
            _ballotHeaders.Add(ballotHeader);
        }

        public void AddStampFillPercentage(float stampFillPercentage)
        {
            _stampFillPercentages.Add(stampFillPercentage);
        }

        public override bool Equals(object obj)
        {
            if (!(obj is BallotPaperOutput other))
            { 
                return base.Equals(obj);
            }

            if (other.BallotId?.InterpretedText != this.BallotId?.InterpretedText) return false;
            if (other.BallotNo?.InterpretedText != this.BallotNo?.InterpretedText) return false;
            if (other.BlankFillPercentage != this.BlankFillPercentage) return false;

            var otherPersonVotes = from pv in other.PersonVotes select (pv.CandidateNumber, pv.FillPercentage, pv.Text);
            var thisPersonVotes = from pv in this.PersonVotes select (pv.CandidateNumber, pv.FillPercentage, pv.Text);
            if (!otherPersonVotes.SequenceEqual(thisPersonVotes)) return false;

            if (!other.Strikeouts.SequenceEqual(this.Strikeouts)) return false;
            if (!other.Renumberings.SequenceEqual(this.Renumberings)) return false;

            var otherWriteIns = from w in other.WriteIns select (w.Affiliation, w.AffiliationFillPercentage, w.Name, w.NameFillPercentage);
            var thisWriteIns = from w in this.WriteIns select (w.Affiliation, w.AffiliationFillPercentage, w.Name, w.NameFillPercentage);
            if (!otherWriteIns.SequenceEqual(thisWriteIns)) return false;

            var otherBallotHeaders = from b in other.BallotHeaders select (b.AffiliationName, b.AffiliationNameConfidence, b.ContestName, b.ContestNameConfidence, b.TemplateFieldId);
            var thisBallotHeaders = from b in other.BallotHeaders select (b.AffiliationName, b.AffiliationNameConfidence, b.ContestName, b.ContestNameConfidence, b.TemplateFieldId);
            if (!otherBallotHeaders.SequenceEqual(thisBallotHeaders)) return false;

            return true;
        }
    }
}
