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

import java.util.ArrayList;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import de.ivu.elect.business.sitzberechnung.control.util.Fraction;
import de.ivu.elect.business.sitzberechnung.control.util.VordereInRangfolgeErmittler;
import de.ivu.elect.business.sitzberechnung.control.verteilung.AbstractVerteilung;
import de.ivu.elect.business.sitzberechnung.control.verteilung.Teilnehmer;

/**
 * Implementation der Verteilung nach Hare-Niemeyer ohne Bezug zum Datenmodell.
 * <p/>
 * Die Verteilung von Restsitzen per Los ist nicht enthalten. Es werden im Ergebnisobjekt lediglich die dafür erforderlichen Informationen geliefert.
 */
public class HareNiemeyerVerteilung extends AbstractVerteilung<HareNiemeyerTeilnehmerErgebnis, HareNiemeyerErgebnis> {

  private static final Logger LOG = LoggerFactory.getLogger(HareNiemeyerVerteilung.class);

  public HareNiemeyerVerteilung() {
    super();
  }

  @Override
  protected HareNiemeyerErgebnis createErgebnisOhneVerteilung(long anzahlSitze, List< ? extends Teilnehmer> dieTeilnehmer) {
    return HareNiemeyerErgebnis.ohneVerteilung(anzahlSitze, dieTeilnehmer);
  }

  @Override
  protected HareNiemeyerErgebnis verteileSitze(long anzahlSitze, List< ? extends Teilnehmer> dieTeilnehmer, long stimmenGesamt) {
    Fraction hareQuotient = getDivisor(anzahlSitze, stimmenGesamt);

    long anzahlVerteilterSitze = 0;

    List<HareNiemeyerTeilnehmerErgebnis> teilnehmerErgebnisse = new ArrayList<>();

    // Erzeuge Ergebnis-Objekte und vergib die ganzzahligen Sitze (nach Abrunden)
    for (Teilnehmer teilnehmer : dieTeilnehmer) {
      HareNiemeyerTeilnehmerErgebnis ergebnis = new HareNiemeyerTeilnehmerErgebnis(teilnehmer, hareQuotient);

      teilnehmerErgebnisse.add(ergebnis);

      anzahlVerteilterSitze += ergebnis.getSitzeAusAbrundung();
    }

    return restsitzverteilung(teilnehmerErgebnisse, anzahlSitze, anzahlVerteilterSitze);
  }

  protected Fraction getDivisor(long anzahlSitze, long stimmenGesamt) {
    return Fraction.getFraction(stimmenGesamt, anzahlSitze);
  }

  protected HareNiemeyerErgebnis restsitzverteilung(List<HareNiemeyerTeilnehmerErgebnis> teilnehmerErgebnisse, long anzahlSitze, long anzahlVerteilterSitze) {
    long anzahlRestsitze = anzahlSitze - anzahlVerteilterSitze;

    if (anzahlRestsitze < 0) { // ELECT-11926, Super-Spezialfall!!! Bspw. in der AGH-Wahl bei Gewinn eines Direktmandates und gleichzeitig 0 Zweitstimmen...
      LOG.warn("Anzahl Restsitze ist negativ und wird auf 0 korrigiert [anzahlRestsitze = {}, anzahlSitze = {}, anzahlVerteilterSitze = {}]", anzahlRestsitze, anzahlSitze, anzahlVerteilterSitze);

      anzahlRestsitze = 0L;
    }

    VordereInRangfolgeErmittler<HareNiemeyerTeilnehmerErgebnis> vre = new VordereInRangfolgeErmittler<>(teilnehmerErgebnisse, HareNiemeyerTeilnehmerErgebnis.SORT_BY_REST, (int) anzahlRestsitze);

    for (HareNiemeyerTeilnehmerErgebnis einErgebnis : vre.getVordere()) {
      einErgebnis.vergibRestsitz();
    }

    for (HareNiemeyerTeilnehmerErgebnis einErgebnis : vre.getUnentschiedene()) {
      einErgebnis.seiTeilnehmerAmLosverfahren();
    }

    if (vre.isEntschieden()) {
      return new HareNiemeyerErgebnis(anzahlSitze, teilnehmerErgebnisse); // ohne Losen
    } else {
      return new HareNiemeyerErgebnis(anzahlSitze, teilnehmerErgebnisse, vre.getUnentschiedene(), vre.getAnzahlUnverteilterSitze()); // mit Losen
    }
  }
}
