
package de.ivu.elect.business.stimmeingabe.control.plausi;

import static de.ivu.elect.jee.entity.EntityUtil.castTo;
import static java.util.Arrays.asList;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

import javax.annotation.Nullable;
import javax.inject.Inject;

import de.ivu.elect.business.ergebnis.boundary.ErgebnisDAO;
import de.ivu.elect.business.ergebnis.boundary.StimmenDAO;
import de.ivu.elect.business.ergebnis.entity.Stimmen;
import de.ivu.elect.business.ergebnis.entity.Stimmenart;
import de.ivu.elect.business.gebietsbaum.boundary.WahldurchfuehrungsgebietDAO;
import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
import de.ivu.elect.business.gebietsbaum.entity.Gebietsart;
import de.ivu.elect.business.gebietsbaum.entity.Wahldurchfuehrungsgebiet;
import de.ivu.elect.business.gruppe.entity.SystemGruppe;
import de.ivu.elect.business.gruppe.entity.SystemGruppeEigenschaften;
import de.ivu.elect.business.gruppeimgebiet.entity.GruppeImGebiet;
import de.ivu.elect.business.stimmeingabe.boundary.ErgebniseingangDAO;
import de.ivu.elect.business.stimmeingabe.boundary.StimmeingabePlausiKonfigurationDAO;
import de.ivu.elect.business.stimmeingabe.control.DaoStimmeingabeKopfzahlKonfigurationContext;
import de.ivu.elect.business.stimmeingabe.control.StimmeingabeKopfzahlKonfiguration;
import de.ivu.elect.business.stimmeingabe.control.StimmeingabeKopfzahlKonfigurationContext;
import de.ivu.elect.business.stimmeingabe.control.StimmeingabePlausiBuilder;
import de.ivu.elect.business.stimmeingabe.entity.Ergebniseingang;
import de.ivu.elect.business.stimmeingabe.entity.Ergebniseingangsart;
import de.ivu.elect.business.stimmeingabe.entity.Stimmeingabe;
import de.ivu.elect.business.stimmeingabe.entity.StimmeingabePlausiFehlerart;
import de.ivu.elect.business.stimmeingabe.entity.StimmeingabePlausiKonfiguration;
import de.ivu.elect.business.stimmeingabe.entity.StimmeingabePlausiKonfigurationCustomData;
import de.ivu.elect.business.stimmeingabe.entity.StimmeingabePlausiTyp;
import de.ivu.elect.business.stimmeingabe.entity.ZwischensummeEingabe;
import de.ivu.elect.business.wahl.entity.Wahl;
import de.ivu.elect.business.wahl.entity.WahlParameterLoader;
import de.ivu.elect.business.wahl.entity.WahlartErgebnisart;
import de.ivu.elect.business.wahlvorschlag.boundary.AbstractDirektkandidatDAO;
import de.ivu.elect.business.wahlvorschlag.boundary.AbstractListeDAO;
import de.ivu.elect.jee.entity.EntityUtil;
import de.ivu.elect.jee.entity.tk.EntityTK;
import de.ivu.elect.jee.faces.Messages;
import de.ivu.elect.util.MapBuilder;

/**
 * @deprecated NICHT FÜR NEUE PLAUSI BUILDER NUTZEN!!
 *
 *             Durchläuft alle Stimmeingaben, schaut dann, welche Plausis zu jeder Stimmeingabe passen. Probleme: Wartung und Verständlichkeit schwierig; Vollständigkeit nicht garantiert. Besser: die
 *             Modelle nutzen, die jetzt am Ergebniseingang hängen.
 */
@Deprecated
public abstract class AbstractIterierenderStimmeingabePlausiBuilder implements StimmeingabePlausiBuilder, Serializable {

  private static final long serialVersionUID = 1L;

  @Inject
  protected transient ErgebniseingangDAO ergebniseingangDAO;

  @Inject
  protected transient ErgebnisDAO ergebnisDAO;

  @Inject
  protected transient StimmenDAO stimmenDAO;

  @Inject
  protected transient StimmeingabePlausiKonfigurationDAO stimmeingabePlausiKonfigurationDAO;

  @Inject
  protected transient StimmeingabeKopfzahlKonfiguration stimmeingabeKopfzahlKonfiguration;

  @Inject
  protected transient WahlParameterLoader wahlParameterLoader;

  @Inject
  protected transient SystemGruppeEigenschaften systemGruppeEigenschaften;

  @Inject
  protected transient Messages messages;

  @Inject
  protected transient WahldurchfuehrungsgebietDAO wahldurchfuehrungsgebietDAO;

  public AbstractIterierenderStimmeingabePlausiBuilder() {
    super();
  }

  @Override
  public void checkPreConditions(Gebiet gebiet, WahlartErgebnisart ergebnisart, AbstractDirektkandidatDAO direktkandidatDAO, AbstractListeDAO listeDAO) {
    // ELECT-8960, to be overridden...
  }

  protected StimmeingabePlausiKonfiguration findStimmeingabePlausiKonfigurationOrDefaults(Stimmeingabe stimmeingabe, StimmeingabePlausiTyp typ, StimmeingabePlausiFehlerart fehlerart) {
    return findStimmeingabePlausiKonfigurationOrDefaults(stimmeingabe, typ, fehlerart, typ.newCustomDataInstance());
  }

  protected StimmeingabePlausiKonfiguration findStimmeingabePlausiKonfigurationOrDefaults(Stimmeingabe stimmeingabe, StimmeingabePlausiTyp typ, StimmeingabePlausiFehlerart fehlerart,
      StimmeingabePlausiKonfigurationCustomData customData) {
    Ergebniseingang ergebniseingang = stimmeingabe.getErgebniseingang();

    return getStimmeingabePlausiDefinitions(ergebniseingang).findStimmeingabePlausiKonfigurationOrLegacyDefaults(getStimmeingabePlausiDefinitionsContext(), getStimmeingabeKopfzahlKonfiguration(),
        ergebniseingang, typ, fehlerart, customData);
  }

  protected abstract AbstractStimmeingabePlausiDefinitions getStimmeingabePlausiDefinitions(Ergebniseingang ergebniseingang);

  protected StimmeingabePlausiDefinitionsContext getStimmeingabePlausiDefinitionsContext() {
    return new StimmeingabePlausiDefinitionsContext(getStimmeingabePlausiKonfigurationDAO(), getWahlParameterLoader());
  }

  @Override
  public void fillStimmeingabePlausis(Ergebniseingang ergebniseingang) {
    Set<Stimmeingabe> stimmeingabenZuPlausibilisieren = new HashSet<>(ergebniseingang.getStimmeingaben());

    Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet = ergebniseingang.getWahldurchfuehrungsgebiet();
    Ergebniseingangsart ergebniseingangsart = ergebniseingang.getErgebniseingangsart();

    Map<Stimmenart, Long> anzahlWahlberechtigteAmGebietPerStimmenart = loadAWerte(ergebniseingang);
    Map<EntityTK, Stimmeingabe> stimmeingabeVorlaeufigPerTK = loadVorlaeufigeStimmeingabenPerTKWennAktuellerErgebniseingangIstEndgueltig(ergebniseingang);

    Map<SystemGruppe, List<Stimmeingabe>> stimmeingabenPerSystemGruppe = MapBuilder.newMapMultipleEntriesFrom(ergebniseingang.getStimmeingaben(),
        Stimmeingabe.getStimmeingabeToSystemGruppeFunction(getSystemGruppeEigenschaften()));

    Map<GruppeImGebiet, List<Stimmeingabe>> stimmeingabenPerGruppeImGebiet = MapBuilder.newMapMultipleEntriesFrom(ergebniseingang.getStimmeingaben(),
        Stimmeingabe.getStimmeingabeToGruppeImGebietFunction(getSystemGruppeEigenschaften()));

    fillStimmeingabePlausis(ergebniseingang, wahldurchfuehrungsgebiet, ergebniseingangsart, stimmeingabenZuPlausibilisieren, stimmeingabenPerSystemGruppe, stimmeingabenPerGruppeImGebiet,
        anzahlWahlberechtigteAmGebietPerStimmenart, stimmeingabeVorlaeufigPerTK);
  }

  protected void fillStimmeingabePlausis(Ergebniseingang ergebniseingang, Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, Ergebniseingangsart ergebniseingangsart,
      Set<Stimmeingabe> stimmeingabenZuPlausibilisieren, Map<SystemGruppe, List<Stimmeingabe>> stimmeingabenPerSystemGruppe, Map<GruppeImGebiet, List<Stimmeingabe>> stimmeingabenPerGruppeImGebiet,
      Map<Stimmenart, Long> anzahlWahlberechtigteAmGebietPerStimmenart, Map<EntityTK, Stimmeingabe> stimmeingabeVorlaeufigPerTK) {
    fillSystemGruppeStimmeingabePlausis(wahldurchfuehrungsgebiet, ergebniseingangsart, stimmeingabenZuPlausibilisieren, stimmeingabenPerSystemGruppe, stimmeingabenPerGruppeImGebiet,
        anzahlWahlberechtigteAmGebietPerStimmenart, stimmeingabeVorlaeufigPerTK);

    fillNichtSystemGruppeStimmeingabePlausis(wahldurchfuehrungsgebiet, stimmeingabenZuPlausibilisieren, stimmeingabenPerGruppeImGebiet, stimmeingabenPerSystemGruppe, ergebniseingangsart,
        stimmeingabeVorlaeufigPerTK);

    logStimmeingabePlausis(ergebniseingang.getStimmeingaben());
  }

  protected abstract void fillWahlSpezifischeSystemGruppeStimmeingabePlausis(Stimmeingabe stimmeingabe, Map<SystemGruppe, List<Stimmeingabe>> stimmeingabenPerSystemGruppe,
      Map<GruppeImGebiet, List<Stimmeingabe>> stimmeingabenPerGruppeImGebiet, Map<GruppeImGebiet, Stimmeingabe> gueltigeListenstimmenAllerParteien, Gebiet gebiet, Gebietsart gebietsart,
      Ergebniseingangsart ergebniseingangsart);

  protected abstract void fillWahlSpezifischeNichtSystemGruppeStimmeingabePlausis(Stimmeingabe stimmeingabe, List<Stimmeingabe> stimmeingabenFuerGruppeImGebiet,
      Ergebniseingangsart ergebniseingangsart);

  private void fillSystemGruppeStimmeingabePlausis(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, Ergebniseingangsart ergebniseingangsart, Set<Stimmeingabe> stimmeingabenZuPlausibilisieren,
      Map<SystemGruppe, List<Stimmeingabe>> stimmeingabenPerSystemGruppe, Map<GruppeImGebiet, List<Stimmeingabe>> stimmeingabenPerGruppeImGebiet,
      Map<Stimmenart, Long> anzahlWahlberechtigteAmGebietPerStimmenart, Map<EntityTK, Stimmeingabe> stimmeingabeVorlaeufigPerTK) {
    Map<Stimmenart, Map<GruppeImGebiet, Stimmeingabe>> gueltigeStimmenAllerParteienProStimmenart = findGueltigeStimmenAllerParteienProStimmenart(stimmeingabenPerGruppeImGebiet);

    Gebietsart gebietsart = wahldurchfuehrungsgebiet.getGebietsart();

    for (List<Stimmeingabe> stimmeingabes : stimmeingabenPerSystemGruppe.values()) {
      for (Stimmeingabe stimmeingabe : stimmeingabes) {
        // Checken ob die Stimmeingabe plausis haben soll.
        if (stimmeingabenZuPlausibilisieren.contains(stimmeingabe)) {

          fillSystemGruppeStimmeingabeAllgemeinePlausis(stimmeingabe, ergebniseingangsart, stimmeingabeVorlaeufigPerTK);

          fillWaehlerAllgemeinePlausis(wahldurchfuehrungsgebiet, ergebniseingangsart, stimmeingabe, stimmeingabenPerSystemGruppe);
          fillWaehlerFuerNichtBriefwahlPlausis(stimmeingabe, stimmeingabenPerSystemGruppe, ergebniseingangsart, wahldurchfuehrungsgebiet, gebietsart);

          // ELECTMUC-853
          if (!isSonderstimmbezirk(wahldurchfuehrungsgebiet)) {
            fillWahlberechtigteFuerNichtBriefwahlPlausis(stimmeingabe, wahldurchfuehrungsgebiet, ergebniseingangsart, gebietsart, stimmeingabenPerSystemGruppe,
                anzahlWahlberechtigteAmGebietPerStimmenart);
          }

          fillGueltigePlausis(stimmeingabe, stimmeingabenPerSystemGruppe, gueltigeStimmenAllerParteienProStimmenart, wahldurchfuehrungsgebiet.getWahl());

          fillWahlSpezifischeSystemGruppeStimmeingabePlausis(stimmeingabe, stimmeingabenPerSystemGruppe, stimmeingabenPerGruppeImGebiet,
              gueltigeStimmenAllerParteienProStimmenart.get(Stimmenart.LISTENSTIMME), wahldurchfuehrungsgebiet, gebietsart, ergebniseingangsart);

          // ELECTMUC-853
          if (isSonderstimmbezirk(wahldurchfuehrungsgebiet)) {
            fillSonderstimmbezirkPlausis(stimmeingabe, ergebniseingangsart);
          }
        }
      }
    }
  }

  // ELECTMUC-853
  private void fillSonderstimmbezirkPlausis(Stimmeingabe stimmeingabe, Ergebniseingangsart ergebniseingangsart) {
    if (isStimmeingabeWahlberechtigte(stimmeingabe)) {
      fillSonderstimmbezirkWahlberechtigtePlausis(stimmeingabe);
    }

    if (isStimmeingabeWaehler(stimmeingabe)) {
      fillSonderstimmbezirkWaehlerPlausis(stimmeingabe);
    }
  }

  // Cf. AbstractKopfzahlPlausiBuilder#fillSonderstimmbezirkWahlberechtigtePlausis!
  // Cf. https://confluence.ivu.de/display/ELECTMUC/Wahl-Glossar#Wahl-Glossar-SonderwahlbezirkSonderwahlbezirk!
  private void fillSonderstimmbezirkWahlberechtigtePlausis(Stimmeingabe stimmeingabeWahlberechtigte) {
    Stimmenart stimmenart = stimmeingabeWahlberechtigte.getStimmenart();

    if (stimmenart.equals(Stimmenart.OHNE_WAHLSCHEIN)) {
      stimmeingabeWahlberechtigte.addStimmeingabePlausi(new StimmeingabeAnzahlGleichAnzahlPlausi(StimmeingabeAnzahlReference.SYSTEM_GRUPPE_WAHLBERECHTIGTE_OHNE_WAHLSCHEIN,
          findStimmeingabePlausiKonfigurationOrDefaults(stimmeingabeWahlberechtigte, StimmeingabePlausiTyp.WAHLBERECHTIGTE_OHNE_WAHLSCHEIN_ANZAHL_GLEICH_NULL, StimmeingabePlausiFehlerart.ERROR),
          getMessages()));
    }

    if (stimmenart.equals(Stimmenart.MIT_WAHLSCHEIN)) {
      stimmeingabeWahlberechtigte.addStimmeingabePlausi(new StimmeingabeAnzahlGleichAnzahlPlausi(StimmeingabeAnzahlReference.SYSTEM_GRUPPE_WAHLBERECHTIGTE_MIT_WAHLSCHEIN,
          findStimmeingabePlausiKonfigurationOrDefaults(stimmeingabeWahlberechtigte, StimmeingabePlausiTyp.WAHLBERECHTIGTE_MIT_WAHLSCHEIN_ANZAHL_GLEICH_NULL, StimmeingabePlausiFehlerart.ERROR),
          getMessages()));
    }

    if (stimmenart.equals(Stimmenart.NICHT_IM_WAEHLERVERZEICHNIS)) {
      stimmeingabeWahlberechtigte.addStimmeingabePlausi(
          new StimmeingabeAnzahlGleichAnzahlPlausi(StimmeingabeAnzahlReference.SYSTEM_GRUPPE_WAHLBERECHTIGTE_NICHT_IM_WAEHLERVERZEICHNIS, findStimmeingabePlausiKonfigurationOrDefaults(
              stimmeingabeWahlberechtigte, StimmeingabePlausiTyp.WAHLBERECHTIGTE_NICHT_IM_WAEHLERVERZEICHNIS_ANZAHL_GLEICH_NULL, StimmeingabePlausiFehlerart.ERROR), getMessages()));
    }

    if (stimmenart.equals(Stimmenart.GESAMT)) {
      stimmeingabeWahlberechtigte.addStimmeingabePlausi(new StimmeingabeAnzahlGleichAnzahlPlausi(StimmeingabeAnzahlReference.SYSTEM_GRUPPE_WAHLBERECHTIGTE_GESAMT,
          findStimmeingabePlausiKonfigurationOrDefaults(stimmeingabeWahlberechtigte, StimmeingabePlausiTyp.WAHLBERECHTIGTE_GESAMT_ANZAHL_GLEICH_NULL, StimmeingabePlausiFehlerart.ERROR),
          getMessages()));
    }
  }

  // Cf. AbstractKopfzahlPlausiBuilder#fillSonderstimmbezirkWaehlerPlausis!
  // Cf. https://confluence.ivu.de/display/ELECTMUC/Wahl-Glossar#Wahl-Glossar-SonderwahlbezirkSonderwahlbezirk!
  private void fillSonderstimmbezirkWaehlerPlausis(Stimmeingabe stimmeingabeWaehler) {
    Stimmenart stimmenart = stimmeingabeWaehler.getStimmenart();

    if (stimmenart.equals(Stimmenart.OHNE_WAHLSCHEIN)) {
      stimmeingabeWaehler.addStimmeingabePlausi(new StimmeingabeAnzahlGleichAnzahlPlausi(StimmeingabeAnzahlReference.SYSTEM_GRUPPE_WAEHLER_OHNE_WAHLSCHEIN,
          findStimmeingabePlausiKonfigurationOrDefaults(stimmeingabeWaehler, StimmeingabePlausiTyp.WAEHLER_OHNE_WAHLSCHEIN_ANZAHL_GLEICH_NULL, StimmeingabePlausiFehlerart.ERROR), getMessages()));
    }
  }

  private void fillSystemGruppeStimmeingabeAllgemeinePlausis(Stimmeingabe stimmeingabe, Ergebniseingangsart ergebniseingangsart, Map<EntityTK, Stimmeingabe> stimmeingabeVorlaeufigPerTK) {
    fillStimmeingabeAllgemeinePlausis(stimmeingabe);

    fillPruefkennzeichenWahlberechtigtePlausis(stimmeingabe, ergebniseingangsart);

    if (ergebniseingangsart.isEndgueltig() && stimmeingabeVorlaeufigPerTK.containsKey(stimmeingabe.getTk())
        && isStimmeingabePlausiKontextGueltig(Collections.singletonList(stimmeingabeVorlaeufigPerTK.get(stimmeingabe.getTk())))) {
      // Kann Plausi: Stimmeingabe vom endgültigen Ergebniseingang mit Stimmeingabe vom vorläufigen Ergebniseingang vergleichen.
      stimmeingabe.addStimmeingabePlausi(new StimmeingabeAnzahlEndgueltigGleichStimmeingabeAnzahlVorlaeufigPlausi(StimmeingabeAnzahlReference.SYSTEM_GRUPPE_STIMMEN,
          stimmeingabeVorlaeufigPerTK.get(stimmeingabe.getTk()),
          findStimmeingabePlausiKonfigurationOrDefaults(stimmeingabe, StimmeingabePlausiTyp.STIMMEINGABE_ANZAHL_ENDGUELTIG_GLEICH_STIMMEINGABE_ANZAHL_VORLAEUFIG, StimmeingabePlausiFehlerart.WARNING),
          getMessages()));
    }
  }

  // NOTE, dass bei Anpassungen auch AbstractKopfzahlPlausiBuilder#fillPruefkennzeichenWahlberechtigtePlausis angepasst werden muss!!!
  private void fillPruefkennzeichenWahlberechtigtePlausis(Stimmeingabe stimmeingabe, Ergebniseingangsart ergebniseingangsart) {
    if (isStimmeingabePruefkennzeichenWahlberechtigte(stimmeingabe)) {
      Gebiet gebiet = stimmeingabe.getGruppeImGebiet().getGebiet();

      if (gebiet != null && gebiet.isInstanceOf(Wahldurchfuehrungsgebiet.class)) {
        Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet = castTo(gebiet, Wahldurchfuehrungsgebiet.class);

        if (AbstractKopfzahlPlausiBuilder.isPruefkennzeichenWahlberechtigteZuPlausibilisierenGegenAnzahlPruefkennzeichenWahlberechtigteAmGebiet(getStimmeingabeKopfzahlKonfiguration(),
            wahldurchfuehrungsgebiet, ergebniseingangsart, stimmeingabe.getErgebniseingangsherkunft())) {
          String pruefkennzeichen = AbstractKopfzahlPlausiBuilder.extractRequiredPruefkennzeichenWahlberechtigteAmGebiet(wahldurchfuehrungsgebiet);

          stimmeingabe.addStimmeingabePlausi(new PruefkennzeichenWahlberechtigteErfasstPlausi(
              findStimmeingabePlausiKonfigurationOrDefaults(stimmeingabe, StimmeingabePlausiTyp.PRUEFKENNZEICHEN_WAHLBERECHTIGTE_ERFASST, StimmeingabePlausiFehlerart.ERROR), getMessages()));
          stimmeingabe.addStimmeingabePlausi(new PruefkennzeichenWahlberechtigteGleichPruefkennzeichenWahlberechtigteAmGebietPlausi(pruefkennzeichen, findStimmeingabePlausiKonfigurationOrDefaults(
              stimmeingabe, StimmeingabePlausiTyp.PRUEFKENNZEICHEN_WAHLBERECHTIGTE_GLEICH_PRUEFKENNZEICHEN_WAHLBERECHTIGTE_AM_GEBIET, StimmeingabePlausiFehlerart.ERROR), getMessages()));
        }
      }
    }
  }

  private boolean isStimmeingabePruefkennzeichenWahlberechtigte(Stimmeingabe stimmeingabe) {
    return isStimmeingabeWahlberechtigte(stimmeingabe) && Stimmenart.PRUEFKENNZEICHEN.equals(stimmeingabe.getStimmenart());
  }

  protected void fillWaehlerAllgemeinePlausis(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, Ergebniseingangsart ergebniseingangsart, Stimmeingabe stimmeingabe,
      Map<SystemGruppe, List<Stimmeingabe>> stimmeingabenPerSystemGruppe) {
    if (isStimmeingabeWaehlerGesamt(stimmeingabe)) {
      Stimmeingabe stimmeingabeWaehlerGesamt = stimmeingabe;

      // ELECT-3345, LTW 02 Muss Fehler / BTW 02 Muss Fehler
      stimmeingabeWaehlerGesamt.addStimmeingabePlausi(new StimmeingabeAnzahlGroesserAnzahlPlausi(StimmeingabeAnzahlReference.SYSTEM_GRUPPE_WAEHLER_GESAMT,
          findStimmeingabePlausiKonfigurationOrDefaults(stimmeingabeWaehlerGesamt, StimmeingabePlausiTyp.WAEHLER_GESAMT_ANZAHL_GROESSER_NULL, StimmeingabePlausiFehlerart.WARNING), getMessages()));

      // ELECTMUC-936, ELECTKOM-271
      stimmeingabeWaehlerGesamt.addStimmeingabePlausi(new WaehlerGesamtAnzahlGroesserGleichAnzahlPlausi(findStimmeingabePlausiKonfigurationOrDefaults(stimmeingabeWaehlerGesamt,
          StimmeingabePlausiTyp.WAEHLER_GESAMT_ANZAHL_GROESSER_GLEICH_MINDESTANZAHL, StimmeingabePlausiFehlerart.WARNING, new StimmeingabeAnzahlComparedToAnzahlPlausiCustomData(50L)), getMessages()));
    }
  }

  private void fillWaehlerFuerNichtBriefwahlPlausis(Stimmeingabe stimmeingabe, Map<SystemGruppe, List<Stimmeingabe>> stimmeingabenPerSystemGruppe, Ergebniseingangsart ergebniseingangsart,
      Gebiet gebiet, Gebietsart gebietsart) {
    if (isStimmeingabeWaehler(stimmeingabe) && !gebietsart.isBriefwahlbezirk()) {
      // ELECTMUC-853
      if (!isSonderstimmbezirk(gebiet)) {
        fillWaehlerMitWahlscheinFuerNichtBriefwahlPlausis(stimmeingabe, stimmeingabenPerSystemGruppe, ergebniseingangsart, gebiet);
        fillWaehlerGesamtFuerNichtBriefwahlPlausis(stimmeingabe, stimmeingabenPerSystemGruppe, ergebniseingangsart);
        fillWaehlerGesamtFuerEndgueltigeNichtBriefwahlPlausis(stimmeingabe, stimmeingabenPerSystemGruppe, ergebniseingangsart);
      } else {
        fillWaehlerMitWahlscheinFuerSonderstimmbezirk(stimmeingabe, stimmeingabenPerSystemGruppe, ergebniseingangsart, gebiet);
      }
    }
  }

  private void fillWaehlerMitWahlscheinFuerNichtBriefwahlPlausis(Stimmeingabe stimmeingabe, Map<SystemGruppe, List<Stimmeingabe>> stimmeingabenPerSystemGruppe, Ergebniseingangsart ergebniseingangsart,
      Gebiet gebiet) {
    if (stimmeingabe.getStimmenart().equals(Stimmenart.MIT_WAHLSCHEIN)) {
      Stimmeingabe stimmeingabeWaehlerMitWahlschein = stimmeingabe;
      Stimmeingabe stimmeingabeWaehlerGesamt = findStimmeingabeMitStimmenart(stimmeingabenPerSystemGruppe.get(SystemGruppe.WAEHLER), Stimmenart.GESAMT);

      if (isStimmeingabePlausiKontextGueltig(Collections.singletonList(stimmeingabeWaehlerGesamt))) {
        stimmeingabeWaehlerMitWahlschein.addStimmeingabePlausi(
            new WaehlerMitWahlscheinAnzahlKleinerGleichWaehlerGesamtAnzahlPlausi(stimmeingabeWaehlerGesamt, findStimmeingabePlausiKonfigurationOrDefaults(stimmeingabeWaehlerMitWahlschein,
                StimmeingabePlausiTyp.WAEHLER_MIT_WAHLSCHEIN_ANZAHL_KLEINER_GLEICH_WAEHLER_GESAMT_ANZAHL, StimmeingabePlausiFehlerart.ERROR), getMessages()));

        // ELECT-9171, kann NIE ohne die vorherige Plausi auftreten!!!
        stimmeingabeWaehlerMitWahlschein.addStimmeingabePlausi(new WaehlerMitWahlscheinAnzahlKleinerWaehlerGesamtAnzahlPlausi(stimmeingabeWaehlerGesamt, findStimmeingabePlausiKonfigurationOrDefaults(
            stimmeingabeWaehlerMitWahlschein, StimmeingabePlausiTyp.WAEHLER_MIT_WAHLSCHEIN_ANZAHL_KLEINER_WAEHLER_GESAMT_ANZAHL, StimmeingabePlausiFehlerart.WARNING), getMessages()));
      }

      // LTW 07 Kann Fehler / BTW Kann Fehler.
      Stimmeingabe wahlberechtigteMitWahlschein = findStimmeingabeMitStimmenart(stimmeingabenPerSystemGruppe.get(SystemGruppe.WAHLBERECHTIGTE), Stimmenart.MIT_WAHLSCHEIN);

      if (isStimmeingabePlausiKontextGueltig(Collections.singletonList(wahlberechtigteMitWahlschein))) {
        double anteilWahlberechtigteMitWahlschein = 0.90D;

        // NOTE, dass auch der Typ StimmeingabePlausiTyp#WAEHLER_MIT_WAHLSCHEIN_ANZAHL_KLEINER_WAHLBERECHTIGTE_MIT_WAHLSCHEIN_ANZAHL_PLUS_WAHLBERECHTIGTE_NICHT_IM_WAEHLERVERZEICHNIS_ANZAHL_ANTEIL
        // existiert, aber bisher nicht an dieser Stelle (== für diese Wahlarten) verwendet wird, cf. AbstractKopfzahlPlausiBuilder#addPlausiWaehlerMitWahlscheinKleinerWahlberechtigteMitWahlscheinAnteil!
        stimmeingabeWaehlerMitWahlschein.addStimmeingabePlausi(new WaehlerMitWahlscheinAnzahlKleinerWahlberechtigteMitWahlscheinAnzahlAnteilPlausi(wahlberechtigteMitWahlschein,
            findStimmeingabePlausiKonfigurationOrDefaults(stimmeingabeWaehlerMitWahlschein, StimmeingabePlausiTyp.WAEHLER_MIT_WAHLSCHEIN_ANZAHL_KLEINER_WAHLBERECHTIGTE_MIT_WAHLSCHEIN_ANZAHL_ANTEIL,
                StimmeingabePlausiFehlerart.WARNING, new StimmeingabeAnzahlComparedToStimmeingabeAnzahlAnteilPlausiCustomData(anteilWahlberechtigteMitWahlschein)),
            getMessages()));
      }
    }
  }

  private void fillWaehlerGesamtFuerNichtBriefwahlPlausis(Stimmeingabe stimmeingabe, Map<SystemGruppe, List<Stimmeingabe>> stimmeingabenPerSystemGruppe, Ergebniseingangsart ergebniseingangsart) {
    if (stimmeingabe.getStimmenart().equals(Stimmenart.GESAMT)) {
      Stimmeingabe stimmeingabeWaehlerGesamt = stimmeingabe;
      Stimmeingabe stimmeingabeWahlberechtigteGesamt = findStimmeingabeMitStimmenart(stimmeingabenPerSystemGruppe.get(SystemGruppe.WAHLBERECHTIGTE), Stimmenart.GESAMT);

      if (isStimmeingabePlausiKontextGueltig(Collections.singletonList(stimmeingabeWahlberechtigteGesamt))) {
        double anteilWahlberechtigteGesamtMin = 0.20D;
        double anteilWahlberechtigteGesamtMax = 0.95D;

        // WASDE-1329
        stimmeingabeWaehlerGesamt.addStimmeingabePlausi(new WaehlerGesamtAnzahlGroesserWahlberechtigteGesamtAnzahlAnteilPlausi(stimmeingabeWahlberechtigteGesamt,
            findStimmeingabePlausiKonfigurationOrDefaults(stimmeingabeWaehlerGesamt, StimmeingabePlausiTyp.WAEHLER_GESAMT_ANZAHL_GROESSER_WAHLBERECHTIGTE_GESAMT_ANZAHL_ANTEIL,
                StimmeingabePlausiFehlerart.WARNING, new StimmeingabeAnzahlComparedToStimmeingabeAnzahlAnteilPlausiCustomData(anteilWahlberechtigteGesamtMin)),
            getMessages()));
        // LTW 06 Kann Fehler / BTW 56 Kann Fehler.
        stimmeingabeWaehlerGesamt.addStimmeingabePlausi(new WaehlerGesamtAnzahlKleinerWahlberechtigteGesamtAnzahlAnteilPlausi(stimmeingabeWahlberechtigteGesamt,
            findStimmeingabePlausiKonfigurationOrDefaults(stimmeingabeWaehlerGesamt, StimmeingabePlausiTyp.WAEHLER_GESAMT_ANZAHL_KLEINER_WAHLBERECHTIGTE_GESAMT_ANZAHL_ANTEIL,
                StimmeingabePlausiFehlerart.WARNING, new StimmeingabeAnzahlComparedToStimmeingabeAnzahlAnteilPlausiCustomData(anteilWahlberechtigteGesamtMax)),
            getMessages()));
      }
    }
  }

  private void fillWaehlerGesamtFuerEndgueltigeNichtBriefwahlPlausis(Stimmeingabe stimmeingabe, Map<SystemGruppe, List<Stimmeingabe>> stimmeingabenPerSystemGruppe,
      Ergebniseingangsart ergebniseingangsart) {
    if (stimmeingabe.getStimmenart().equals(Stimmenart.GESAMT)) {
      // LTW 05 Kann Fehler / BTW 55 Kann Fehler.
      // Nur für endgültige ErgebnisEingänge, da die wahlberechtigteOhneWahlschein und die waehlerMitWahlschein nur in einem endgültigen Ergebniseingang vorhanden sind.
      Stimmeingabe stimmeingabeWaehlerGesamt = stimmeingabe;
      Stimmeingabe stimmeingabeWaehlerMitWahlschein = findStimmeingabeMitStimmenart(stimmeingabenPerSystemGruppe.get(SystemGruppe.WAEHLER), Stimmenart.MIT_WAHLSCHEIN);

      Stimmeingabe stimmeingabeWahlberechtigteOhneWahlschein = findStimmeingabeMitStimmenart(stimmeingabenPerSystemGruppe.get(SystemGruppe.WAHLBERECHTIGTE), Stimmenart.OHNE_WAHLSCHEIN);

      if (isStimmeingabePlausiKontextGueltig(asList(stimmeingabeWahlberechtigteOhneWahlschein, stimmeingabeWaehlerMitWahlschein))) {
        double anteilWahlberechtigtenOhneWahlschein = 0.98D;

        stimmeingabeWaehlerGesamt.addStimmeingabePlausi(new WaehlerOhneWahlscheinBerechnetAnzahlKleinerWahlberechtigteOhneWahlscheinAnzahlAnteilPlausi(stimmeingabeWaehlerGesamt,
            stimmeingabeWaehlerMitWahlschein, stimmeingabeWahlberechtigteOhneWahlschein,
            findStimmeingabePlausiKonfigurationOrDefaults(stimmeingabeWaehlerGesamt,
                StimmeingabePlausiTyp.WAEHLER_OHNE_WAHLSCHEIN_BERECHNET_ANZAHL_KLEINER_WAHLBERECHTIGTE_OHNE_WAHLSCHEIN_ANZAHL_ANTEIL, StimmeingabePlausiFehlerart.WARNING,
                new WaehlerOhneWahlscheinAnzahlBerechnetComparedToStimmeingabeAnzahlAnteilPlausiCustomData(anteilWahlberechtigtenOhneWahlschein)),
            getMessages()));
      }
    }
  }

  private void fillWaehlerMitWahlscheinFuerSonderstimmbezirk(Stimmeingabe stimmeingabe, Map<SystemGruppe, List<Stimmeingabe>> stimmeingabenPerSystemGruppe, Ergebniseingangsart ergebniseingangsart,
      Gebiet gebiet) {
    if (stimmeingabe.getStimmenart().equals(Stimmenart.MIT_WAHLSCHEIN)) {
      Stimmeingabe stimmeingabeWaehlerMitWahlschein = stimmeingabe;
      Stimmeingabe stimmeingabeWaehlerGesamt = findStimmeingabeMitStimmenart(stimmeingabenPerSystemGruppe.get(SystemGruppe.WAEHLER), Stimmenart.GESAMT);

      if (isStimmeingabePlausiKontextGueltig(Collections.singletonList(stimmeingabeWaehlerGesamt))) {
        stimmeingabeWaehlerMitWahlschein.addStimmeingabePlausi(
            new SonderstimmbezirkWaehlerMitWahlscheinAnzahlGleichWaehlerGesamtAnzahlPlausi(stimmeingabeWaehlerGesamt, findStimmeingabePlausiKonfigurationOrDefaults(stimmeingabeWaehlerMitWahlschein,
                StimmeingabePlausiTyp.WAEHLER_MIT_WAHLSCHEIN_ANZAHL_GLEICH_WAEHLER_GESAMT_ANZAHL, StimmeingabePlausiFehlerart.ERROR), getMessages()));
      }
    }
  }

  private void fillWahlberechtigteFuerNichtBriefwahlPlausis(Stimmeingabe stimmeingabe, Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, Ergebniseingangsart ergebniseingangsart,
      Gebietsart gebietsart, Map<SystemGruppe, List<Stimmeingabe>> stimmeingabenPerSystemGruppe, Map<Stimmenart, Long> anzahlWahlberechtigteAmGebietPerStimmenart) {
    if (isStimmeingabeWahlberechtigte(stimmeingabe) && !gebietsart.isBriefwahlbezirk()) {
      fillWahlberechtigteAllgemeinFuerNichtBriefwahlPlausis(stimmeingabe, wahldurchfuehrungsgebiet, ergebniseingangsart, stimmeingabenPerSystemGruppe, anzahlWahlberechtigteAmGebietPerStimmenart);
      fillWahlberechtigteGesamtFuerNichtBriefwahlPlausis(stimmeingabe, stimmeingabenPerSystemGruppe, ergebniseingangsart);
      fillUrnenwahlKopfzahlSummenWahlberechtigtePlausis(stimmeingabe, stimmeingabenPerSystemGruppe, ergebniseingangsart);
    }
  }

  protected void fillWahlberechtigteAllgemeinFuerNichtBriefwahlPlausis(Stimmeingabe stimmeingabe, Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, Ergebniseingangsart ergebniseingangsart,
      Map<SystemGruppe, List<Stimmeingabe>> stimmeingabenPerSystemGruppe, Map<Stimmenart, Long> anzahlWahlberechtigteAmGebietPerStimmenart) {
    // NoOp per default.
  }

  protected void fillWahlberechtigteGesamtFuerNichtBriefwahlPlausis(Stimmeingabe stimmeingabe, Map<SystemGruppe, List<Stimmeingabe>> stimmeingabenPerSystemGruppe,
      Ergebniseingangsart ergebniseingangsart) {
    // NoOp per default.
  }

  // TODO ELECT-6493, cf. AbstractKopfzahlPlausiBuilder#fillUrnenwahlKopfzahlSummenWahlberechtigtePlausis!
  protected void fillUrnenwahlKopfzahlSummenWahlberechtigtePlausis(Stimmeingabe stimmeingabe, Map<SystemGruppe, List<Stimmeingabe>> stimmeingabenPerSystemGruppe,
      Ergebniseingangsart ergebniseingangsart) {
    if (stimmeingabe.getStimmenart().equals(Stimmenart.GESAMT)) {
      Gebiet gebiet = stimmeingabe.getGruppeImGebiet().getGebiet();

      if (gebiet != null && gebiet.isInstanceOf(Wahldurchfuehrungsgebiet.class)) {
        Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet = castTo(gebiet, Wahldurchfuehrungsgebiet.class);

        if (AbstractKopfzahlPlausiBuilder.isWahlberechtigteGesamtZuPlausibilisieren(getStimmeingabeKopfzahlKonfiguration(), wahldurchfuehrungsgebiet, ergebniseingangsart,
            getStimmeingabeKopfzahlKonfigurationContext())) {
          Stimmeingabe stimmeingabeWahlberechtigteGesamt = stimmeingabe;

          Stimmeingabe stimmeingabeWahlberechtigteOhneWahlschein = findStimmeingabeMitStimmenart(stimmeingabenPerSystemGruppe.get(SystemGruppe.WAHLBERECHTIGTE), Stimmenart.OHNE_WAHLSCHEIN);
          Stimmeingabe stimmeingabeWahlberechtigteMitWahlschein = findStimmeingabeMitStimmenart(stimmeingabenPerSystemGruppe.get(SystemGruppe.WAHLBERECHTIGTE), Stimmenart.MIT_WAHLSCHEIN);

          if (stimmeingabeWahlberechtigteOhneWahlschein != null && stimmeingabeWahlberechtigteMitWahlschein != null) {
            Stimmeingabe stimmeingabeWahlberechtigteNichtImWaehlerverzeichnis = findStimmeingabeMitStimmenart(stimmeingabenPerSystemGruppe.get(SystemGruppe.WAHLBERECHTIGTE),
                Stimmenart.NICHT_IM_WAEHLERVERZEICHNIS);

            stimmeingabeWahlberechtigteGesamt.addStimmeingabePlausi(new WahlberechtigteGesamtAnzahlGleichWahlberechtigteAnzahlenSummePlausi(stimmeingabeWahlberechtigteOhneWahlschein,
                stimmeingabeWahlberechtigteMitWahlschein, stimmeingabeWahlberechtigteNichtImWaehlerverzeichnis, findStimmeingabePlausiKonfigurationOrDefaults(stimmeingabeWahlberechtigteGesamt,
                    StimmeingabePlausiTyp.WAHLBERECHTIGTE_GESAMT_ANZAHL_GLEICH_WAHLBERECHTIGTE_ANZAHLEN_SUMME, StimmeingabePlausiFehlerart.ERROR),
                getMessages()));
          }
        }
      }
    }
  }

  private void fillGueltigePlausis(Stimmeingabe stimmeingabe, Map<SystemGruppe, List<Stimmeingabe>> stimmeingabenPerSystemGruppe,
      Map<Stimmenart, Map<GruppeImGebiet, Stimmeingabe>> gueltigeStimmenAllerParteienProStimmenart, Wahl wahl) {
    if (isStimmeingabeGueltige(stimmeingabe)) {
      fillGueltigeAllgemeinPlausis(stimmeingabe, stimmeingabenPerSystemGruppe, gueltigeStimmenAllerParteienProStimmenart, wahl);
      fillGueltigeDirektstimmenPlausis(stimmeingabe, stimmeingabenPerSystemGruppe, gueltigeStimmenAllerParteienProStimmenart);
    }
  }

  private void fillGueltigeAllgemeinPlausis(Stimmeingabe stimmeingabeGueltigeStimmen, Map<SystemGruppe, List<Stimmeingabe>> stimmeingabenPerSystemGruppe,
      Map<Stimmenart, Map<GruppeImGebiet, Stimmeingabe>> gueltigeStimmenAllerParteienProStimmenart, Wahl wahl) {
    // LTW 07 Muss, LTW 09 Muss Fehler. / BTW 08 Muss, BTW 10 Muss Fehler.
    // Für alle Stimmenarten.
    Stimmenart stimmenart = stimmeingabeGueltigeStimmen.getStimmenart();
    List<Stimmeingabe> stimmeingabenGruppeStimmen = new ArrayList<>(gueltigeStimmenAllerParteienProStimmenart.get(stimmenart).values());

    if (isStimmeingabePlausiKontextGueltig(stimmeingabenGruppeStimmen)) {
      // Der Standardfall!
      StimmeingabePlausiTyp typ = StimmeingabePlausiTyp.GUELTIGE_REFERENDUM_STIMMEN_ANZAHL_GLEICH_REFERENDUM_OPTION_REFERENDUM_STIMMEN_ANZAHLEN_SUMME;

      // Legacy-Fallbacks...
      if (Stimmenart.DIREKTSTIMME.equals(stimmenart)) {
        typ = StimmeingabePlausiTyp.GUELTIGE_DIREKTSTIMMEN_ANZAHL_GLEICH_GRUPPE_DIREKTSTIMMEN_ANZAHLEN_SUMME;
      }

      if (Stimmenart.LISTENSTIMME.equals(stimmenart)) {
        typ = StimmeingabePlausiTyp.GUELTIGE_LISTENSTIMMEN_ANZAHL_GLEICH_GRUPPE_LISTENSTIMMEN_ANZAHLEN_SUMME;
      }

      if (Stimmenart.LISTENSTIMME_ABZUZIEHEN.equals(stimmenart)) {
        typ = StimmeingabePlausiTyp.GUELTIGE_LISTENSTIMMEN_ABZUZIEHEN_ANZAHL_GLEICH_GRUPPE_LISTENSTIMMEN_ABZUZIEHEN_ANZAHLEN_SUMME;
      }

      stimmeingabeGueltigeStimmen.addStimmeingabePlausi(new GueltigeStimmenAnzahlGleichGruppeStimmenAnzahlenSummePlausi(stimmeingabenGruppeStimmen,
          findStimmeingabePlausiKonfigurationOrDefaults(stimmeingabeGueltigeStimmen, typ, StimmeingabePlausiFehlerart.ERROR), getMessages()));
    }
  }

  protected void fillGueltigeDirektstimmenPlausis(Stimmeingabe stimmeingabe, Map<SystemGruppe, List<Stimmeingabe>> stimmeingabenPerSystemGruppe,
      Map<Stimmenart, Map<GruppeImGebiet, Stimmeingabe>> gueltigeStimmenAllerParteienProStimmenart) {
    // NoOp per default.
  }

  private void fillNichtSystemGruppeStimmeingabePlausis(Gebiet gebiet, Set<Stimmeingabe> stimmeingabenZuPlausibilisieren, Map<GruppeImGebiet, List<Stimmeingabe>> stimmeingabenPerGruppeImGebiet,
      Map<SystemGruppe, List<Stimmeingabe>> stimmeingabenPerSystemGruppe, Ergebniseingangsart ergebniseingangsart, Map<EntityTK, Stimmeingabe> stimmeingabeVorlaeufigPerTK) {
    for (Map.Entry<GruppeImGebiet, List<Stimmeingabe>> entry : stimmeingabenPerGruppeImGebiet.entrySet()) {
      for (Stimmeingabe stimmeingabe : entry.getValue()) {
        // Checken ob die Stimmeingabe plausis haben soll.
        if (stimmeingabenZuPlausibilisieren.contains(stimmeingabe)) {

          fillStimmeingabeAllgemeinePlausis(stimmeingabe);

          fillNichtSystemGruppeStimmeingabeErwarteteWertePlausi(stimmeingabe, entry.getKey(), ergebniseingangsart, stimmeingabenPerSystemGruppe, stimmeingabeVorlaeufigPerTK);

          fillWahlSpezifischeNichtSystemGruppeStimmeingabePlausis(stimmeingabe, entry.getValue(), ergebniseingangsart);
        }
      }
    }
  }

  private void fillNichtSystemGruppeStimmeingabeErwarteteWertePlausi(Stimmeingabe stimmeingabe, GruppeImGebiet gruppeImGebiet, Ergebniseingangsart ergebniseingangsart,
      Map<SystemGruppe, List<Stimmeingabe>> stimmeingabenPerSystemGruppe, Map<EntityTK, Stimmeingabe> stimmeingabeVorlaeufigPerTK) {
    // Kann Plausi: Endgültiger Ergebniseingang mit Vorläufigem Ergebniseingang vergleichen.
    if (stimmeingabe.isGruppeStimmeingabe() && ergebniseingangsart.isEndgueltig() && stimmeingabeVorlaeufigPerTK.containsKey(stimmeingabe.getTk())) {
      if (isStimmeingabePlausiKontextGueltig(asList(stimmeingabeVorlaeufigPerTK.get(stimmeingabe.getTk())))) {
        stimmeingabe.addStimmeingabePlausi(new StimmeingabeAnzahlEndgueltigGleichStimmeingabeAnzahlVorlaeufigPlausi(StimmeingabeAnzahlReference.GRUPPE_STIMMEN,
            stimmeingabeVorlaeufigPerTK.get(stimmeingabe.getTk()), findStimmeingabePlausiKonfigurationOrDefaults(stimmeingabe,
                StimmeingabePlausiTyp.STIMMEINGABE_ANZAHL_ENDGUELTIG_GLEICH_STIMMEINGABE_ANZAHL_VORLAEUFIG, StimmeingabePlausiFehlerart.WARNING),
            getMessages()));
      }
    }
  }

  private void fillStimmeingabeAllgemeinePlausis(Stimmeingabe stimmeingabe) {
    if (stimmeingabe.isAnzahlEingabeZuKonvertieren()) {
      // Allgemeneiner Muss Fehler
      stimmeingabe.addStimmeingabePlausi(new StimmeingabeAnzahlKonvertierbarPlausi(
          findStimmeingabePlausiKonfigurationOrDefaults(stimmeingabe, StimmeingabePlausiTyp.STIMMEINGABE_ANZAHL_KONVERTIERBAR, StimmeingabePlausiFehlerart.ERROR), getMessages()));
    }
  }

  protected boolean isSonderstimmbezirk(Gebiet gebiet) {
    return Boolean.TRUE.equals(gebiet.getSonderstimmbezirk());
  }

  protected boolean isStimmeingabeGueltige(Stimmeingabe stimmeingabe) {
    return SystemGruppe.GUELTIGE.equalsGruppe(stimmeingabe.getGruppeImGebiet().getGruppe(), getSystemGruppeEigenschaften());
  }

  protected boolean isStimmeingabeUngueltige(Stimmeingabe stimmeingabe) {
    return SystemGruppe.UNGUELTIGE.equalsGruppe(stimmeingabe.getGruppeImGebiet().getGruppe(), getSystemGruppeEigenschaften());
  }

  private boolean isStimmeingabeWahlberechtigte(Stimmeingabe stimmeingabe) {
    return SystemGruppe.WAHLBERECHTIGTE.equalsGruppe(stimmeingabe.getGruppeImGebiet().getGruppe(), getSystemGruppeEigenschaften());
  }

  protected boolean isStimmeingabeWaehlerOhneWahlschein(Stimmeingabe stimmeingabe) {
    return isStimmeingabeWaehler(stimmeingabe) && isStimmeingabeStimmenart(stimmeingabe, Stimmenart.OHNE_WAHLSCHEIN);
  }

  protected boolean isStimmeingabeWaehlerMitWahlschein(Stimmeingabe stimmeingabe) {
    return isStimmeingabeWaehler(stimmeingabe) && isStimmeingabeStimmenart(stimmeingabe, Stimmenart.MIT_WAHLSCHEIN);
  }

  protected boolean isStimmeingabeWaehlerGesamt(Stimmeingabe stimmeingabe) {
    return isStimmeingabeWaehler(stimmeingabe) && isStimmeingabeStimmenart(stimmeingabe, Stimmenart.GESAMT);
  }

  protected boolean isStimmeingabeWaehlerMitWahlscheinInUrnenwahlAnzahlAusUrnenwahl(Stimmeingabe stimmeingabe) {
    return isStimmeingabeWaehler(stimmeingabe) && isStimmeingabeStimmenart(stimmeingabe, Stimmenart.ALS_URNENWAHL_IN_URNE);
  }

  protected boolean isStimmeingabeWaehlerMitWahlscheinInUrnenwahlAnzahlAusBriefwahl(Stimmeingabe stimmeingabe) {
    return isStimmeingabeWaehler(stimmeingabe) && isStimmeingabeStimmenart(stimmeingabe, Stimmenart.ALS_BRIEFWAHL_IN_URNE);
  }

  protected boolean isStimmeingabeWaehler(Stimmeingabe stimmeingabe) {
    return SystemGruppe.WAEHLER.equalsGruppe(stimmeingabe.getGruppeImGebiet().getGruppe(), getSystemGruppeEigenschaften()) && isPseudoStimmenart(stimmeingabe);
  }

  private boolean isPseudoStimmenart(de.ivu.elect.business.stimmeingabe.entity.Stimmeingabe stimmeingabe) {
    return isPseudoStimmenart(stimmeingabe.getStimmenart());

  }

  @Deprecated // Sinn und Zweck erschließt sich nicht...
  protected static boolean isPseudoStimmenart(Stimmenart stimmenart) {
    return !(stimmenart.isDirektstimme() || stimmenart.isListenstimme() || stimmenart.isListenstimmeLeer() || stimmenart.isListenstimmeAbzuziehen()
        || stimmenart.isListenstimmeEinzelnerWahlvorschlagUnveraendert() || stimmenart.isListenstimmeEinzelnerWahlvorschlagVeraendert() || stimmenart.isListenstimmeEinzelnerWahlvorschlagPanaschiert()
        || stimmenart.isReferendumStimmenart() || stimmenart.isListenkandidaturstimmeNichtErgebnisrelevant());
  }

  protected boolean isStimmeingabeStimmenart(Stimmeingabe stimmeingabe, Stimmenart stimmenart) {
    return stimmenart.equals(stimmeingabe.getStimmenart());
  }

  private Map<Stimmenart, Map<GruppeImGebiet, Stimmeingabe>> findGueltigeStimmenAllerParteienProStimmenart(Map<GruppeImGebiet, List<Stimmeingabe>> stimmeingabenPerGruppeImGebiet) {
    Map<Stimmenart, Map<GruppeImGebiet, Stimmeingabe>> gueltigeStimmenAllerParteien = new HashMap<>();
    Map<GruppeImGebiet, Stimmeingabe> gueltigeListenstimmenAllerParteien = new HashMap<>();
    Map<GruppeImGebiet, Stimmeingabe> gueltigeDirektstimmenAllerParteien = new HashMap<>();
    gueltigeStimmenAllerParteien.put(Stimmenart.DIREKTSTIMME, gueltigeDirektstimmenAllerParteien);
    gueltigeStimmenAllerParteien.put(Stimmenart.LISTENSTIMME, gueltigeListenstimmenAllerParteien);

    for (Map.Entry<GruppeImGebiet, List<Stimmeingabe>> entry : stimmeingabenPerGruppeImGebiet.entrySet()) {
      GruppeImGebiet gruppeImGebiet = entry.getKey();
      if (entry.getValue() != null) {
        for (Stimmeingabe stimmeingabe : entry.getValue()) {
          if (!stimmeingabe.isSystemGruppenStimmeingabe() && stimmeingabe.isGruppeStimmeingabe() && stimmeingabe.getStimmenart().equals(Stimmenart.LISTENSTIMME)) {
            gueltigeListenstimmenAllerParteien.put(gruppeImGebiet, stimmeingabe);
          }

          if (!stimmeingabe.isSystemGruppenStimmeingabe() && stimmeingabe.isGruppeStimmeingabe() && stimmeingabe.getStimmenart().equals(Stimmenart.DIREKTSTIMME)) {
            gueltigeDirektstimmenAllerParteien.put(gruppeImGebiet, stimmeingabe);
          }
        }
      }
    }

    return gueltigeStimmenAllerParteien;
  }

  @Nullable
  protected Stimmeingabe findStimmeingabeMitStimmenart(List<Stimmeingabe> stimmeingaben, Stimmenart stimmenart) {
    if (stimmeingaben != null) {
      return stimmeingaben.stream().filter(stimmeingabe -> stimmeingabe.getStimmenart().equals(stimmenart)).findFirst().orElse(null);
    }
    return null;
  }

  protected Stimmen findStimmenMitStimmenart(List<Stimmen> stimmen, Stimmenart stimmenart) {
    if (stimmen != null) {
      return stimmen.stream().filter(stimme -> stimme.getStimmenart().equals(stimmenart)).findFirst().orElse(null);
    }
    return null;
  }

  private Map<Stimmenart, Long> loadAWerte(Ergebniseingang ergebniseingang) {
    StimmeingabeKopfzahlKonfiguration stimmeingabeKopfzahlKonfiguration = getStimmeingabeKopfzahlKonfiguration();

    Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet = ergebniseingang.getWahldurchfuehrungsgebiet();
    Ergebniseingangsart ergebniseingangsart = ergebniseingang.getErgebniseingangsart();

    Map<Stimmenart, Long> anzahlWahlberechtigtePerStimmenart = new HashMap<>();
    StimmeingabeKopfzahlKonfigurationContext stimmeingabeKopfzahlKonfigurationContext = getStimmeingabeKopfzahlKonfigurationContext();
    anzahlWahlberechtigtePerStimmenart.put(Stimmenart.OHNE_WAHLSCHEIN,
        stimmeingabeKopfzahlKonfiguration.extractWahlberechtigteOhneWahlscheinAmGebietHinterlegt(wahldurchfuehrungsgebiet, ergebniseingangsart, stimmeingabeKopfzahlKonfigurationContext));
    anzahlWahlberechtigtePerStimmenart.put(Stimmenart.MIT_WAHLSCHEIN,
        stimmeingabeKopfzahlKonfiguration.extractWahlberechtigteMitWahlscheinAmGebietHinterlegt(wahldurchfuehrungsgebiet, ergebniseingangsart, stimmeingabeKopfzahlKonfigurationContext));
    anzahlWahlberechtigtePerStimmenart.put(Stimmenart.NICHT_IM_WAEHLERVERZEICHNIS,
        stimmeingabeKopfzahlKonfiguration.extractWahlberechtigteNichtImWaehlerverzeichnisAmGebietHinterlegt(wahldurchfuehrungsgebiet, ergebniseingangsart, stimmeingabeKopfzahlKonfigurationContext));
    anzahlWahlberechtigtePerStimmenart.put(Stimmenart.GESAMT,
        stimmeingabeKopfzahlKonfiguration.extractWahlberechtigteGesamtAmGebietHinterlegt(wahldurchfuehrungsgebiet, ergebniseingangsart, stimmeingabeKopfzahlKonfigurationContext));

    return anzahlWahlberechtigtePerStimmenart;
  }

  private Map<EntityTK, Stimmeingabe> loadVorlaeufigeStimmeingabenPerTKWennAktuellerErgebniseingangIstEndgueltig(Ergebniseingang ergebniseingang) {
    Map<EntityTK, Stimmeingabe> stimmeingabeVorlaeufigPerTK = null;

    if (ergebniseingang.getErgebniseingangsart().isEndgueltig()) {
      Ergebniseingang vorlaeufigerErgebniseingang = ergebniseingang.getWahldurchfuehrungsgebiet().getLetzterErgebniseingangAktuellInErgebnisUebernehmbar(ergebniseingangDAO,
          Ergebniseingangsart.VORLAEUFIG);

      if (vorlaeufigerErgebniseingang != null) {
        stimmeingabeVorlaeufigPerTK = EntityUtil.mappeNachKonkretestemTk(vorlaeufigerErgebniseingang.getStimmeingaben());
      }
    }

    if (stimmeingabeVorlaeufigPerTK == null) {
      return new HashMap<>();
    }

    return stimmeingabeVorlaeufigPerTK;
  }

  protected boolean isStimmeingabePlausiKontextGueltig(Collection<Stimmeingabe> stimmeingabePlausiKontext) {
    if (stimmeingabePlausiKontext == null || stimmeingabePlausiKontext.size() == 0) {
      return false;
    }

    return stimmeingabePlausiKontext.stream().noneMatch(Objects::isNull);
  }

  @Nullable
  protected Stimmeingabe findZwischensummeAnzahlMitIndex(List<Stimmeingabe> stimmeingaben, Long summenIndex) {
    if (stimmeingaben == null) {
      return null;
    }

    return stimmeingaben.stream().filter(stimmeingabe -> stimmeingabe.isZwischensummeEingabe() && ((ZwischensummeEingabe) stimmeingabe).getSummenIndex().equals(summenIndex)).findFirst().orElse(null);
  }

  protected StimmeingabePlausiKonfigurationDAO getStimmeingabePlausiKonfigurationDAO() {
    return stimmeingabePlausiKonfigurationDAO;
  }

  protected StimmeingabeKopfzahlKonfiguration getStimmeingabeKopfzahlKonfiguration() {
    return stimmeingabeKopfzahlKonfiguration;
  }

  protected WahlParameterLoader getWahlParameterLoader() {
    return wahlParameterLoader;
  }

  protected SystemGruppeEigenschaften getSystemGruppeEigenschaften() {
    return systemGruppeEigenschaften;
  }

  protected Messages getMessages() {
    return messages;
  }

  protected StimmeingabeKopfzahlKonfigurationContext getStimmeingabeKopfzahlKonfigurationContext() {
    return new DaoStimmeingabeKopfzahlKonfigurationContext(wahldurchfuehrungsgebietDAO);
  }
}
