
package de.ivu.elect.business.sitzberechnung.control.absolutemehrheitswahl;

import static de.ivu.elect.jee.entity.EntityUtil.castTo;

import java.util.Collection;
import java.util.List;

import javax.inject.Inject;

import de.ivu.elect.business.gebietsbaum.entity.Wahldurchfuehrungsgebiet;
import de.ivu.elect.business.kandidatur.boundary.WasWaehlerBenannteKandidaturKonfiguration;
import de.ivu.elect.business.sitzberechnung.control.LessAbstractSitzberechnungsalgorithmus;
import de.ivu.elect.business.sitzberechnung.control.direktwahl.StandardDirektwahlSitzberechnungsalgorithmusInternal;
import de.ivu.elect.business.sitzberechnung.entity.SitzberechnungsalgorithmusKonfiguration;
import de.ivu.elect.business.sitzberechnung.entity.Sitzberechnungseingabe;
import de.ivu.elect.business.sitzberechnung.entity.Sitzberechnungsergebnis;
import de.ivu.elect.business.wahl.entity.WahlartSitzberechnungsalgorithmus;
import de.ivu.elect.business.wahlvorschlag.entity.Direktkandidat;
import de.ivu.elect.business.wahlvorschlag.entity.Wahlvorschlag;

/**
 * Direktwahl, bei der der Sieger die absoluten Mehrheit erhalten muss. Ansonsten findet eine Stichwahl zwischen dem Erstplatzierten und dem Zweitplatzierten statt. Dies findet z. B. Anwendung bei den
 * meisten Bürgermeister- und Landratswahlen in Deutschland. (siehe z.B. https://de.wikipedia.org/wiki/Mehrheitswahl#Absolute_Mehrheitswahl).
 *
 * @see AbsoluteMehrheitswahlSitzberechnungsalgorithmusInternal
 */
public class AbsoluteMehrheitswahlSitzberechnungsalgorithmus extends LessAbstractSitzberechnungsalgorithmus {

  private static final long serialVersionUID = 1L;

  @Inject
  private transient WasWaehlerBenannteKandidaturKonfiguration waehlerBenannteKandidaturKonfiguration;

  public AbsoluteMehrheitswahlSitzberechnungsalgorithmus() {
    super();
  }

  @Override
  public WahlartSitzberechnungsalgorithmus getSitzberechnungsalgorithmusEnum() {
    return WahlartSitzberechnungsalgorithmus.ELECT_ABSOLUTE_MEHRHEITSWAHL;
  }

  @Override
  public Sitzberechnungsergebnis fuehreSitzberechnungDurch(Sitzberechnungseingabe<SitzberechnungsalgorithmusKonfiguration> sitzberechnungseingabe) {
    StandardDirektwahlSitzberechnungsalgorithmusInternal standardDirektwahlSitzberechnungsalgorithmusInternal = new StandardDirektwahlSitzberechnungsalgorithmusInternal(sitzberechnungseingabe,
        isVergibtSitze());

    List<Wahldurchfuehrungsgebiet> gebiete = standardDirektwahlSitzberechnungsalgorithmusInternal.bestimmeDirektkandidatenGebiete(); // ELECT-9409

    if (sitzberechnungseingabe.getStimmergebnisDaten().getWahl().getWahlgang().isStichwahl()) {
      return standardDirektwahlSitzberechnungsalgorithmusInternal.berechneSitzverteilung(gebiete); // ELECTKOM-828
    }

    return new AbsoluteMehrheitswahlSitzberechnungsalgorithmusInternal(this, sitzberechnungseingabe, waehlerBenannteKandidaturKonfiguration).berechneSitzverteilung(gebiete);
  }

  @Override
  public boolean isVergibtSitze() {
    return false;
  }

  @Override
  public boolean isBerechnetErgebnisFallsWaehlerBenannteWahlvorschlaegeSonstigeErfasst() {
    return false;
  }

  @Override
  public boolean isWaehlerBenannteWahlvorschlaegeErfassbar(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, Collection< ? extends Wahlvorschlag> wahlvorschlaege) {
    return countDirektkandidatenAufStimmzettelWaehlerBenannteDirektkandidatenExcluded(wahlvorschlaege) <= 1; // ELECT-9317
  }

  private long countDirektkandidatenAufStimmzettelWaehlerBenannteDirektkandidatenExcluded(Collection< ? extends Wahlvorschlag> wahlvorschlaege) {
    int result = 0;

    for (Wahlvorschlag wahlvorschlag : wahlvorschlaege) {
      if (wahlvorschlag.isDirektkandidat() && wahlvorschlag.isAufStimmzettel()
          && !waehlerBenannteKandidaturKonfiguration.isWaehlerBenannterDirektkandidat(castTo(wahlvorschlag, Direktkandidat.class))) {
        result++;
      }
    }

    return result;
  }
}
