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

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.ToLongFunction;

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

import de.ivu.elect.business.ergebnis.control.NlStimmenGewichtetKumulierer;
import de.ivu.elect.business.ergebnis.control.StimmenPool;
import de.ivu.elect.business.ergebnis.entity.Stimmabgabeart;
import de.ivu.elect.business.ergebnis.entity.Stimmenart;
import de.ivu.elect.business.gebietsbaum.boundary.GebietExtendedDAO;
import de.ivu.elect.business.gebietsbaum.entity.Wahldurchfuehrungsgebiet;
import de.ivu.elect.business.gruppe.entity.Gruppe;
import de.ivu.elect.business.gruppe.entity.GruppeKumulierteStimmen;
import de.ivu.elect.business.gruppe.entity.NlGruppeKumulierteStimmen;
import de.ivu.elect.business.gruppe.entity.NlSystemGruppe;
import de.ivu.elect.business.gruppe.entity.SystemGruppe;
import de.ivu.elect.business.gruppeimgebiet.entity.GruppeImGebiet;
import de.ivu.elect.business.internet.control.GebietUndStimmabgabeart;
import de.ivu.elect.business.stimmeingabe.control.NlStimmeingabeKopfzahlKonfiguration;
import de.ivu.elect.business.stimmeingabe.control.StimmeingabeGruppeStimmenErfassenLabelGenerator;
import de.ivu.elect.business.stimmzettel.entity.EntityAufStimmzettelDaten;
import de.ivu.elect.business.wahl.entity.Wahl;
import de.ivu.elect.business.wahl.entity.WahlartErgebnisart;
import de.ivu.elect.business.wahlvorschlag.entity.Wahlvorschlag;


import static org.apache.commons.lang3.StringUtils.isAllBlank;

@Specializes
public class NlGruppeKumulierteStimmenSearch extends GruppeKumulierteStimmenSearch {

  @Inject
  protected transient NlStimmeingabeKopfzahlKonfiguration nlStimmeingabeKopfzahlKonfiguration;

  @Inject
  private transient GebietExtendedDAO gebietExtendedDAO;

  public NlGruppeKumulierteStimmenSearch() {
    super();
  }

  @Override
  protected List<GruppeKumulierteStimmen> collectGruppeKumulierteStimmen(Wahl wahl, WahlartErgebnisart ergebnisart, Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, StimmenPool stimmenPool,
      StimmenPool stimmenPoolFuerDifferenz, GruppeKumulierteStimmenSearchParameters searchParameters, Map<Gruppe, Set<Wahlvorschlag>> wahlvorschlaegeInWahlPerGruppe,
      List<GruppeImGebiet> gruppenImGebietAufGebiet, StimmeingabeGruppeStimmenErfassenLabelGenerator stimmeingabeGruppeStimmenErfassenLabelGenerator,
      Map<Gruppe, Set<GruppeImGebiet>> gigInGebietshierachieUebergeordnetNachGruppen) {

    List<GruppeKumulierteStimmen> parentResult = super.collectGruppeKumulierteStimmen(wahl, ergebnisart, wahldurchfuehrungsgebiet, stimmenPool, stimmenPoolFuerDifferenz, searchParameters,
        wahlvorschlaegeInWahlPerGruppe, gruppenImGebietAufGebiet, stimmeingabeGruppeStimmenErfassenLabelGenerator, gigInGebietshierachieUebergeordnetNachGruppen);

    List<GruppeKumulierteStimmen> result = new ArrayList<>();

    Long anzahlListenstimmenGewichtetInsgesamt = 0L;

    List<GruppeKumulierteStimmen> systemGruppeKumulierteStimmenGueltige = new ArrayList<>();
    List<GruppeKumulierteStimmen> systemGruppeKumulierteStimmenBlanko = new ArrayList<>();
    List<GruppeKumulierteStimmen> systemGruppeKumulierteStimmenUngueltige = new ArrayList<>();

    List<GruppeKumulierteStimmen> systemGruppeKumulierteStimmenStimmzettelEingenommen = new ArrayList<>();
    List<GruppeKumulierteStimmen> systemGruppeKumulierteStimmenStimmzettelEingenommenAbweichend = new ArrayList<>();

    // ELECTVAPP-620
    // - Anteil der Stimmzettel eingenommen gesamt bezieht sich auf Anzahl Wahlberechtigte gesamt
    // - Anteile der gültigen, ungültigen und leeren Stimmzettel/Stimmen beziehen sich auf Anzahl Stimmzettel eingenommen gesamt

    Long anzahlWahlberechtigteGesamt = getAnzahlWahlberechtigteGesamt(wahldurchfuehrungsgebiet, stimmenPool);
    Long anzahlStimmzettelEingenommenGesamt = getAnzahlStimmzettelEingenommenGesamt(wahldurchfuehrungsgebiet, stimmenPool);

    NlStimmenGewichtetKumulierer nlStimmenGewichtetKumulierer = new NlStimmenGewichtetKumulierer(wahl, () -> gebietExtendedDAO.findByWahl(wahl));
    GebietUndStimmabgabeart gebietUndStimmabgabeart = new GebietUndStimmabgabeart(wahldurchfuehrungsgebiet, Stimmabgabeart.GESAMT); // Stimmabgabeart wird hier nicht verwendet und ist deshalb egal

    for (GruppeImGebiet gruppeImGebiet : gruppenImGebietAufGebiet) {
      if (!gruppeImGebiet.isSystemGruppe() && isGruppeImGebietApplicable(gruppeImGebiet)) {
        Gruppe gruppe = gruppeImGebiet.getGruppe();
        GruppeKumulierteStimmen parentGruppeKumulierteStimmen = parentResult.stream().filter(gruppeKumulierteStimmen -> gruppe.equals(gruppeKumulierteStimmen.getGruppe())).findFirst().orElse(null);
        Long anzahlListenstimmenGewichtet = nlStimmenGewichtetKumulierer.getSummeListenstimmenGewichtetMitVoteValues(gebietUndStimmabgabeart, (voteValueGebietUndStimmabgabeart, voteValueFct) -> wichteListenstimmen(parentGruppeKumulierteStimmen, voteValueGebietUndStimmabgabeart, voteValueFct, stimmenPool));
        anzahlListenstimmenGewichtetInsgesamt += anzahlListenstimmenGewichtet;
        result.add(new NlGruppeKumulierteStimmen(parentGruppeKumulierteStimmen, anzahlListenstimmenGewichtet));
      }

      if (isSystemGruppeGueltige(gruppeImGebiet)) {
        systemGruppeKumulierteStimmenGueltige.addAll(collectSystemGruppeKumulierteStimmen(ergebnisart, wahldurchfuehrungsgebiet, NlSystemGruppe.GUELTIGE, gruppeImGebiet, 1, stimmenPool,
            stimmenPoolFuerDifferenz, anzahlWahlberechtigteGesamt, anzahlStimmzettelEingenommenGesamt, gruppenImGebietAufGebiet, stimmeingabeGruppeStimmenErfassenLabelGenerator, nlStimmenGewichtetKumulierer, gebietUndStimmabgabeart));
      }

      if (isSystemGruppeBlanko(gruppeImGebiet)) {
        systemGruppeKumulierteStimmenBlanko.addAll(collectSystemGruppeKumulierteStimmen(ergebnisart, wahldurchfuehrungsgebiet, NlSystemGruppe.BLANKO, gruppeImGebiet, 2, stimmenPool,
            stimmenPoolFuerDifferenz, anzahlWahlberechtigteGesamt, anzahlStimmzettelEingenommenGesamt, gruppenImGebietAufGebiet, stimmeingabeGruppeStimmenErfassenLabelGenerator, nlStimmenGewichtetKumulierer, gebietUndStimmabgabeart));
      }

      if (isSystemGruppeUngueltige(gruppeImGebiet)) {
        systemGruppeKumulierteStimmenUngueltige.addAll(collectSystemGruppeKumulierteStimmen(ergebnisart, wahldurchfuehrungsgebiet, NlSystemGruppe.UNGUELTIGE, gruppeImGebiet, 3, stimmenPool,
            stimmenPoolFuerDifferenz, anzahlWahlberechtigteGesamt, anzahlStimmzettelEingenommenGesamt, gruppenImGebietAufGebiet, stimmeingabeGruppeStimmenErfassenLabelGenerator, nlStimmenGewichtetKumulierer, gebietUndStimmabgabeart));
      }

      if (isSystemGruppeStimmzettelEingenommen(gruppeImGebiet)) {
        systemGruppeKumulierteStimmenStimmzettelEingenommen.addAll(collectSystemGruppeKumulierteStimmen(ergebnisart, wahldurchfuehrungsgebiet, NlSystemGruppe.STIMMZETTEL_EINGENOMMEN, gruppeImGebiet,
            4, stimmenPool, stimmenPoolFuerDifferenz, anzahlWahlberechtigteGesamt, anzahlStimmzettelEingenommenGesamt, gruppenImGebietAufGebiet, stimmeingabeGruppeStimmenErfassenLabelGenerator, nlStimmenGewichtetKumulierer, gebietUndStimmabgabeart));
      }

      if (isSystemGruppeStimmzettelEingenommenAbweichend(gruppeImGebiet)) {
        systemGruppeKumulierteStimmenStimmzettelEingenommenAbweichend
            .addAll(collectSystemGruppeKumulierteStimmen(ergebnisart, wahldurchfuehrungsgebiet, NlSystemGruppe.STIMMZETTEL_EINGENOMMEN_ABWEICHEND, gruppeImGebiet, 5, stimmenPool,
                stimmenPoolFuerDifferenz, anzahlWahlberechtigteGesamt, anzahlStimmzettelEingenommenGesamt, gruppenImGebietAufGebiet, stimmeingabeGruppeStimmenErfassenLabelGenerator, nlStimmenGewichtetKumulierer, gebietUndStimmabgabeart));
      }
    }

    if (systemGruppeKumulierteStimmenStimmzettelEingenommen.isEmpty()) { // ELECTVAPP-856, ELECTVAPP-862
      gruppenImGebietAufGebiet.stream() //
          .filter(this::isSystemGruppeStimmzettelEingenommen).findAny()
          .map(gruppeImGebiet -> createSystemGruppeKumulierteStimmen(ergebnisart, NlSystemGruppe.STIMMZETTEL_EINGENOMMEN, Stimmenart.GESAMT_EINGENOMMEN, gruppeImGebiet, 4,
              getMessage("DEFAULT.UEBRIGE.ANZAHL.GESAMT_EINGENOMMEN_BERECHNET.kennung"), getMessage("DEFAULT.UEBRIGE.ANZAHL.GESAMT_EINGENOMMEN_BERECHNET.bezeichnung"),
              anzahlStimmzettelEingenommenGesamt, null, null, anzahlWahlberechtigteGesamt, anzahlStimmzettelEingenommenGesamt, gruppenImGebietAufGebiet, stimmeingabeGruppeStimmenErfassenLabelGenerator)) //
          .ifPresent(systemGruppeKumulierteStimmenStimmzettelEingenommen::add);
    }

    result.addAll(systemGruppeKumulierteStimmenGueltige);
    result.addAll(systemGruppeKumulierteStimmenBlanko);
    result.addAll(systemGruppeKumulierteStimmenUngueltige);

    result.addAll(systemGruppeKumulierteStimmenStimmzettelEingenommen);
    result.addAll(systemGruppeKumulierteStimmenStimmzettelEingenommenAbweichend);

    Long anzahlAbgegebeneStimmenGewichtet = systemGruppeKumulierteStimmenStimmzettelEingenommen.stream()
        .map(gruppeKumulierteStimmen -> ((NlGruppeKumulierteStimmen) gruppeKumulierteStimmen).getAnzahlListenstimmenGewichtet())
        .filter(Objects::nonNull) // ELECTVAPP-1706
        .findFirst()
        .orElse(null);

    for (GruppeKumulierteStimmen gruppeKumulierteStimmen : result) {
      if (gruppeKumulierteStimmen instanceof NlGruppeKumulierteStimmen) {
        NlGruppeKumulierteStimmen nlGruppeKumulierteStimmen = (NlGruppeKumulierteStimmen) gruppeKumulierteStimmen;
        if (gruppeKumulierteStimmen.isSystemGruppe()) {
          if (NlSystemGruppe.GUELTIGE.equalsSystemGruppe(gruppeKumulierteStimmen.getGruppe(), getSystemGruppeEigenschaften())
              || NlSystemGruppe.UNGUELTIGE.equalsSystemGruppe(gruppeKumulierteStimmen.getGruppe(), getSystemGruppeEigenschaften())
              || systemGruppeKumulierteStimmenBlanko.contains(
              nlGruppeKumulierteStimmen)) { // NOTE: Test mit NlSystemGruppe.BLANKO.equalsSystemGruppe() ist nicht eindeutig
            nlGruppeKumulierteStimmen.setAnzahlListenstimmenGewichtetAllerGruppen(anzahlAbgegebeneStimmenGewichtet);
          }
        } else {
          nlGruppeKumulierteStimmen.setAnzahlListenstimmenGewichtetAllerGruppen(anzahlListenstimmenGewichtetInsgesamt);
        }
      }
    }

    return result;
  }

  private long wichteListenstimmen(GruppeKumulierteStimmen kumulierteStimmen, GebietUndStimmabgabeart voteValueGebietUndStimmabgabeart, ToLongFunction<Wahldurchfuehrungsgebiet> voteValueFct, StimmenPool stimmenPool) {
    Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet = voteValueGebietUndStimmabgabeart.getWahldurchfuehrungsgebiet();

    Long summeListenstimmen = stimmenKumulierer.getSummeListenstimmenFuerPolitischeGruppeAufGebietUndDarunter(kumulierteStimmen.getGruppe(), wahldurchfuehrungsgebiet, stimmenPool);

    if (summeListenstimmen == null) {
      return 0L;
    }

    long wichtung = voteValueFct.applyAsLong(wahldurchfuehrungsgebiet);

    return wichtung * summeListenstimmen;
  }

  private List<GruppeKumulierteStimmen> collectSystemGruppeKumulierteStimmen(WahlartErgebnisart ergebnisart, Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, NlSystemGruppe nlSystemGruppe,
                                                                             GruppeImGebiet gruppeImGebiet, long positionRelativ, StimmenPool stimmenPool, StimmenPool stimmenPoolFuerDifferenz, Long anzahlWahlberechtigteGesamt, Long anzahlStimmzettelEingenommenGesamt,
                                                                             List<GruppeImGebiet> gruppenImGebietAufGebiet, StimmeingabeGruppeStimmenErfassenLabelGenerator stimmeingabeGruppeStimmenErfassenLabelGenerator, NlStimmenGewichtetKumulierer nlStimmenGewichtetKumulierer, GebietUndStimmabgabeart gebietUndStimmabgabeart) {
    List<GruppeKumulierteStimmen> result = new ArrayList<>();

    EntityAufStimmzettelDaten entityAufStimmzettelDaten = getEntityAufStimmzettelDaten();

    for (Stimmenart stimmenart : nlSystemGruppe.getStimmenarten()) {
      if (isSystemGruppeRelevant(wahldurchfuehrungsgebiet, ergebnisart, nlSystemGruppe, stimmenart)) {
        String kennung = stimmeingabeGruppeStimmenErfassenLabelGenerator.getGruppeStimmenKennung(entityAufStimmzettelDaten, gruppeImGebiet, ergebnisart, stimmenart, gruppenImGebietAufGebiet);
        String bezeichnung = stimmeingabeGruppeStimmenErfassenLabelGenerator.getGruppeStimmenBezeichnung(entityAufStimmzettelDaten, gruppeImGebiet, ergebnisart, stimmenart, gruppenImGebietAufGebiet);

        Long anzahlStimmen = getAnzahlSystemGruppeStimmen(wahldurchfuehrungsgebiet, stimmenPool, nlSystemGruppe, stimmenart);
        Long anzahlStimmenGewichtet = nlStimmenGewichtetKumulierer.getSummeListenstimmenGewichtetMitVoteValues(gebietUndStimmabgabeart, (voteValueGebietUndStimmabgabeart, voteValueFct) -> wichteSystemGruppeStimmen(nlSystemGruppe, stimmenart, voteValueGebietUndStimmabgabeart, voteValueFct, stimmenPool));
        Long anzahlStimmenFuerDifferenz = getAnzahlSystemGruppeStimmen(wahldurchfuehrungsgebiet, stimmenPoolFuerDifferenz, nlSystemGruppe, stimmenart);

        GruppeKumulierteStimmen gruppeKumulierteStimmen = createSystemGruppeKumulierteStimmen(ergebnisart, nlSystemGruppe, stimmenart, gruppeImGebiet, positionRelativ, kennung, bezeichnung,
            anzahlStimmen, anzahlStimmenGewichtet, anzahlStimmenFuerDifferenz, anzahlWahlberechtigteGesamt, anzahlStimmzettelEingenommenGesamt, gruppenImGebietAufGebiet, stimmeingabeGruppeStimmenErfassenLabelGenerator);

        if (gruppeKumulierteStimmen != null) {
          result.add(gruppeKumulierteStimmen);
        }
      }
    }

    return result;
  }

  private long wichteSystemGruppeStimmen(NlSystemGruppe nlSystemGruppe, Stimmenart stimmenart, GebietUndStimmabgabeart voteValueGebietUndStimmabgabeart, ToLongFunction<Wahldurchfuehrungsgebiet> voteValueFct, StimmenPool stimmenPool) {
    Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet = voteValueGebietUndStimmabgabeart.getWahldurchfuehrungsgebiet();

    Long summeSystemGruppeStimmen = getAnzahlSystemGruppeStimmen(wahldurchfuehrungsgebiet, stimmenPool, nlSystemGruppe, stimmenart);

    if (summeSystemGruppeStimmen == null) {
      return 0L;
    }

    long wichtung = voteValueFct.applyAsLong(wahldurchfuehrungsgebiet);

    return wichtung * summeSystemGruppeStimmen;
  }

  @Nullable
  private GruppeKumulierteStimmen createSystemGruppeKumulierteStimmen(WahlartErgebnisart ergebnisart, NlSystemGruppe nlSystemGruppe, Stimmenart stimmenart, GruppeImGebiet gruppeImGebiet,
      long positionRelativ, String kennung, String bezeichnung, Long anzahlStimmen, Long anzahlStimmenGewichtet, Long anzahlStimmenFuerDifferenz, Long anzahlWahlberechtigteGesamt, Long anzahlStimmzettelEingenommenGesamt,
      List<GruppeImGebiet> gruppenImGebietAufGebiet, StimmeingabeGruppeStimmenErfassenLabelGenerator stimmeingabeGruppeStimmenErfassenLabelGenerator) {
    if (!isAllBlank(kennung, bezeichnung)) {
      Long anzahlStimmenGesamt = anzahlStimmzettelEingenommenGesamt;
      String msgKeyAnteilStimmenGesamtTooltip = "kopfzahl.kumulierte.stimmen.tooltip.anteil.stimmzettel.eingenommen.zu.stimmzettel.eingenommen.gesamt";

      if (NlSystemGruppe.STIMMZETTEL_EINGENOMMEN.equals(nlSystemGruppe) && Stimmenart.GESAMT_EINGENOMMEN.equals(stimmenart)) {
        anzahlStimmenGesamt = anzahlWahlberechtigteGesamt;
        msgKeyAnteilStimmenGesamtTooltip = "kopfzahl.kumulierte.stimmen.tooltip.anteil.stimmzettel.eingenommen.zu.wahlberechtigte.gesamt";
      }

      Long anzahlDirektstimmen = null;
      Long anzahlDirektstimmenFuerDifferenz = null;
      boolean anzahlDirektstimmenFuerDifferenzVorhanden = false;
      Long anzahlDirektstimmenGesamt = null;
      String anteilDirektstimmenGesamtTooltip = null;

      Long anzahlListenstimmen = anzahlStimmen;
      Long anzahlListenstimmenFuerDifferenz = anzahlStimmenFuerDifferenz;
      boolean anzahlListenstimmenFuerDifferenzVorhanden = anzahlStimmenFuerDifferenz != null && anzahlStimmenFuerDifferenz > 0L;
      Long anzahlListenstimmenGesamt = anzahlStimmenGesamt;
      String anteilListenstimmenGesamtTooltip = getMessage(msgKeyAnteilStimmenGesamtTooltip);

      GruppeKumulierteStimmen gruppeKumulierteStimmen = createGruppeKumulierteStimmen(gruppeImGebiet, anzahlDirektstimmen, anzahlListenstimmen, null, null,null, Collections.emptyMap()/* Systemgruppe */);

      if (NlSystemGruppe.GUELTIGE.equals(nlSystemGruppe) || NlSystemGruppe.UNGUELTIGE.equals(nlSystemGruppe)
          || NlSystemGruppe.BLANKO.equals(nlSystemGruppe) || NlSystemGruppe.STIMMZETTEL_EINGENOMMEN.equals(nlSystemGruppe)) { // nur Stimmenwerte gewichtet anzeigen
        gruppeKumulierteStimmen = new NlGruppeKumulierteStimmen(gruppeKumulierteStimmen, anzahlStimmenGewichtet);
      }

      gruppeKumulierteStimmen.setAnzahlDirektstimmenVorperiode(anzahlDirektstimmenFuerDifferenz);
      gruppeKumulierteStimmen.setAnzahlDirektstimmenVorperiodeVorhanden(anzahlDirektstimmenFuerDifferenzVorhanden);
      gruppeKumulierteStimmen.setAnzahlListenstimmenVorperiode(anzahlListenstimmenFuerDifferenz);
      gruppeKumulierteStimmen.setAnzahlListenstimmenVorperiodeVorhanden(anzahlListenstimmenFuerDifferenzVorhanden);

      long positionZurSortierung = getSystemGruppePosition(gruppeKumulierteStimmen);
      long positionOffset = positionRelativ;

      if (positionZurSortierung < 0) {
        positionOffset = -1 * (1000 / positionRelativ);
      }

      positionZurSortierung += positionOffset;

      gruppeKumulierteStimmen.setPosition(positionZurSortierung);
      gruppeKumulierteStimmen.setKennung(kennung);
      gruppeKumulierteStimmen.setName(bezeichnung);

      if (!NlSystemGruppe.STIMMZETTEL_EINGENOMMEN_ABWEICHEND.equals(nlSystemGruppe)) {
        gruppeKumulierteStimmen.setAnzahlDirektstimmenAllerGruppen(anzahlDirektstimmenGesamt);
        gruppeKumulierteStimmen.setAnteilDirektstimmenTooltip(anteilDirektstimmenGesamtTooltip);
        gruppeKumulierteStimmen.setAnzahlListenstimmenAllerGruppen(anzahlListenstimmenGesamt);
        gruppeKumulierteStimmen.setAnteilListenstimmenTooltip(anteilListenstimmenGesamtTooltip);
      }

      return gruppeKumulierteStimmen;
    }

    return null;
  }

  private boolean isSystemGruppeRelevant(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, WahlartErgebnisart ergebnisart, NlSystemGruppe nlSystemGruppe, Stimmenart stimmenart) {
    return nlStimmeingabeKopfzahlKonfiguration.isGruppeMitStimmenartBeiStimmeingabeRelevant(wahldurchfuehrungsgebiet, ergebnisart, nlSystemGruppe, stimmenart);
  }

  private boolean isSystemGruppeBlanko(GruppeImGebiet gruppeImGebiet) {
    return gruppeImGebiet != null && isSystemGruppeBlanko(gruppeImGebiet.getGruppe());
  }

  private boolean isSystemGruppeBlanko(Gruppe gruppe) {
    return NlSystemGruppe.BLANKO.equalsSystemGruppe(gruppe, getSystemGruppeEigenschaften());
  }

  private boolean isSystemGruppeStimmzettelEingenommen(GruppeImGebiet gruppeImGebiet) {
    return gruppeImGebiet != null && isSystemGruppeStimmzettelEingenommen(gruppeImGebiet.getGruppe());
  }

  private boolean isSystemGruppeStimmzettelEingenommen(Gruppe gruppe) {
    return NlSystemGruppe.STIMMZETTEL_EINGENOMMEN.equalsSystemGruppe(gruppe, getSystemGruppeEigenschaften());
  }

  private boolean isSystemGruppeStimmzettelEingenommenAbweichend(GruppeImGebiet gruppeImGebiet) {
    return gruppeImGebiet != null && isSystemGruppeStimmzettelEingenommenAbweichend(gruppeImGebiet.getGruppe());
  }

  private boolean isSystemGruppeStimmzettelEingenommenAbweichend(Gruppe gruppe) {
    return NlSystemGruppe.STIMMZETTEL_EINGENOMMEN_ABWEICHEND.equalsSystemGruppe(gruppe, getSystemGruppeEigenschaften());
  }

  private long getAnzahlWahlberechtigteGesamt(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, StimmenPool stimmenPool) {
    return getAnzahlSystemGruppeStimmen(wahldurchfuehrungsgebiet, stimmenPool, NlSystemGruppe.WAHLBERECHTIGTE, Stimmenart.GESAMT);
  }

  private long getAnzahlStimmzettelEingenommenGesamt(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, StimmenPool stimmenPool) {
    long result = getAnzahlSystemGruppeStimmen(wahldurchfuehrungsgebiet, stimmenPool, NlSystemGruppe.STIMMZETTEL_EINGENOMMEN, Stimmenart.GESAMT_EINGENOMMEN);

    if (result > 0) {
      return result;
    }

    // ELECT-11104, cf. NlErgebnisDatenStimmzettel#getSummeStimmzettelEingenommenGesamt!!!
    return getAnzahlSystemGruppeStimmen(wahldurchfuehrungsgebiet, stimmenPool, NlSystemGruppe.GUELTIGE, Stimmenart.LISTENSTIMME) //
        + getAnzahlSystemGruppeStimmen(wahldurchfuehrungsgebiet, stimmenPool, NlSystemGruppe.UNGUELTIGE, Stimmenart.LISTENSTIMME) //
        + getAnzahlSystemGruppeStimmen(wahldurchfuehrungsgebiet, stimmenPool, NlSystemGruppe.BLANKO, Stimmenart.LISTENSTIMME_LEER);
  }

  private long getAnzahlSystemGruppeStimmen(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, StimmenPool stimmenPool, NlSystemGruppe nlSystemGruppe, Stimmenart stimmenart) {
    return getAnzahlSystemGruppeStimmen(wahldurchfuehrungsgebiet, stimmenPool, nlSystemGruppe.getSystemGruppe(), stimmenart);
  }

  private long getAnzahlSystemGruppeStimmen(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, StimmenPool stimmenPool, SystemGruppe systemGruppe, Stimmenart stimmenart) {
    return getStimmenKumulierer().bestimmeSummeSystemGruppenStimmenAufGebiet(stimmenPool, wahldurchfuehrungsgebiet, systemGruppe, stimmenart);
  }
}
