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

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.NachNummerSortierer;
import de.ivu.elect.business.sitzberechnung.control.verteilung.AbstractVerteilung;
import de.ivu.elect.business.sitzberechnung.control.verteilung.Teilnehmer;

/**
 * Implementation der D'Hondt-Verteilung als Divisor-Verfahren ohne Bezug zum Datenmodell. Damit ist es auch möglich, eine angefangene Verteilung später fortzusetzen.
 * <p>
 * Die Verteilung von Restsitzen per Los ist nicht enthalten. Es werden im Ergebnisobjekt lediglich die dafür
 * erforderlichen Informationen geliefert.
 */
public class DHondtWeiterverteilung extends AbstractVerteilung<DHondtWeiterverteilungTeilnehmerErgebnis, DHondtWeiterverteilungErgebnis> {

    /**
     * @param anzahlSitze
     * @param dieTeilnehmer darf nur WeiterverteilungTeilnehmer enthalten. Ansonsten kommt es zu einer ClassCastException
     * @return
     */
    @Override
    protected DHondtWeiterverteilungErgebnis createErgebnisOhneVerteilung(long anzahlSitze, List<? extends Teilnehmer> dieTeilnehmer) {
        return DHondtWeiterverteilungErgebnis.ohneVerteilung(anzahlSitze, dieTeilnehmer);
    }

    @Override
    protected DHondtWeiterverteilungErgebnis verteileSitze(long anzahlSitze, List<? extends Teilnehmer> dieTeilnehmer, long stimmenGesamt) {
        List<DHondtWeiterverteilungTeilnehmerErgebnis> teilnehmerErgebnisse = new ArrayList<>();
        // Erzeuge Ergebnis-Objekte und vergib die ganzzahligen Sitze (nach Abrunden)
        for (Teilnehmer teilnehmer : dieTeilnehmer) {
            DHondtWeiterverteilungTeilnehmerErgebnis ergebnis = new DHondtWeiterverteilungTeilnehmerErgebnis(teilnehmer);
            teilnehmerErgebnisse.add(ergebnis);
        }

        return restsitzverteilung(anzahlSitze, teilnehmerErgebnisse, anzahlSitze);
    }

    private DHondtWeiterverteilungErgebnis restsitzverteilung(long anzahlSitze, List<DHondtWeiterverteilungTeilnehmerErgebnis> teilnehmerErgebnisse,
                                                              long dieAnzahlRestsitze) {
        long anzahlRestsitze = dieAnzahlRestsitze;
        while (anzahlRestsitze > 0) {
            NachNummerSortierer<DHondtWeiterverteilungTeilnehmerErgebnis, Fraction> sortierer = new NachNummerSortierer<>();
            for (DHondtWeiterverteilungTeilnehmerErgebnis teilnehmerErgebnis : teilnehmerErgebnisse) {
                sortierer.add(teilnehmerErgebnis.getNaechsterQuotient(), teilnehmerErgebnis);
            }

            List<DHondtWeiterverteilungTeilnehmerErgebnis> erstplatzierte = sortierer.getErstplatzierte();
            if (erstplatzierte.size() > anzahlRestsitze) {
                for (DHondtWeiterverteilungTeilnehmerErgebnis dHondtTeilnehmerErgebnis : erstplatzierte) {
                    dHondtTeilnehmerErgebnis.seiTeilnehmerAmLosverfahren();
                }
                // Ergebnis mit Losen
                return new DHondtWeiterverteilungErgebnis(anzahlSitze, teilnehmerErgebnisse, erstplatzierte, anzahlRestsitze);
            } else {
                for (DHondtWeiterverteilungTeilnehmerErgebnis dHondtTeilnehmerErgebnis : erstplatzierte) {
                    dHondtTeilnehmerErgebnis.vergibRestsitz();
                    anzahlRestsitze--;
                }
            }
        }

        // Ergebnis ohne Losen
        return new DHondtWeiterverteilungErgebnis(anzahlSitze, teilnehmerErgebnisse);
    }

}
