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

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

import javax.annotation.Nullable;

import de.ivu.elect.business.ergebnis.entity.Ergebnis;
import de.ivu.elect.business.ergebnis.entity.Stimmen;
import de.ivu.elect.business.ergebnis.entity.Stimmenart;
import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
import de.ivu.elect.business.gebietsbaum.entity.Wahldurchfuehrungsgebiet;
import de.ivu.elect.business.gruppe.entity.Gruppe;

/**
 *  on 17.02.2016.
 */
@Deprecated // Implementierung zu speziel, de.ivu.elect.business.hochrechnung.boundary.HochrechnungsalgorithmusFinder nutzen
public class StimmenHochrechner extends AbstractStimmenHochrechner {

  private static final Long NOCH_NICHT_BERECHNET = -1L;

  /**
   * Füllt das ergebnisFuerHochrechnung mit Stimmen-Objekten. Diese werden - falls vorhanden - aus ergebnis kopiert,
   * oder andernfalls hochgerechnet aus den Stimmen von ergebnis und den vorperiodenStimmen.
   * 
   * @param ergebnis
   *          das aktuelle, meist noch unvollständige Ergebnis
   * @param ergebnisFuerHochrechnung
   *          das Ergebnis für die Hochrechnung
   * @param vorperiodenStimmen
   *          die Stimmen-Objekte der Vorperiode
   */
  public void ergaenzeHochgerechneteStimmen(Ergebnis ergebnis, Ergebnis ergebnisFuerHochrechnung,
      Collection<Stimmen> vorperiodenStimmen) {
    StimmergebnisDaten stimmergebnisDaten = stimmergebnisDatenCreator.createErgebnisDatenSitzberechnung(ergebnis);
    StimmenFuerHochrechnung stimmenFuerHochrechnung = new StimmenFuerHochrechnung(stimmergebnisDaten,
        vorperiodenStimmen);

    Set<Gebiet> erfassteGebiete = stimmergebnisDaten.getErfassteGebiete();
    for (Wahldurchfuehrungsgebiet gebiet : stimmergebnisDaten.getErfassungsgebiete()) {
      if (erfassteGebiete.contains(gebiet)) {
        kopiereStimmenVonErfassungsgebiet(gebiet, stimmenFuerHochrechnung, ergebnisFuerHochrechnung);
      } else {
        hochrechneStimmenVonErfassungsgebiet(gebiet, stimmenFuerHochrechnung, stimmergebnisDaten,
            ergebnisFuerHochrechnung);
      }
    }
  }

  /**
   * Legt für alle Stimmen-Objekte aus gebiet je eine Kopie für das ergebnisFuerHochrechnung an.
   */
  private void kopiereStimmenVonErfassungsgebiet(Wahldurchfuehrungsgebiet gebiet,
      StimmenFuerHochrechnung stimmenFuerHochrechnung, Ergebnis ergebnisFuerHochrechnung) {
    for (Stimmen toCopy : stimmenFuerHochrechnung.getErfassteStimmen(gebiet)) {
      Stimmen kopie = new Stimmen();
      kopie.setErgebnis(ergebnisFuerHochrechnung);
      kopie.setGruppe(toCopy.getGruppe());
      kopie.setGebiet(toCopy.getGebiet());
      kopie.setAnzahl(toCopy.getAnzahl());
      kopie.setStimmenart(toCopy.getStimmenart());

      stimmenDAO.persist(kopie);
    }
  }

  /**
   * Hochrechnung für Stimmen einer Stimmenart (LISTENSTIMME oder DIREKTSTIMME) für eine Gruppe in einem
   * Erfassungsgebiet, für das kein Ergebnis eingegangen ist. Es werden der Reihe nach die übergeordneten Gebiete
   * durchprobiert für die folgenden beiden Prognosen: 1. StimmenKumuliert * StimmenVorperiode /
   * StimmenVorperiodeKumulierte (also jeweils bezogen auf die jeweilige Gruppe) und 2. StimmenKumuliert *
   * GueltigeStimmenVorperiode / GueltigeStimmenVorperiodeKumuliert (d.h. wenn die Gruppe im Gebiet keine
   * Vorperioden-Werte hat, dann werden alle gültigen Stimmen zum Gewichten herangezogen). Die kumulierten Werte sind
   * immer vom übergeordneten Gebiet, die Vorperioden-Werte von hochzurechnenden Erfassungsgebiet.
   *
   * @param gruppe
   *          für diese Gruppe wird hochgerechnet
   * @param gueltige
   *          die Systemgruppe der gültigen Stimmen
   * @param gebiet
   *          für dieses Gebiet wird hochgerechnet
   * @param uebergeordneteGebiete
   *          sind die Gebiete oberhalb von gebiet bis zur Wurzel
   * @param stimmenart
   *          DIREKTSTIMME oder LISTENSTIMME
   * @return eine Prognose für das Stimmenergebnis der gruppe auf dem gebiet, oder null, falls keine Prognose möglich
   *         war
   */
  @Override
  protected @Nullable Long getHochgerechneteStimmen(Gruppe gruppe, Gruppe gueltige, Wahldurchfuehrungsgebiet gebiet,
      List<Wahldurchfuehrungsgebiet> uebergeordneteGebiete, Stimmenart stimmenart,
      StimmenFuerHochrechnung stimmenFuerHochrechnung) {
    Long stimmenVorperiode = stimmenFuerHochrechnung.getStimmenVorperiode(gruppe, gebiet, stimmenart).get();
    Long gueltigeStimmenVorperiode = NOCH_NICHT_BERECHNET;

    for (Wahldurchfuehrungsgebiet uebergeordnetesGebiet : uebergeordneteGebiete) {
      Long prognose = berechnePrognose(
          () -> stimmenFuerHochrechnung.getStimmenKumuliert(gruppe, uebergeordnetesGebiet, stimmenart),
          stimmenVorperiode,
          () -> stimmenFuerHochrechnung.getStimmenVorperiodeKumuliert(gruppe, uebergeordnetesGebiet, stimmenart));
      if (prognose != null) {
        return prognose;
      }
      if (NOCH_NICHT_BERECHNET.equals(gueltigeStimmenVorperiode)) {
        gueltigeStimmenVorperiode = stimmenFuerHochrechnung.getGueltigeStimmenVorperiode(gueltige, gebiet, stimmenart)
            .get();
      }
      prognose = berechnePrognose(
          () -> stimmenFuerHochrechnung.getStimmenKumuliert(gruppe, uebergeordnetesGebiet, stimmenart),
          gueltigeStimmenVorperiode, () -> stimmenFuerHochrechnung.getGueltigeStimmenVorperiodeKumuliert(gueltige,
              uebergeordnetesGebiet, stimmenart));
      if (prognose != null) {
        return prognose;
      }
    }

    return null;
  }

}
