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

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

import javax.inject.Inject;

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

import de.ivu.elect.business.aufgabe.boundary.AufgabeDAO;
import de.ivu.elect.business.ergebnis.boundary.AbstractErgebnisDAO;
import de.ivu.elect.business.ergebnis.boundary.AbstractGewaehltDAO;
import de.ivu.elect.business.ergebnis.boundary.AbstractSitzeDAO;
import de.ivu.elect.business.ergebnis.boundary.AbstractStimmenDAO;
import de.ivu.elect.business.ergebnis.entity.AbstractErgebnis;
import de.ivu.elect.business.ergebnis.entity.Ergebnis;
import de.ivu.elect.business.ergebnis.entity.Ergebnisart;
import de.ivu.elect.business.ergebnis.entity.Gewaehlt;
import de.ivu.elect.business.ergebnis.entity.ReadOnlyGruppenStimmen;
import de.ivu.elect.business.ergebnis.entity.Sitze;
import de.ivu.elect.business.ergebnis.entity.Stimmen;
import de.ivu.elect.business.fremdkandidatur.boundary.AbstractFremdkandidaturDAO;
import de.ivu.elect.business.gebietsbaum.boundary.AbstractGebietPolygonPointDAO;
import de.ivu.elect.business.gebietsbaum.boundary.AbstractGebietsgruppierungDAO;
import de.ivu.elect.business.gebietsbaum.boundary.AbstractWahldurchfuehrungsgebietDAO;
import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
import de.ivu.elect.business.gebietsbaum.entity.Wahldurchfuehrungsgebiet;
import de.ivu.elect.business.gruppe.boundary.AbstractGruppeDAO;
import de.ivu.elect.business.gruppe.boundary.AbstractPolitischeGruppeDAO;
import de.ivu.elect.business.gruppe.entity.Gruppe;
import de.ivu.elect.business.gruppe.entity.SystemGruppeEigenschaften;
import de.ivu.elect.business.gruppeimgebiet.boundary.AbstractGruppeImGebietDAO;
import de.ivu.elect.business.gruppeimgebiet.control.GruppeImGebietReihenfolgeVorsortierer;
import de.ivu.elect.business.internet.boundary.AbstractInternetSeiteDAO;
import de.ivu.elect.business.kandidatenportal.boundary.AbstractWvpWarnungDAO;
import de.ivu.elect.business.kandidatur.boundary.AbstractKandidaturDAO;
import de.ivu.elect.business.kandidatur.control.StimmzettelFormatter;
import de.ivu.elect.business.kandidatur.entity.AbstractKandidatur;
import de.ivu.elect.business.kandidatur.entity.Listenkandidatur;
import de.ivu.elect.business.niederschrift.boundary.AbstractNiederschriftDAO;
import de.ivu.elect.business.person.boundary.AbstractPersonenVerknuepfungDAO;
import de.ivu.elect.business.personenpruefung.boundary.AbstractPersonPruefungKonfigurationDAO;
import de.ivu.elect.business.pruefung.boundary.ZulassungPrueferFactory;
import de.ivu.elect.business.staat.boundary.AbstractStaatenGruppeDAO;
import de.ivu.elect.business.staat.entity.StaatenGruppeTyp;
import de.ivu.elect.business.stimmeingabe.boundary.AbstractErgebnisGueltigerEingangDAO;
import de.ivu.elect.business.stimmeingabe.boundary.AbstractErgebniseingangDAO;
import de.ivu.elect.business.stimmeingabe.boundary.AbstractStimmeingabePlausiKonfigurationDAO;
import de.ivu.elect.business.stimmeingabe.boundary.ErgebniseingangDAOHelper;
import de.ivu.elect.business.stimmeingabe.boundary.StimmeingabeErfassenDescriptionResolver;
import de.ivu.elect.business.stimmeingabe.entity.StimmenLadenArt;
import de.ivu.elect.business.stimmen.control.ErgebnisStimmenFilter;
import de.ivu.elect.business.stimmzettel.boundary.AbstractStimmzettelDAO;
import de.ivu.elect.business.stimmzettel.boundary.AbstractStimmzettelKopfdatumDAO;
import de.ivu.elect.business.stimmzettel.boundary.EntityAufStimmzettelDatenStimmzettelPositionBerechnung;
import de.ivu.elect.business.veranstaltung.boundary.AbstractVeranstaltungDAO;
import de.ivu.elect.business.wahl.boundary.AbstractWahldurchfuehrungDAO;
import de.ivu.elect.business.wahl.control.Loader;
import de.ivu.elect.business.wahl.entity.AbstractWahlParameterLoader;
import de.ivu.elect.business.wahl.entity.Wahl;
import de.ivu.elect.business.wahl.entity.WahlParameterWerte;
import de.ivu.elect.business.wahl.entity.Wahldurchfuehrung;
import de.ivu.elect.business.wahlausschuss.boundary.AbstractWahlausschussEntschaedigungDAO;
import de.ivu.elect.business.wahlausschuss.boundary.AbstractWahlausschussmitgliedDAO;
import de.ivu.elect.business.wahlbeteiligung.boundary.AbstractWahlbeteiligungAuswertungDAO;
import de.ivu.elect.business.wahlbeteiligung.boundary.AbstractWahlbeteiligungVerlaufDAO;
import de.ivu.elect.business.wahlrechtspruefung.boundary.AbstractPruefkriteriumDAO;
import de.ivu.elect.business.wahlrechtspruefung.boundary.AbstractWahlrechtspruefungDAO;
import de.ivu.elect.business.wahlvorschlag.boundary.AbstractReferendumOptionDAO;
import de.ivu.elect.business.wahlvorschlag.boundary.AbstractWahlvorschlagDAO;
import de.ivu.elect.business.wahlvorschlag.entity.ReferendumOption;
import de.ivu.elect.business.wahlvorschlagsperson.boundary.AbstractWahlvorschlagsPersonDAO;


import static java.util.Arrays.asList;
import static java.util.stream.Collectors.toList;

public abstract class AbstractStimmergebnisDatenCreator implements Serializable {

  private static final long serialVersionUID = 1L;

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

  @Inject
  private transient GruppeImGebietReihenfolgeVorsortierer gruppeImGebietReihenfolgeVorsortierer;

  @Inject
  private transient StimmzettelFormatter stimmzettelFormatter;

  @Inject
  private transient EntityAufStimmzettelDatenStimmzettelPositionBerechnung entityAufStimmzettelDatenStimmzettelPositionBerechnung;

  @Inject
  private transient ErgebnisStimmenFilter ergebnisStimmenFilter;

  @Inject
  private transient SystemGruppeEigenschaften systemGruppeEigenschaften;

  @Inject
  private transient StimmenKumulierer stimmenKumulierer;

  @Inject
  private transient StimmeingabeErfassenDescriptionResolver stimmeingabeErfassenDescriptionResolver;

  @Inject
  private transient AufgabeDAO aufgabeDAO;

  public AbstractStimmergebnisDatenCreator() {
    super();
  }

  public StimmergebnisDaten createErgebnisDatenExporte(Ergebnis unattachedErgebnis) {
    return createErgebnisDaten(unattachedErgebnis, WahlParameterWerte.STIMMEN_LADEN_FUER_EXPORTE);
  }

  public StimmergebnisDaten createErgebnisDatenSitzberechnung(Ergebnis unattachedErgebnis) {
    return createErgebnisDaten(unattachedErgebnis, WahlParameterWerte.STIMMEN_LADEN_FUER_SITZBERECHNUNG);
  }

  public StimmergebnisDaten createErgebnisDatenPraesentation(Ergebnis unattachedErgebnis) {
    return createErgebnisDaten(unattachedErgebnis, WahlParameterWerte.STIMMEN_LADEN_FUER_PRAESENTATION);
  }

  private StimmergebnisDaten createErgebnisDaten(Ergebnis unattachedErgebnis, WahlParameterWerte wahlParameterWerte) {
    return createStimmergebnisDaten(unattachedErgebnis, Collections.emptySet(), wahlParameterWerte);
  }

  public StimmergebnisDaten createStimmergebnisDaten(Ergebnis unattachedErgebnis, Set<Wahldurchfuehrungsgebiet> auszuschliessendeGebiete, WahlParameterWerte wahlParameterWerte) {
    Ergebnis ergebnis = getErgebnisDAO().attach(unattachedErgebnis);
    return createStimmergebnisDaten(unattachedErgebnis, auszuschliessendeGebiete, getStimmenLadenArt(wahlParameterWerte, ergebnis));
  }

  public StimmergebnisDaten createStimmergebnisDaten(Ergebnis unattachedErgebnis, Set<Wahldurchfuehrungsgebiet> auszuschliessendeGebiete, StimmenLadenArt stimmenLadenArt) {
    LOG.info("Stimmergebnisdaten");
    Ergebnis ergebnis = getErgebnisDAO().attach(unattachedErgebnis);
    Wahldurchfuehrung wahl = getWahldurchfuehrungDAO().findByPrimaryKey(ergebnis.getWahl().getPk());

    Ergebnis ergebnisVorperiode = AbstractErgebnis.getErgebnisZurWahlNachErgebnisart(Ergebnisart.VORPERIODE, wahl, getErgebnisDAO());

    LOG.info("Stimmergebnisdaten - stimmenLadenArt: " + stimmenLadenArt);

    LOG.info("Stimmergebnisdaten - auszuschliessendeGebiete.size(): " + auszuschliessendeGebiete.size());
    StimmenFilter stimmenFilter = getStimmenFilterFuerZubetrachtendeGebiete(auszuschliessendeGebiete);

    // Eager loading von gebieten und Gruppen um Hibernate Proxy-Meldung zu meiden
    List<Gruppe> gruppen = ladeGruppen(wahl);
    Loader<Gruppe> gruppenLoader = () -> gruppen;

    List<Wahldurchfuehrungsgebiet> gebiete = getWahldurchfuehrungsgebietDAO().findByWahl(wahl);
    Loader<Wahldurchfuehrungsgebiet> gebietLoader = () -> gebiete;

    StimmenPool stimmenPool;

    Map<Long, ReferendumOption> refrendumOptionNachId = getReferendumOptionDAO().findByWahl(wahl).stream()
        .collect(Collectors.toMap(ReferendumOption::getId, Function.identity()));

    final Map<Long, Gebiet> gebietNachId;
    final Map<Long, Gruppe> gruppeNachId;

    if (stimmenLadenArt.isLazy()) {
      stimmenPool = StimmenPool.createStimmenPool(getStimmenDAO(), ergebnis, stimmenFilter);
      gebietNachId = new HashMap<>();
      gruppeNachId = new HashMap<>();
    } else if (stimmenLadenArt.isNoEntity()) {
      gebietNachId = gebiete.stream().collect(Collectors.toMap(Gebiet::getId, Function.identity()));
      gruppeNachId = gruppen.stream().collect(Collectors.toMap(Gruppe::getId, Function.identity()));
      Map<Long, Listenkandidatur> listenkandidaturMap = ladeListenkandidaturen(wahl).stream()
          .collect(Collectors.toMap(Listenkandidatur::getId, Function.identity()));

      if (stimmenLadenArt.isNoEntityEager()) {
        List<Stimmen> stimmenNoEntity = ladeStimmenNichtAlsEntityMitListenkandidaturen(ergebnis, gebietNachId, gruppeNachId, refrendumOptionNachId,
            listenkandidaturMap);
        stimmenPool = StimmenPool.createStimmenPoolFuerAlleStimmen(stimmenNoEntity, stimmenFilter);
      } else {
        List<Stimmen> stimmenNoEntity = ladeStimmenNichtAlsEntityOhneListenkandidaturen(ergebnis, gebietNachId, gruppeNachId, refrendumOptionNachId);
        stimmenPool = StimmenPool.createStimmenPoolFuerAlleStimmenOhneListenkandidaturStimmen(stimmenNoEntity,
            e2 -> ladeStimmenListenkandidaturenNoEntity(e2, gebietNachId, gruppeNachId, refrendumOptionNachId, listenkandidaturMap), ergebnis, stimmenFilter);
      }
    } else {
      gebietNachId = new HashMap<>();
      gruppeNachId = new HashMap<>();
      stimmenPool = StimmenPool.createStimmenPoolFuerAlleStimmen(getStimmenDAO().findByErgebnisNoCache(ergebnis), stimmenFilter);
    }

    LOG.info("Stimmergebnisdaten - done");

    return new StimmergebnisDaten(wahl,
        gebietLoader,
        gruppenLoader,
        () -> getGruppeImGebietDAO().findByWahl(wahl),
        () -> getWahlvorschlagDAO().findByWahl(wahl),
        () -> getKandidaturDAO().findByWahl(wahl)/* ELECTKOM-117 */.stream().filter(k -> k.getWahlvorschlag() != null).collect(toList()),
        () -> getPersonenVerknuepfungDAO().findByWahl(wahl),
        ergebnisart -> getStimmen(wahl, ergebnisart, stimmenLadenArt, gebietNachId, gruppeNachId, refrendumOptionNachId),
        ergebnisart -> getGewaehlte(wahl, ergebnisart),
        ergebnisart -> getSitze(wahl, ergebnisart),
        () -> getStaatenGruppeDAO().findByWahlAndStaatenGruppeTyp(wahl, asList(StaatenGruppeTyp.values())),
        () -> getGebietsgruppierungDAO().findByWahl(wahl),
        () -> getWahlvorschlagsPersonDAO().findByWahlOhneUnterstuetzer(wahl),
        () -> getWahlausschussmitgliedDAO().findByWahl(wahl),
        () -> getWahlausschussEntschaedigungDAO().findByWahl(wahl), ergebnis, ergebnisVorperiode, stimmenPool, systemGruppeEigenschaften, stimmenKumulierer,
        getWahlParameterLoader(),
        getZulassungPrueferFactory().createZulassungPruefer(),
        stimmzettelFormatter,
        entityAufStimmzettelDatenStimmzettelPositionBerechnung,
        gruppeImGebietReihenfolgeVorsortierer,
        () -> getGebietPolygonPointDAO().findByWahl(wahl),
        () -> getFremdkandidaturDAO().findByWahl(wahl), () -> getInternbetSeiteDAO().findByWahl(wahl),
        () -> ladeErfassteGebiete(ergebnis, auszuschliessendeGebiete), ergebnisStimmenFilter,
        ergebniseingangsart -> ErgebniseingangDAOHelper.from(getErgebniseingangDAO())
            .findByNeuesteEingaengeByWahlAndErgebniseingangsart(wahl, ergebniseingangsart),
        e -> getErgebnisGueltigerEingangDAO().findByWahlAndErgebniseingangsart(wahl, e),
        () -> getWahlbeteiligungVerlaufDAO().findByWahl(wahl),
        () -> getWahlbeteiligungAuswertungDAO().findByWahl(wahl), stimmeingabeErfassenDescriptionResolver,
        () -> getStimmeingabePlausiKonfigurationDAO().findByWahl(wahl),
        () -> getVeranstaltungDAO().findByWahlOrderByBeginn(wahl),
        () -> getStimmzettelDAO().findByWahl(wahl),
        () -> getStimmzettelKopfdatumDAO().findByWahl(wahl),
        () -> getPruefkriteriumDAO().findByWahlOrderByPosition(wahl),
        () -> getWahlrechtspruefungDAO().findByWahl(wahl),
        () -> getWvpWarnungDAO().findByWahl(wahl),
        () -> getPersonPruefungKonfigurationDAO().findByWahl(wahl),
        () -> getNiederschriftDAO().findByWahl(wahl),
        () -> aufgabeDAO.findByWahl(wahl));
  }

  protected List<Listenkandidatur> ladeListenkandidaturen(Wahldurchfuehrung wahl) {
    return getKandidaturDAO().findByWahl(wahl).stream().filter(AbstractKandidatur::isListenkandidatur)
        .map(kandidatur -> kandidatur.castTo(Listenkandidatur.class).getEntity())
        .collect(Collectors.toCollection(() -> new ArrayList<>(1000)));
  }

  protected abstract AbstractVeranstaltungDAO getVeranstaltungDAO();

  protected abstract AbstractWahlbeteiligungVerlaufDAO getWahlbeteiligungVerlaufDAO();

  protected abstract AbstractWahlbeteiligungAuswertungDAO getWahlbeteiligungAuswertungDAO();

  protected abstract AbstractStimmeingabePlausiKonfigurationDAO getStimmeingabePlausiKonfigurationDAO();

  protected abstract AbstractPruefkriteriumDAO getPruefkriteriumDAO();

  protected abstract AbstractWahlrechtspruefungDAO getWahlrechtspruefungDAO();

  protected abstract AbstractWvpWarnungDAO getWvpWarnungDAO();

  protected abstract AbstractPersonPruefungKonfigurationDAO getPersonPruefungKonfigurationDAO();

  // ELECT-2229
  private StimmenFilter getStimmenFilterFuerZubetrachtendeGebiete(final Set<Wahldurchfuehrungsgebiet> auszuschliessendeErfassungsgebiete) {
    // ELECT-7529
    if (auszuschliessendeErfassungsgebiete.isEmpty()) {
      return LasseAllesDurchStimmenFilter.INSTANCE;
    }
    return stimmen -> !auszuschliessendeErfassungsgebiete.contains(stimmen.getGebiet());
  }

  private StimmenLadenArt getStimmenLadenArt(WahlParameterWerte wahlParameterWerte, Ergebnis ergebnis) {
    StimmenLadenArt stimmenLadenArt = getWahlParameterLoader().resolveEnumValue(ergebnis.getWahl(), wahlParameterWerte, StimmenLadenArt.class);
    if (stimmenLadenArt == null) {
      // ELECT-7413
      LOG.warn("Fehlende Einstellung für stimmenLadenArt");
      stimmenLadenArt = StimmenLadenArt.EAGER;
    }
    return stimmenLadenArt;
  }

  private List<Stimmen> ladeStimmenNichtAlsEntityOhneListenkandidaturen(Ergebnis ergebnis, Map<Long, Gebiet> gebietNachId, Map<Long, Gruppe> gruppeNachId,
                                                                        Map<Long, ReferendumOption> refrendumOptionNachId) {
    LOG.info("ladeStimmenNichtAlsEntityOhneListenkandidaturen: " + ergebnis.getErgebnisart());
    Map<Long, Ergebnis> ergebnisNachId = new HashMap<>();
    ergebnisNachId.put(ergebnis.getId(), ergebnis);
    List<ReadOnlyGruppenStimmen> stimmen = getStimmenDAO().findByErgebnisOhneListenkandidaturenReadOnlyNoCache(ergebnis);
    LOG.info("ladeStimmenNichtAlsEntityOhneListenkandidaturen - finder done " + stimmen.size());
    stimmen.forEach(s -> s.init(ergebnisNachId, gebietNachId, gruppeNachId, refrendumOptionNachId));
    LOG.info("ladeStimmenNichtAlsEntityOhneListenkandidaturen - init done");
    List result = stimmen;
    return result;
  }

  private List<Stimmen> ladeStimmenNichtAlsEntityMitListenkandidaturen(Ergebnis ergebnis, Map<Long, Gebiet> gebietNachId, Map<Long, Gruppe> gruppeNachId,
                                                                       Map<Long, ReferendumOption> refrendumOptionNachId, Map<Long, Listenkandidatur> listenkandidaturenNachId) {
    LOG.info("ladeStimmenNichtAlsEntityMitListenkandidaturen: " + ergebnis.getErgebnisart());
    Map<Long, Ergebnis> ergebnisNachId = new HashMap<>();
    ergebnisNachId.put(ergebnis.getId(), ergebnis);
    List<ReadOnlyGruppenStimmen> stimmen = getStimmenDAO().findByErgebnisReadOnlyNoCache(ergebnis);
    LOG.info("ladeStimmenNichtAlsEntityMitListenkandidaturen - finder done " + stimmen.size());
    stimmen.forEach(s -> s.init(ergebnisNachId, gebietNachId, gruppeNachId, refrendumOptionNachId, listenkandidaturenNachId));
    LOG.info("ladeStimmenNichtAlsEntityMitListenkandidaturen - init done");
    List result = stimmen;
    return result;
  }

  private List<Stimmen> ladeStimmenListenkandidaturenNoEntity(Ergebnis ergebnis, Map<Long, Gebiet> gebietNachId, Map<Long, Gruppe> gruppeNachId, Map<Long, ReferendumOption> refrendumOptionNachId,
                                                              Map<Long, Listenkandidatur> listenkandidaturenNachId) {
    LOG.info("ladeStimmenListenkandidaturenNoEntity: " + ergebnis.getErgebnisart());
    Map<Long, Ergebnis> ergebnisNachId = new HashMap<>();
    ergebnisNachId.put(ergebnis.getId(), ergebnis);
    List<ReadOnlyGruppenStimmen> stimmen = getStimmenDAO().findByErgebnisListenkandidaturstimmenReadOnly(ergebnis);
    LOG.info("ladeStimmenListenkandidaturenNoEntity - finder done " + stimmen.size());
    stimmen.forEach(s -> s.init(ergebnisNachId, gebietNachId, gruppeNachId, refrendumOptionNachId, listenkandidaturenNachId));
    LOG.info("ladeStimmenListenkandidaturenNoEntity - init done");
    List result = stimmen;
    return result;
  }

  protected abstract AbstractGebietPolygonPointDAO getGebietPolygonPointDAO();

  protected abstract AbstractFremdkandidaturDAO getFremdkandidaturDAO();

  protected abstract AbstractInternetSeiteDAO getInternbetSeiteDAO();

  protected abstract ZulassungPrueferFactory getZulassungPrueferFactory();

  protected abstract AbstractWahlausschussEntschaedigungDAO getWahlausschussEntschaedigungDAO();

  protected abstract AbstractWahlausschussmitgliedDAO getWahlausschussmitgliedDAO();

  protected abstract AbstractStaatenGruppeDAO getStaatenGruppeDAO();

  protected abstract AbstractGebietsgruppierungDAO getGebietsgruppierungDAO();

  protected abstract AbstractPersonenVerknuepfungDAO getPersonenVerknuepfungDAO();

  protected abstract AbstractKandidaturDAO getKandidaturDAO();

  protected abstract AbstractWahlvorschlagDAO getWahlvorschlagDAO();

  protected abstract AbstractWahlvorschlagsPersonDAO getWahlvorschlagsPersonDAO();

  protected abstract AbstractGruppeImGebietDAO getGruppeImGebietDAO();

  protected abstract AbstractWahldurchfuehrungsgebietDAO getWahldurchfuehrungsgebietDAO();

  protected abstract AbstractStimmenDAO getStimmenDAO();

  protected abstract AbstractGewaehltDAO getGewaehltDAO();

  protected abstract AbstractSitzeDAO getSitzeDAO();

  protected abstract AbstractWahldurchfuehrungDAO getWahldurchfuehrungDAO();

  protected abstract AbstractErgebnisDAO getErgebnisDAO();

  protected abstract AbstractErgebniseingangDAO getErgebniseingangDAO();

  protected abstract AbstractReferendumOptionDAO getReferendumOptionDAO();

  protected abstract AbstractStimmzettelDAO getStimmzettelDAO();

  protected abstract AbstractStimmzettelKopfdatumDAO getStimmzettelKopfdatumDAO();

  protected abstract AbstractWahlParameterLoader getWahlParameterLoader();

  protected abstract AbstractNiederschriftDAO getNiederschriftDAO();

  private List<Stimmen> getStimmen(Wahl wahl, Ergebnisart ergebnisart, StimmenLadenArt stimmenLadenArt, Map<Long, Gebiet> gebietNachId, Map<Long, Gruppe> gruppeNachId,
                                   Map<Long, ReferendumOption> refrendumOptionNachId) {
    Ergebnis ergebnis = getErgebnis(wahl, ergebnisart);
    if (ergebnis == null) {
      return Collections.emptyList();
    }
    if (stimmenLadenArt.isNoEntity()) {
      return ladeStimmenNichtAlsEntityOhneListenkandidaturen(ergebnis, gebietNachId, gruppeNachId, refrendumOptionNachId);
    }

    return getStimmenDAO().findByErgebnisNoCache(ergebnis);
  }

  private Ergebnis getErgebnis(Wahl wahl, Ergebnisart ergebnisart) {
    return AbstractErgebnis.getErgebnisZurWahlNachErgebnisart(ergebnisart, wahl, getErgebnisDAO());
  }

  private List<Gebiet> ladeErfassteGebiete(Ergebnis ergebnis, Set<Wahldurchfuehrungsgebiet> auszuschliessendeGebiete) {
    return getErgebnisGueltigerEingangDAO().findByErgebnis(ergebnis).stream().map(s -> s.getErgebniseingang().getGebiet())
        .filter(g -> !auszuschliessendeGebiete.contains(g)) // ELECT-7529
        .collect(toList());
  }

  protected abstract AbstractErgebnisGueltigerEingangDAO getErgebnisGueltigerEingangDAO();

  private List<Gruppe> ladeGruppen(Wahldurchfuehrung wahl) {
    // Vermeide: HHH000179: Narrowing proxy to class de.ivu.elect.business.gruppe.entity.PolitischeGruppe - this operation breaks ==
    Set<Gruppe> gruppenSet = new HashSet<>();
    gruppenSet.addAll(getPolitischeGruppeDAO().findByWahl(wahl));
    gruppenSet.addAll(getGruppeDAO().findByWahl(wahl));
    return new ArrayList<>(gruppenSet);

  }

  protected abstract AbstractGruppeDAO getGruppeDAO();

  protected abstract AbstractPolitischeGruppeDAO getPolitischeGruppeDAO();

  private List<Gewaehlt> getGewaehlte(Wahl wahl, Ergebnisart ergebnisart) {
    Ergebnis ergebnis = getErgebnis(wahl, ergebnisart);
    if (ergebnis == null) {
      return new ArrayList<>();
    } else {
      return getGewaehltDAO().findByErgebnis(ergebnis);
    }
  }

  private List<Sitze> getSitze(Wahl wahl, Ergebnisart ergebnisart) {
    Ergebnis ergebnis = getErgebnis(wahl, ergebnisart);

    if (ergebnis == null) {
      return new ArrayList<>();
    } else {
      return getSitzeDAO().findByErgebnis(ergebnis);
    }
  }
}
