/*
 * ElectionResultForP2ListsDeterminator
 *
 * Created on 01.01.2009
 * Copyright (c) 2009 Kiesraad
 */
package de.ivu.wahl.result.determination;

import de.ivu.wahl.result.Util;
import de.ivu.wahl.result.drawlots.DrawingLotsCallback;
import de.ivu.wahl.result.gsda.GeneralSeatDistributor;
import de.ivu.wahl.result.gsda.GsdaParameters;
import de.ivu.wahl.result.gsda.GsdaResult;
import de.ivu.wahl.result.i18n.MessageKeys;
import de.ivu.wahl.result.i18n.Messages;
import de.ivu.wahl.result.result.AnomalyFactory;
import de.ivu.wahl.result.result.AssignmentSupplement;
import de.ivu.wahl.result.result.AssignmentType;
import de.ivu.wahl.result.result.Distribution;
import de.ivu.wahl.result.result.ElectionResultCollector;

import java.util.Map;

import static de.ivu.wahl.result.determination.ElectionResultDeterminator.APPLOG;

/**
 * Determines the number of seats for the P2 lists belonging to a given P3 list.
 *
 * @author , IVU Traffic Technologies AG
 */
public class ElectionResultForP2ListsDeterminator {

  /**
   * @return an unmodifiable Map with the number of seats for each P2 list or P3 list
   */
  public GsdaResult<P2List> calculate(DrawingLotsCallback drawingLotsCallback, P3List parent,
                                      long totalSeats, Map<P2List, Long> listsAndVotes, Map<P2List, Long> listsAndCandidates,
                                      ElectionResultCollector electionResultCollector, AnomalyFactory anomalyFactory,
                                      Distribution distribution) {

    long totalVotes = getTotalVotes(listsAndVotes);
    APPLOG.info(Messages.bind(MessageKeys.Result_Tracelog_AssignmentSeatsWithin,
            totalSeats,
            parent.getName()));

    if (totalSeats > 0 && listsAndVotes.keySet().size() > 1) {
      APPLOG.info("*** " //$NON-NLS-1$
              + Messages.bind(MessageKeys.Result_Tracelog_ElectoralQuota,
              parent.getName(),
              Util.displayQuotient(totalVotes, totalSeats)));
      assignmentStarted(parent, totalVotes, totalSeats, electionResultCollector);

      GsdaParameters parameters;
      if (needsCosalgMode(parent)) {
        parameters = GsdaParameters.forP2DistributionTkPs2();
      } else {
        parameters = GsdaParameters.forP2P3Distribution();
      }
      GeneralSeatDistributor<P2List, P3List> generalSeatDistributor = new GeneralSeatDistributor<>(
              drawingLotsCallback, parent, totalSeats, listsAndVotes, listsAndCandidates, parameters,
              electionResultCollector, anomalyFactory, distribution);
      return generalSeatDistributor.calculate();
    } else {
      return new GsdaResult<>(trivialDistribution(parent,
              totalSeats,
              listsAndCandidates,
              electionResultCollector));
    }
  }

  private long getTotalVotes(Map<P2List, Long> listsAndVotes) {
    return listsAndVotes.values().stream().mapToLong(Long::longValue).sum();
  }

  private void assignmentStarted(P3List parent, long totalVotes, long totalSeats,
                                 ElectionResultCollector electionResultCollector) {

    electionResultCollector.subDistributionStarted(parent, totalVotes, totalSeats, Distribution.P2);
  }

  /**
   * @return <code>true</code> if p3List is a list group, i.e. has more than one P2List.
   * <code>false</code> otherwise.
   */
  private boolean needsCosalgMode(P3List p3List) {
    return p3List.getP2Lists().size() > 1;
  }

  /**
   * The trivial cases: no seats or only one list
   */
  private Map<P2List, Long> trivialDistribution(P3List parent, long totalSeats, Map<P2List, Long> listsAndCandidates,
                                                ElectionResultCollector electionResultCollector) {

    final int index = 1;
    final boolean byLot = false;
    final boolean cosalgMode = false;
    final AssignmentType assignmentType = AssignmentType.TRIVIAL;

    AssignmentTracer<P2List, P3List> assignmentTracer = new AssignmentTracer<>(listsAndCandidates,
            parent, electionResultCollector);
    for (P2List list : listsAndCandidates.keySet()) {
      assignmentTracer.assignment(index,
              list,
              assignmentType,
              byLot,
              totalSeats,
              cosalgMode,
              AssignmentSupplement.NULL);
    }
    assignmentTracer.finishAssignment(index, cosalgMode);

    return assignmentTracer.getResult();
  }
}
