
package de.ivu.elect.business.ergebnis.boundary;

import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.List;

import javax.inject.Inject;

import de.ivu.elect.business.ergebnis.control.StimmenVergleichsHochrechner;
import de.ivu.elect.business.ergebnis.entity.Ergebnis;
import de.ivu.elect.business.ergebnis.entity.ErgebnisStatus;
import de.ivu.elect.business.ergebnis.entity.Ergebnisart;
import de.ivu.elect.business.ergebnis.entity.Stimmen;
import de.ivu.elect.business.sitzberechnung.control.Sitzberechnung;
import de.ivu.elect.business.wahl.entity.Wahl;

/**
 * Die Vergleichshochrechnung basiert auf einem Vorperioden-Ergebnis und einem vollständigen oder unvollständigen
 * aktuellen Ergebnis. Mindestens zwei Erfassungsgebiete müssen eingegangen sein. Die Vergleichshochrechnung berechnet
 * für jedes Erfassungsgebiet eine Prognose, für die auch im aktuellen Ergebnis ein Ergebniseingang vorliegt. Diese
 * Prognose ergibt sich durch Hochrechnen auf der Basis des aktuellen Ergebnisses abzüglich des Ergebniseingangs für das
 * jeweils hochgerechnete Gebiet.
 */
public class VergleichsHochrechnungErgebnisberechnung {

  @Inject
  ErgebnisDAO ergebnisDAO;

  @Inject
  StimmenDAO stimmenDAO;

  @Inject
  StimmenVergleichsHochrechner stimmenVergleichsHochrechner;

  @Inject
  Sitzberechnung sitzberechnung;

  /**
   * Wenn ein Vorperode-Ergebnis vorliegt, erzeuge eine Kopie von ergebnis und ergänze dieses um hochgerechnete
   * Stimmergebnisse und führe damit eine Sitzberechnung durch. Wenn kein Vorperiode-Ergebnis vorliegt, dann tue nichts.
   * 
   * @param ergebnis
   *          das aktuelle Ergebnis (vorläufig oder endgültig), auf dessen Grundlage eine Hochrechnung erstellt werden
   *          soll
   */
  public void fuehreHochrechnungDurchWennMoeglich(Ergebnis ergebnis, String ausloeser) {
    Collection<Stimmen> vorperiodenStimmen = getVorperiodenStimmen(ergebnis.getWahl());
    if (!vorperiodenStimmen.isEmpty()) {
      berechneHochrechnung(ergebnis, ausloeser, vorperiodenStimmen);
    }
  }

  private Collection<Stimmen> getVorperiodenStimmen(Wahl wahl) {
    List<Ergebnis> ergebnisseVorperiode = ergebnisDAO.findByWahlAndErgebnisart(wahl, Ergebnisart.VORPERIODE);
    if (ergebnisseVorperiode.isEmpty()) {
      return Collections.emptyList();
    }
    Ergebnis ergebnisVorperiode = ergebnisseVorperiode.get(0);
    List<Stimmen> stimmenVorperiode = stimmenDAO.findByErgebnisNoCache(ergebnisVorperiode);
    return stimmenVorperiode;
  }

  /**
   * Erzeugt ein persistentes Ergebnis-Objekt, ergänzt es um hochgerechnete Stimmen-Objekte.
   */
  private void berechneHochrechnung(Ergebnis ergebnis, String ausloeser, Collection<Stimmen> vorperiodenStimmen) {
    Ergebnis ergebnisFuerHochrechnung = createErgebnisFuerDieHochrechnung(ergebnis);
    setzeZeitpunktUndAusloeser(ausloeser, ergebnisFuerHochrechnung);
    stimmenVergleichsHochrechner.ergaenzeHochgerechneteStimmen(ergebnis, ergebnisFuerHochrechnung, vorperiodenStimmen);
  }

  /**
   * Erzeugt eine persistente Kopie von ergebnis, in der die Hochrechnung gespeichert wird
   */
  private Ergebnis createErgebnisFuerDieHochrechnung(Ergebnis ergebnis) {
    Ergebnis result = new Ergebnis();
    result.setWahl(ergebnis.getWahl());
    Ergebnisart ergebnisart = ergebnis.getErgebnisart().isEndgueltig()
        ? Ergebnisart.HOCHRECHNUNG_ENDGUELTIG
        : Ergebnisart.HOCHRECHNUNG_VORLAEUFIG;
    result.setErgebnisart(ergebnisart);
    result.setErgebnisStatus(ErgebnisStatus.ZU_BERECHNEN);
    result.setName(ergebnis.getName()); // TODO: Vielleicht ein eigener Name???
    result.setEingangssequenznummer(ergebnis.getEingangssequenznummer());
    result.setSitzberechnungsalgorithmus(ergebnis.getSitzberechnungsalgorithmus());
    ergebnisDAO.persist(result);

    return result;
  }

  private void setzeZeitpunktUndAusloeser(String ausloeser, Ergebnis ergebnis) {
    ergebnis.setAusloeser(ausloeser);
    ergebnis.setZeitstempel(new Date());
  }

}
