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

import java.io.Serializable;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import javax.inject.Inject;

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

import de.ivu.elect.business.ergebnis.boundary.AbstractDHondtQuotientDAO;
import de.ivu.elect.business.ergebnis.boundary.AbstractErgebnisDAO;
import de.ivu.elect.business.ergebnis.boundary.AbstractErgebnisLogEintragDAO;
import de.ivu.elect.business.ergebnis.entity.Ergebnis;
import de.ivu.elect.business.ergebniskonflikt.boundary.AbstractErgebnisKonfliktDAO;
import de.ivu.elect.business.gebietsbaum.entity.Wahldurchfuehrungsgebiet;
import de.ivu.elect.business.sitzberechnung.control.SitzberechnungsalgorithmusFinder;
import de.ivu.elect.business.stimmeingabe.boundary.AbstractErgebnisGueltigerEingangDAO;
import de.ivu.elect.business.stimmeingabe.entity.ErgebnisGueltigerEingang;
import de.ivu.elect.business.wahl.entity.WahlParameterWerte;

public abstract class AbstractErgebnisDatenCreator implements Serializable {

  private static final long serialVersionUID = 1L;

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

  @Inject
  private transient SitzberechnungsalgorithmusFinder sitzberechnungsalgorithmusFinder;

  public AbstractErgebnisDatenCreator() {
    super();
  }

  public ErgebnisDaten createErgebnisDatenSitzberechnung(Ergebnis ergebnis) {
    return createErgebnisDaten(ergebnis, WahlParameterWerte.STIMMEN_LADEN_FUER_SITZBERECHNUNG);
  }

  public ErgebnisDaten createErgebnisDatenExporte(Ergebnis ergebnis) {
    return createErgebnisDaten(ergebnis, WahlParameterWerte.STIMMEN_LADEN_FUER_EXPORTE);
  }

  public ErgebnisDaten createErgebnisDatenPraesentation(Ergebnis ergebnis, Set<Wahldurchfuehrungsgebiet> auszuschliessendeGebiete) {
    return createErgebnisDaten(ergebnis, auszuschliessendeGebiete, WahlParameterWerte.STIMMEN_LADEN_FUER_PRAESENTATION);
  }

  protected ErgebnisDaten createErgebnisDaten(Ergebnis unattachedErgebnis, WahlParameterWerte wahlParameterWerte) {
    return createErgebnisDaten(unattachedErgebnis, Collections.emptySet(), wahlParameterWerte);
  }

  public ErgebnisDaten createErgebnisDaten(Ergebnis unattachedErgebnis, Set<Wahldurchfuehrungsgebiet> auszuschliessendeGebiete, WahlParameterWerte wahlParameterWerte) {
    LOG.info("Ergebnisdaten - stimmergebnisDaten");

    Ergebnis ergebnis = getErgebnisDAO().attach(unattachedErgebnis);
    StimmergebnisDaten stimmergebnisDaten = getStimmergebnisDatenCreator().createStimmergebnisDaten(unattachedErgebnis, auszuschliessendeGebiete,
        wahlParameterWerte);

    LOG.info("Ergebnisdaten - done");

    return new ErgebnisDaten(stimmergebnisDaten, () -> getErgebnisKonfliktDAO().findByErgebnisOrderById(ergebnis),
        () -> getErgebnisLogEintragDAO().findByErgebnisOrderByPosition(ergebnis),
        sitzberechnungsalgorithmusFinder.getSitzberechnungsalgorithmus(stimmergebnisDaten.getWahl()),
        () -> getDHondtQuotientDAO().findByErgebnis(ergebnis),
        () -> filter(getErgebnisGueltigerEingangDAO().findByErgebnis(ergebnis), auszuschliessendeGebiete));
  }

  private List<ErgebnisGueltigerEingang> filter(List<ErgebnisGueltigerEingang> gueltigeEingaenge, Set<Wahldurchfuehrungsgebiet> auszuschliessendeGebiete) {
    // ELECT-7529
    if (auszuschliessendeGebiete.isEmpty()) {
      return gueltigeEingaenge;
    }

    return gueltigeEingaenge.stream()
        .filter(e -> e.getWahldurchfuehrungsgebiet() != null /*ELECTKOM-1715*/ && !auszuschliessendeGebiete.contains(e.getWahldurchfuehrungsgebiet()))
        .collect(Collectors.toList());
  }

  protected abstract AbstractErgebnisGueltigerEingangDAO getErgebnisGueltigerEingangDAO();

  protected abstract AbstractDHondtQuotientDAO getDHondtQuotientDAO();

  protected abstract AbstractErgebnisLogEintragDAO getErgebnisLogEintragDAO();

  protected abstract AbstractErgebnisKonfliktDAO getErgebnisKonfliktDAO();

  protected abstract AbstractErgebnisDAO getErgebnisDAO();

  protected abstract AbstractStimmergebnisDatenCreator getStimmergebnisDatenCreator();

}
