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

import java.util.Date;

import javax.ejb.Stateless;
import javax.inject.Inject;

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

import de.ivu.elect.business.ergebnis.control.ErgebnisberechnungAusloeserHook;
import de.ivu.elect.business.ergebnis.entity.AbstractErgebnis;
import de.ivu.elect.business.ergebnis.entity.Ergebnis;
import de.ivu.elect.business.ergebnis.entity.ErgebnisPK;
import de.ivu.elect.business.ergebnis.entity.ErgebnisStatus;
import de.ivu.elect.business.ergebnis.entity.ZwischenergebnisBerechnenEvent;
import de.ivu.elect.business.gebietsbaum.boundary.WahldurchfuehrungsgebietDAO;
import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
import de.ivu.elect.business.gruppe.boundary.PolitischeGruppeDAO;
import de.ivu.elect.business.gruppe.entity.Gruppe;
import de.ivu.elect.business.internet.entity.PraesentationGenerierenEvent;
import de.ivu.elect.business.sitzberechnung.control.Sitzberechnung;
import de.ivu.elect.business.sitzberechnung.entity.SitzberechnungsalgorithmusKonfiguration;
import de.ivu.elect.business.stimmeingabe.control.sequenznummer.ErgebnissequenznummerSetzenEvent;
import de.ivu.elect.jee.background.NotifyOnSuccess;

@Stateless
public class Ergebnisberechnung {

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

  @Inject
  private transient ErgebnisDAO ergebnisDAO;

  @Inject
  private transient ErgebnisberechnungAusloeserHook ergebnisberechnungAusloeserHook;

//  @Inject
//  HochrechnungErgebnisberechnung hochrechnungErgebnisberechnung;
//
//  @Inject
//  VergleichsHochrechnungErgebnisberechnung vergleichsHochrechnungErgebnisberechnung;

  @Inject
  private transient Sitzberechnung sitzberechnung;

  @Inject
  transient PraesentationGenerierenEvent praesentationGenerierenEvent;

  @Inject
  private transient ZwischenergebnisBerechnenEvent zwischenergebnisBerechnenEvent;

  @Inject
  private transient ErgebnissequenznummerSetzenEvent ergebnissequenznummerSetzenEvent;

  @Inject
  private transient PolitischeGruppeDAO politischeGruppeDAO;

  @Inject
  private transient WahldurchfuehrungsgebietDAO wahldurchfuehrungsgebietDAO;


  public void ergebnisBerechnenAusloesenUebernahmeGueltigerEingaenge(Ergebnis ergebnisNotAttached) {
    ergebnisBerechnenAusloesen(ergebnisNotAttached, true);
  }

  public void ergebnisBerechnenAusloesenOhneUebernahme(Ergebnis ergebnisNotAttached) {
    ergebnisBerechnenAusloesen(ergebnisNotAttached, false);
  }

  private void ergebnisBerechnenAusloesen(Ergebnis ergebnisNotAttached, boolean gueltigeEingaengeUebernehmen) {
    ergebnissequenznummerSetzenEvent.sendChangeEvent(); // Falls noch nicht geschehen...
    Ergebnis ergebnis = ergebnisDAO.attach(ergebnisNotAttached);
    ergebnis.setErgebnisStatus(ErgebnisStatus.ZU_BERECHNEN);

    if (gueltigeEingaengeUebernehmen) {
      ergebnis.setEingangssequenznummer(AbstractErgebnis.EINGANGSSEQUENZNUMMER_BERECHNUNG_UND_UEBERNAHME_AUTOMATISCH_ERZWUNGEN);
    }

    ergebnisberechnungAusloeserHook.ergebnisBerechnenAusloesen(ergebnis);

    LOG.info("ergebnisBerechnenAusloesen: " + ergebnis);
    zwischenergebnisBerechnenEvent.sendChangeEvent();
  }

  /**
   * Aktualisiert die Berechnung des Ergebnisses
   */
  @NotifyOnSuccess(PraesentationGenerierenEvent.class)
  public void aktualisiereErgebnis(ErgebnisPK ergebnisPK, String ausloeser,
                                   SitzberechnungsalgorithmusKonfiguration konfiguration) {
    Ergebnis ergebnis = ergebnisDAO.findByPrimaryKey(ergebnisPK);
    setzeZeitpunktUndAusloeser(ausloeser, ergebnis);

    // Gruppen un gebiete laden, um Hibernate Proxy-Meldung zu meiden
    politischeGruppeDAO.findByWahl(ergebnis.getWahl()).forEach(Gruppe::getSchluessel);
    wahldurchfuehrungsgebietDAO.findByWahl(ergebnis.getWahl()).forEach(Gebiet::getNummer);

    berechneErgebnis(ausloeser, konfiguration, ergebnis);

    // Provisorisch: Nach der Sitzberechnung ein anderes Ergebnis für die Hochrechnung erzeugen
    //    hochrechnungErgebnisberechnung.fuehreHochrechnungDurchWennMoeglich(ergebnis, ausloeser, konfiguration);
    //    vergleichsHochrechnungErgebnisberechnung.fuehreHochrechnungDurchWennMoeglich(ergebnis, ausloeser);

  }

  /**
   * Sitzberechnung
   */
  private void berechneErgebnis(String ausloeser, SitzberechnungsalgorithmusKonfiguration konfiguration,
                                Ergebnis ergebnis) {
    setzeZeitpunktUndAusloeser(ausloeser, ergebnis);
    sitzberechnung.berechneSitze(ergebnis, konfiguration);
  }

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

}
