/** @return list of all ballots for the voter */
  @Override
  public List<ProportionalBallot> getProportionalBallotsForVoter(Voter voter) {
    List<ProportionalBallot> ballots = new ArrayList<>();
    for (ProportionalElection election : proportionalElectionRepository.findAll()) {
      ProportionalBallot ballot =
          proportionalBallotRepository.findByProportionalElectionIdAndVoterId(
              election.getId(), voter.getId());
      if (ballot == null) {
        ballot = new ProportionalBallot();
        ballot.setProportionalElection(election);
        ballot.setVoter(voter);
        ballot.setVotedCandidates(new ArrayList<ProportionalBallotVotedCandidate>());
        ballots.add(ballot);
      }
    }

    return ballots;
  }
  /**
   * calculate the results of the proportional results
   *
   * @return a list with ElectionVotes
   */
  @Override
  public List<ElectionVotes> getProportionalElectionsResults() {
    List<ProportionalElection> proportionalElections =
        EVotingUtil.makeCollection(proportionalElectionRepository.findAll());
    List<ElectionVotes> list = new ArrayList<>();

    for (ProportionalElection election : proportionalElections) {
      ElectionVotes electionVotes = new ElectionVotes();
      electionVotes.setElection(election);
      list.add(electionVotes);

      List<ProportionalBallot> ballots =
          proportionalBallotRepository.findByProportionalElectionId(election.getId());

      // iterate over all ballots to count votes for party
      for (ProportionalBallot ballot : ballots) {
        // if voter voted for a certain list we can take the number of
        // seats to add to total votes
        if (ballot.getVotedList() != null) {
          electionVotes.setTotalVotes(electionVotes.getTotalVotes() + election.getNumberOfSeats());
        } else {
          // because list is not assigned to party, we only count the
          // voted candidates
          electionVotes.setTotalVotes(
              electionVotes.getTotalVotes() + ballot.getVotedCandidates().size());
        }
      }

      for (ProportionalBallot ballot : ballots) {
        if (ballot.getVotedList() != null) {
          // the party gets all votes of the list, if all candidates
          // are from this party
          int partyListCount = election.getNumberOfSeats();
          for (ProportionalBallotVotedCandidate ballotVotedCandidate :
              ballot.getVotedCandidates()) {
            Candidate candidate = ballotVotedCandidate.getCandidate();
            if (candidate.getParty().getId().equals(ballot.getVotedList().getParty().getId())
                == false) {
              // the canidate on the list is no from this party,
              // so we decrement the party list votes by one
              partyListCount--;
              // the party of this candidate gets a vote
              PartyListVotes partyListVotes =
                  electionVotes.getPartyListVotes().get(candidate.getParty());
              if (partyListVotes == null) {
                partyListVotes = new PartyListVotes();
                electionVotes.getPartyListVotes().put(candidate.getParty(), partyListVotes);
              }
              // increment the party votes by one
              partyListVotes.setPartyVotes(partyListVotes.getPartyVotes() + 1);
            }
          }

          // set the remaining votes for the list party
          PartyListVotes partyListVotes =
              electionVotes.getPartyListVotes().get(ballot.getVotedList().getParty());
          if (partyListVotes == null) {
            partyListVotes = new PartyListVotes();
            electionVotes.getPartyListVotes().put(ballot.getVotedList().getParty(), partyListVotes);
          }
          partyListVotes.setPartyVotes(partyListVotes.getPartyVotes() + partyListCount);

        }

        // if voter didnt vote for a certain party, we can simply count
        // the votes for each candidates party
        else {
          for (ProportionalBallotVotedCandidate ballotVotedCandidate :
              ballot.getVotedCandidates()) {
            Candidate candidate = ballotVotedCandidate.getCandidate();
            // get the vote counter for the candidates party
            PartyListVotes partyListVotes =
                electionVotes.getPartyListVotes().get(candidate.getParty());
            if (partyListVotes == null) {
              partyListVotes = new PartyListVotes();
              electionVotes.getPartyListVotes().put(candidate.getParty(), partyListVotes);
            }
            // increment the party votes by one
            partyListVotes.setPartyVotes(partyListVotes.getPartyVotes() + 1);
          }
        }
      }

      int distributedSeats = 0; // counter to see, if seats are remaining

      for (Entry<Party, PartyListVotes> entry : electionVotes.getPartyListVotes().entrySet()) {
        // total votes for this party
        int totalPartyVotes = entry.getValue().getPartyVotes();
        // total votesfor the wholeelection
        int totalVotes = electionVotes.getTotalVotes();

        int wonSeats = totalPartyVotes / ((totalVotes / (election.getNumberOfSeats() + 1)) + 1);
        entry.getValue().setWonSeats(wonSeats);
        distributedSeats += wonSeats;
      }

      // we need to distribute the remaining seats
      while (distributedSeats < election.getNumberOfSeats()) {
        int greatestQuotient = 0;
        PartyListVotes greatestQuotientPartyListVote = null;

        for (Entry<Party, PartyListVotes> entry : electionVotes.getPartyListVotes().entrySet()) {
          // total votes for this party
          int totalPartyVotes = entry.getValue().getPartyVotes();
          // the number of already won seats
          int numberOfWonSeats = entry.getValue().getWonSeats();

          int quotient = totalPartyVotes / (numberOfWonSeats + 1);

          // this party has temporary the highest quotient
          if (quotient > greatestQuotient) {
            greatestQuotient = quotient;
            greatestQuotientPartyListVote = entry.getValue();
          }
        }

        // assign the seat to the party with the greatest quotient
        if (greatestQuotientPartyListVote != null) {
          distributedSeats++;
          greatestQuotientPartyListVote.setWonSeats(
              greatestQuotientPartyListVote.getWonSeats() + 1);
        } else {
          throw new RuntimeException("unable to distribute the remaining seats!");
        }
      }

      // now we distributed all seats and have to calculate which
      // candidate won has been elected

      // first count the votes for each candidate
      for (ProportionalBallot ballot : ballots) {
        for (ProportionalBallotVotedCandidate ballotVotedCandidate : ballot.getVotedCandidates()) {
          Candidate candidate = ballotVotedCandidate.getCandidate();
          PartyListVotes partyListVotes =
              electionVotes.getPartyListVotes().get(candidate.getParty());
          int votesForCandidate = 0;
          if (partyListVotes.getCandidateVotes().containsKey(candidate)) {
            votesForCandidate = partyListVotes.getCandidateVotes().get(candidate);
          }
          partyListVotes.getCandidateVotes().put(candidate, votesForCandidate + 1);
        }
      }

      // now we choose the candidates with highest votes count
      for (Entry<Party, PartyListVotes> entry : electionVotes.getPartyListVotes().entrySet()) {
        int remainingSeats = entry.getValue().getWonSeats();
        while (remainingSeats > 0) {
          int greatestVotes = 0;
          Candidate temporaryCandidate = null;
          for (Entry<Candidate, Integer> candidateEntry :
              entry.getValue().getCandidateVotes().entrySet()) {
            // this candidate is not yet elected and has temporary
            // the highest vote count
            if (entry.getValue().getVotedCandidates().contains(candidateEntry.getKey()) == false
                && candidateEntry.getValue() > greatestVotes) {
              greatestVotes = candidateEntry.getValue();
              temporaryCandidate = candidateEntry.getKey();
            }
          }

          // this candidate is elected
          if (temporaryCandidate != null) {
            entry.getValue().getVotedCandidates().add(temporaryCandidate);
            remainingSeats--;
          } else {
            throw new RuntimeException("seats can not be assigned to a candidate");
          }
        }
      }
    }
    return list;
  }
 /** cast the ballots */
 @Override
 public void castProportionalBallotsForVoter(List<ProportionalBallot> proportionalBallots) {
   proportionalBallotRepository.save(proportionalBallots);
 }