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

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

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> {

  @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(anzahlSitze, teilnehmerErgebnisse, anzahlSitze - anzahlVerteilterSitze);
  }

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

  private HareNiemeyerErgebnis restsitzverteilung(long anzahlSitze,
      List<HareNiemeyerTeilnehmerErgebnis> teilnehmerErgebnisse, long anzahlRestsitze) {
    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()) {
      // ohne Losen
      return new HareNiemeyerErgebnis(anzahlSitze, teilnehmerErgebnisse);
    } else {
      // mit Losen
      return new HareNiemeyerErgebnis(anzahlSitze, teilnehmerErgebnisse, vre.getUnentschiedene(),
          vre.getAnzahlUnverteilterSitze());
    }
  }
}
