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

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

import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Supplier;

import javax.annotation.Nonnull;

import de.ivu.elect.business.ergebnis.control.StimmenPool;
import de.ivu.elect.business.ergebnis.entity.Ergebnis;
import de.ivu.elect.business.ergebnis.entity.Stimmabgabeart;
import de.ivu.elect.business.ergebnis.entity.Stimmen;
import de.ivu.elect.business.gebietsbaum.control.GebietByBreadthFirstComparator;
import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
import de.ivu.elect.business.gebietsbaum.entity.Wahldurchfuehrungsgebiet;
import de.ivu.elect.business.gruppe.entity.AbstractGruppe;
import de.ivu.elect.business.gruppe.entity.Gruppe;
import de.ivu.elect.business.gruppe.entity.GruppeKumulierteStimmen;
import de.ivu.elect.business.gruppe.entity.SystemGruppe;
import de.ivu.elect.business.gruppeimgebiet.entity.GruppeImGebiet;
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 de.ivu.elect.util.NullComparator;

public class GruppeNachErfassungsgebietStimmenSearch extends AbstractGruppeStimmenSearch<GruppeNachErfassungsgebietStimmenSearchParameters> {

  private static final Comparator<Object> NULL_COMPARATOR = NullComparator.NON_NULL_BEFORE_NULL_COMPARATOR;

  private static final Comparator<Gebiet> GEBIET_COMPARATOR = GebietByBreadthFirstComparator.INSTANCE;

  public GruppeNachErfassungsgebietStimmenSearch() {
    super();
  }

  @Override // ELECT-9401
  protected Comparator< ? super GruppeKumulierteStimmen> getDefaultComparator() {
    return (one, another) -> {
      if (one == null || another == null) {
        return NULL_COMPARATOR.compare(one, another);
      }

      return GEBIET_COMPARATOR.compare(one.getGebiet(), another.getGebiet());
    };
  }

  @Override
  protected List<GruppeKumulierteStimmen> getGruppeKumulierteStimmenInUntergeordneterGebietshierarchie(Wahl wahl, WahlartErgebnisart ergebnisart, Gebiet gebietSelected, StimmenPool stimmenPool,
      StimmenPool stimmenPoolFuerDifferenz, List<Wahlvorschlag> alleWahlvorschlaege, Gruppe gruppeGueltige, GruppeNachErfassungsgebietStimmenSearchParameters searchParameters,
      Supplier<Set<Gebiet>> erfassteGebiete, Map<Gruppe, Set<GruppeImGebiet>> gigNachGruppen) {
    List<GruppeKumulierteStimmen> result = new ArrayList<>();

    List<Gruppe> gruppenSelected = gruppeExtendedDAO.attachAll(searchParameters.getGruppen());

    if (gruppenSelected.isEmpty()) {
      return result;
    }

    for (Gebiet gebiet : searchParameters.getStimmabgabeart().filterGebieteWennErfassungsgebiet(searchParameters.getGebietsauswahl().getGebiete(gebietSelected), erfassteGebiete)) {
      Wahldurchfuehrungsgebiet erfassungsgebiet = gebiet.castTo(Wahldurchfuehrungsgebiet.class).getEntity();

      Long anzahlDirektstimmenImGebietInsgesamt = getStimmenKumulierer().getSummeDirektstimmenFuerPolitischeGruppeAufGebietUndDarunter(gruppeGueltige, erfassungsgebiet, stimmenPool);
      Long anzahlDirektstimmenFuerDifferenzImGebietInsgesamt = getStimmenKumulierer().getSummeDirektstimmenFuerPolitischeGruppeAufGebietUndDarunter(gruppeGueltige, erfassungsgebiet,
          stimmenPoolFuerDifferenz);
      boolean anzahlDirektstimmenFuerDifferenzImGebietInsgesamtVorhanden = !stimmenPoolFuerDifferenz.getDirektstimmen(gruppeGueltige).isEmpty();

      Long anzahlListenstimmenImGebietInsgesamt = getStimmenKumulierer().getSummeListenstimmenFuerPolitischeGruppeAufGebietUndDarunter(gruppeGueltige, erfassungsgebiet, stimmenPool);
      Long anzahlListenstimmenFuerDifferenzImGebietInsgesamt = getStimmenKumulierer().getSummeListenstimmenFuerPolitischeGruppeAufGebietUndDarunter(gruppeGueltige, erfassungsgebiet,
          stimmenPoolFuerDifferenz);
      boolean anzahlListenstimmenFuerDifferenzImGebietInsgesamtVorhanden = !stimmenPoolFuerDifferenz.getListenstimmenDerGruppe(gruppeGueltige).isEmpty();

      Long anzahlStimmzettelNurEinzelnerWahlvorschlagUnveraendertImGebietInsgesamt = getStimmenKumulierer()
          .getSummeListenstimmenEinzelnerWahlvorschlagUnveraendertFuerPolitischeGruppeAufGebietUndDarunter(gruppeGueltige, erfassungsgebiet, stimmenPool);
      Long anzahlStimmzettelNurEinzelnerWahlvorschlagUnveraendertFuerDifferenzImGebietInsgesamt = getStimmenKumulierer()
          .getSummeListenstimmenEinzelnerWahlvorschlagUnveraendertFuerPolitischeGruppeAufGebietUndDarunter(gruppeGueltige, erfassungsgebiet, stimmenPoolFuerDifferenz);

      Long anzahlStimmzettelNurEinzelnerWahlvorschlagVeraendertImGebietInsgesamt = getStimmenKumulierer()
          .getSummeListenstimmenEinzelnerWahlvorschlagVeraendertFuerPolitischeGruppeAufGebietUndDarunter(gruppeGueltige, erfassungsgebiet, stimmenPool);
      Long anzahlStimmzettelNurEinzelnerWahlvorschlagVeraendertFuerDifferenzImGebietInsgesamt = getStimmenKumulierer()
          .getSummeListenstimmenEinzelnerWahlvorschlagVeraendertFuerPolitischeGruppeAufGebietUndDarunter(gruppeGueltige, erfassungsgebiet, stimmenPoolFuerDifferenz);

      Long anzahlWahlberechtigteGesamt = null;
      Long anzahlWahlberechtigteGesamtFuerDifferenz = null;
      boolean anzahlWahlberechtigteGesamtFuerDifferenzVorhanden = false;

      Long anzahlWaehlerGesamt = null;
      Long anzahlWaehlerGesamtFuerDifferenz = null;
      boolean anzahlWaehlerGesamtFuerDifferenzVorhanden = false;

      for (Gruppe gruppeSelected : gruppenSelected) {
        GruppeKumulierteStimmen gruppeKumulierteStimmen;

        if (isSystemGruppeWaehler(gruppeSelected)) {
          // Wähler werden nicht nach erst und Zweitstimmen unterschieden, der Vergleichswert sind die Wahlberechtigten

          if (anzahlWaehlerGesamt == null) {
            anzahlWaehlerGesamt = getStimmenKumulierer().getSummeWaehlerGesamtStimmenAufGebiet(stimmenPool, gebiet);
          }

          if (anzahlWaehlerGesamtFuerDifferenz == null) {
            anzahlWaehlerGesamtFuerDifferenz = getStimmenKumulierer().getSummeWaehlerGesamtStimmenAufGebiet(stimmenPoolFuerDifferenz, gebiet);
            anzahlWaehlerGesamtFuerDifferenzVorhanden = anzahlWaehlerGesamtFuerDifferenz != null && anzahlWaehlerGesamtFuerDifferenz > 0L;
          }

          if (anzahlWahlberechtigteGesamt == null) {
            anzahlWahlberechtigteGesamt = getStimmenKumulierer().getSummeWahlberechtigteGesamtAusStimmen(stimmenPool, erfassungsgebiet);
          }

          if (anzahlWahlberechtigteGesamtFuerDifferenz == null) {
            anzahlWahlberechtigteGesamtFuerDifferenz = getStimmenKumulierer().getSummeWahlberechtigteGesamtAusStimmen(stimmenPoolFuerDifferenz, erfassungsgebiet);
            anzahlWahlberechtigteGesamtFuerDifferenzVorhanden = anzahlWahlberechtigteGesamtFuerDifferenz != null && anzahlWahlberechtigteGesamtFuerDifferenz > 0L;
          }

          gruppeKumulierteStimmen = createGruppeKumulierteStimmen(gruppeSelected, erfassungsgebiet, anzahlWaehlerGesamt, gigNachGruppen);

          gruppeKumulierteStimmen.setAnzahlDirektstimmenVorperiode(anzahlWaehlerGesamtFuerDifferenz);
          gruppeKumulierteStimmen.setAnzahlDirektstimmenVorperiodeVorhanden(anzahlWaehlerGesamtFuerDifferenzVorhanden);
          gruppeKumulierteStimmen.setAnzahlListenstimmenVorperiode(anzahlWaehlerGesamtFuerDifferenz);
          gruppeKumulierteStimmen.setAnzahlListenstimmenVorperiodeVorhanden(anzahlWaehlerGesamtFuerDifferenzVorhanden);

          gruppeKumulierteStimmen.setAnzahlStimmzettelNurEinzelnerWahlvorschlagUnveraendertVorperiode(null);
          gruppeKumulierteStimmen.setAnzahlStimmzettelNurEinzelnerWahlvorschlagVeraendertVorperiode(null);

          // "Alle" sind bei Wählern die Wahlberechtigten
          gruppeKumulierteStimmen.setAnzahlDirektstimmenAllerGruppen(anzahlWahlberechtigteGesamt);
          gruppeKumulierteStimmen.setAnzahlListenstimmenAllerGruppen(anzahlWahlberechtigteGesamt);

          gruppeKumulierteStimmen.setAnzahlStimmzettelNurEinzelnerWahlvorschlagUnveraendertAllerGruppen(null);
          gruppeKumulierteStimmen.setAnzahlStimmzettelNurEinzelnerWahlvorschlagVeraendertAllerGruppen(null);

          gruppeKumulierteStimmen.setAnzahlDirektstimmenVorperiodeAllerGruppen(anzahlWahlberechtigteGesamtFuerDifferenz);
          gruppeKumulierteStimmen.setAnzahlDirektstimmenVorperiodeAllerGruppenVorhanden(anzahlWahlberechtigteGesamtFuerDifferenzVorhanden);
          gruppeKumulierteStimmen.setAnzahlListenstimmenVorperiodeAllerGruppen(anzahlWahlberechtigteGesamtFuerDifferenz);
          gruppeKumulierteStimmen.setAnzahlListenstimmenVorperiodeAllerGruppenVorhanden(anzahlWahlberechtigteGesamtFuerDifferenzVorhanden);

          gruppeKumulierteStimmen.setAnzahlStimmzettelNurEinzelnerWahlvorschlagUnveraendertVorperiodeAllerGruppen(null);
          gruppeKumulierteStimmen.setAnzahlStimmzettelNurEinzelnerWahlvorschlagVeraendertVorperiodeAllerGruppen(null);
        } else if (isSystemGruppeWahlberechtigte(gruppeSelected)) {
          // Wahlberechtigte haben keine Unterscheidung zwischen Erst- und Zweitstimmen
          if (anzahlWahlberechtigteGesamt == null) {
            anzahlWahlberechtigteGesamt = getStimmenKumulierer().getSummeWahlberechtigteGesamtAusStimmen(stimmenPool, erfassungsgebiet);
          }

          if (anzahlWahlberechtigteGesamtFuerDifferenz == null) {
            anzahlWahlberechtigteGesamtFuerDifferenz = getStimmenKumulierer().getSummeWahlberechtigteGesamtAusStimmen(stimmenPoolFuerDifferenz, erfassungsgebiet);
            anzahlWahlberechtigteGesamtFuerDifferenzVorhanden = anzahlWahlberechtigteGesamtFuerDifferenz != null && anzahlWahlberechtigteGesamtFuerDifferenz > 0L;
          }

          gruppeKumulierteStimmen = createGruppeKumulierteStimmen(gruppeSelected, erfassungsgebiet, anzahlWahlberechtigteGesamt, gigNachGruppen);
          gruppeKumulierteStimmen.setAnzahlDirektstimmenVorperiode(anzahlWahlberechtigteGesamtFuerDifferenz);
          gruppeKumulierteStimmen.setAnzahlDirektstimmenVorperiodeVorhanden(anzahlWahlberechtigteGesamtFuerDifferenzVorhanden);
          gruppeKumulierteStimmen.setAnzahlListenstimmenVorperiode(anzahlWahlberechtigteGesamtFuerDifferenz);
          gruppeKumulierteStimmen.setAnzahlListenstimmenVorperiodeVorhanden(anzahlWahlberechtigteGesamtFuerDifferenzVorhanden);

          gruppeKumulierteStimmen.setAnzahlStimmzettelNurEinzelnerWahlvorschlagUnveraendertVorperiode(null);
          gruppeKumulierteStimmen.setAnzahlStimmzettelNurEinzelnerWahlvorschlagVeraendertVorperiode(null);

          // "Alle" gibt es bei Wahlberechtigten nicht
          gruppeKumulierteStimmen.setAnzahlDirektstimmenAllerGruppen(null);
          gruppeKumulierteStimmen.setAnzahlListenstimmenAllerGruppen(null);

          gruppeKumulierteStimmen.setAnzahlStimmzettelNurEinzelnerWahlvorschlagUnveraendertAllerGruppen(null);
          gruppeKumulierteStimmen.setAnzahlStimmzettelNurEinzelnerWahlvorschlagVeraendertAllerGruppen(null);

          gruppeKumulierteStimmen.setAnzahlDirektstimmenVorperiodeAllerGruppen(null);
          gruppeKumulierteStimmen.setAnzahlDirektstimmenVorperiodeAllerGruppenVorhanden(false);
          gruppeKumulierteStimmen.setAnzahlListenstimmenVorperiodeAllerGruppen(null);
          gruppeKumulierteStimmen.setAnzahlListenstimmenVorperiodeAllerGruppenVorhanden(false);

          gruppeKumulierteStimmen.setAnzahlStimmzettelNurEinzelnerWahlvorschlagUnveraendertVorperiodeAllerGruppen(null);
          gruppeKumulierteStimmen.setAnzahlStimmzettelNurEinzelnerWahlvorschlagVeraendertVorperiodeAllerGruppen(null);
        } else {
          Long anzahlDirektstimmen = bestimmeDirektstimmenDerGruppeImGebiet(stimmenPool, erfassungsgebiet, gruppeSelected);
          Long anzahlDirektstimmenFuerDifferenz = bestimmeDirektstimmenFuerDifferenzDerGruppeImGebiet(stimmenPoolFuerDifferenz, erfassungsgebiet, gruppeSelected);
          boolean anzahlDirektstimmenFuerDifferenzVorhanden = !stimmenPoolFuerDifferenz.getDirektstimmen(gruppeSelected).isEmpty();

          Long anzahlListenstimmen = bestimmeListenstimmenDerGruppeImGebiet(stimmenPool, erfassungsgebiet, gruppeSelected);
          Long anzahlListenstimmenFuerDifferenz = bestimmeListenstimmenFuerDifferenzDerGruppeImGebiet(stimmenPoolFuerDifferenz, erfassungsgebiet, gruppeSelected);
          boolean anzahlListenstimmenFuerDifferenzVorhanden = !stimmenPoolFuerDifferenz.getListenstimmenDerGruppe(gruppeSelected).isEmpty();

          Long anzahlStimmzettelNurEinzelnerWahlvorschlagUnveraendert = bestimmeStimmzettelNurEinzelnerWahlvorschlagUnveraendertDerGruppeImGebiet(stimmenPool, erfassungsgebiet, gruppeSelected);
          Long anzahlStimmzettelNurEinzelnerWahlvorschlagUnveraendertFuerDifferenz = bestimmeStimmzettelNurEinzelnerWahlvorschlagUnveraendertFuerDifferenzDerGruppeImGebiet(stimmenPoolFuerDifferenz,
              erfassungsgebiet, gruppeSelected);

          Long anzahlStimmzettelNurEinzelnerWahlvorschlagVeraendert = bestimmeStimmzettelNurEinzelnerWahlvorschlagVeraendertDerGruppeImGebiet(stimmenPool, erfassungsgebiet, gruppeSelected);
          Long anzahlStimmzettelNurEinzelnerWahlvorschlagVeraendertFuerDifferenz = bestimmeStimmzettelNurEinzelnerWahlvorschlagVeraendertFuerDifferenzDerGruppeImGebiet(stimmenPoolFuerDifferenz,
              erfassungsgebiet, gruppeSelected);

          gruppeKumulierteStimmen = createGruppeKumulierteStimmen(gruppeSelected, erfassungsgebiet, anzahlDirektstimmen, anzahlListenstimmen, anzahlStimmzettelNurEinzelnerWahlvorschlagUnveraendert,
              anzahlStimmzettelNurEinzelnerWahlvorschlagVeraendert, gigNachGruppen);

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

          gruppeKumulierteStimmen.setAnzahlStimmzettelNurEinzelnerWahlvorschlagUnveraendertVorperiode(anzahlStimmzettelNurEinzelnerWahlvorschlagUnveraendertFuerDifferenz);
          gruppeKumulierteStimmen.setAnzahlStimmzettelNurEinzelnerWahlvorschlagVeraendertVorperiode(anzahlStimmzettelNurEinzelnerWahlvorschlagVeraendertFuerDifferenz);

          if (isSystemGruppeGueltige(gruppeSelected) || isSystemGruppeUngueltige(gruppeSelected)) {
            // Vergleiche Gültige und Ungültige nicht zu "gueltigen", sondern zu "Wählern gesamt"

            if (anzahlWaehlerGesamt == null) {
              anzahlWaehlerGesamt = getStimmenKumulierer().getSummeWaehlerGesamtStimmenAufGebiet(stimmenPool, gebiet);
            }

            if (anzahlWaehlerGesamtFuerDifferenz == null) {
              anzahlWaehlerGesamtFuerDifferenz = getStimmenKumulierer().getSummeWaehlerGesamtStimmenAufGebiet(stimmenPoolFuerDifferenz, gebiet);
              anzahlWaehlerGesamtFuerDifferenzVorhanden = anzahlWaehlerGesamtFuerDifferenz != null && anzahlWaehlerGesamtFuerDifferenz > 0L;
            }

            gruppeKumulierteStimmen.setAnzahlDirektstimmenAllerGruppen(anzahlWaehlerGesamt);
            gruppeKumulierteStimmen.setAnzahlListenstimmenAllerGruppen(anzahlWaehlerGesamt);

            gruppeKumulierteStimmen.setAnzahlStimmzettelNurEinzelnerWahlvorschlagUnveraendertAllerGruppen(null);
            gruppeKumulierteStimmen.setAnzahlStimmzettelNurEinzelnerWahlvorschlagVeraendertAllerGruppen(null);

            gruppeKumulierteStimmen.setAnzahlDirektstimmenVorperiodeAllerGruppen(anzahlWaehlerGesamtFuerDifferenz);
            gruppeKumulierteStimmen.setAnzahlDirektstimmenVorperiodeAllerGruppenVorhanden(anzahlWaehlerGesamtFuerDifferenzVorhanden);
            gruppeKumulierteStimmen.setAnzahlListenstimmenVorperiodeAllerGruppen(anzahlWaehlerGesamtFuerDifferenz);
            gruppeKumulierteStimmen.setAnzahlListenstimmenVorperiodeAllerGruppenVorhanden(anzahlWaehlerGesamtFuerDifferenzVorhanden);

            gruppeKumulierteStimmen.setAnzahlStimmzettelNurEinzelnerWahlvorschlagUnveraendertVorperiodeAllerGruppen(null);
            gruppeKumulierteStimmen.setAnzahlStimmzettelNurEinzelnerWahlvorschlagVeraendertVorperiodeAllerGruppen(null);
          } else {
            gruppeKumulierteStimmen.setAnzahlDirektstimmenAllerGruppen(anzahlDirektstimmenImGebietInsgesamt);
            gruppeKumulierteStimmen.setAnzahlListenstimmenAllerGruppen(anzahlListenstimmenImGebietInsgesamt);

            gruppeKumulierteStimmen.setAnzahlStimmzettelNurEinzelnerWahlvorschlagUnveraendertAllerGruppen(anzahlStimmzettelNurEinzelnerWahlvorschlagUnveraendertImGebietInsgesamt);
            gruppeKumulierteStimmen.setAnzahlStimmzettelNurEinzelnerWahlvorschlagVeraendertAllerGruppen(anzahlStimmzettelNurEinzelnerWahlvorschlagVeraendertImGebietInsgesamt);

            gruppeKumulierteStimmen.setAnzahlDirektstimmenVorperiodeAllerGruppen(anzahlDirektstimmenFuerDifferenzImGebietInsgesamt);
            gruppeKumulierteStimmen.setAnzahlDirektstimmenVorperiodeAllerGruppenVorhanden(anzahlDirektstimmenFuerDifferenzImGebietInsgesamtVorhanden);
            gruppeKumulierteStimmen.setAnzahlListenstimmenVorperiodeAllerGruppen(anzahlListenstimmenFuerDifferenzImGebietInsgesamt);
            gruppeKumulierteStimmen.setAnzahlListenstimmenVorperiodeAllerGruppenVorhanden(anzahlListenstimmenFuerDifferenzImGebietInsgesamtVorhanden);

            gruppeKumulierteStimmen
                .setAnzahlStimmzettelNurEinzelnerWahlvorschlagUnveraendertVorperiodeAllerGruppen(anzahlStimmzettelNurEinzelnerWahlvorschlagUnveraendertFuerDifferenzImGebietInsgesamt);
            gruppeKumulierteStimmen.setAnzahlStimmzettelNurEinzelnerWahlvorschlagVeraendertVorperiodeAllerGruppen(anzahlStimmzettelNurEinzelnerWahlvorschlagVeraendertFuerDifferenzImGebietInsgesamt);
          }
        }

        if (gruppeSelected.isSystemGruppe()) {
          gruppeKumulierteStimmen.setName(getSystemGruppeEigenschaften().getKurzname(gruppeSelected.getSystemGruppe(getSystemGruppeEigenschaften())));
        } else {
          gruppeKumulierteStimmen.setName(gruppeSelected.getKurzname());
        }

        result.add(gruppeKumulierteStimmen);
      }
    }

    return result;
  }

  private Long bestimmeListenstimmenFuerDifferenzDerGruppeImGebiet(StimmenPool stimmenPoolFuerDifferenz, Wahldurchfuehrungsgebiet erfassungsgebiet, Gruppe gruppeSelected) {
    return getStimmenKumulierer().getSummeListenstimmenFuerPolitischeGruppeAufGebietUndDarunter(gruppeSelected, erfassungsgebiet, stimmenPoolFuerDifferenz);
  }

  private Long bestimmeDirektstimmenFuerDifferenzDerGruppeImGebiet(StimmenPool stimmenPoolFuerDifferenz, Wahldurchfuehrungsgebiet erfassungsgebiet, Gruppe gruppeSelected) {
    return getStimmenKumulierer().getSummeDirektstimmenFuerPolitischeGruppeAufGebietUndDarunter(gruppeSelected, erfassungsgebiet, stimmenPoolFuerDifferenz);
  }

  private Long bestimmeStimmzettelNurEinzelnerWahlvorschlagUnveraendertFuerDifferenzDerGruppeImGebiet(StimmenPool stimmenPoolFuerDifferenz, Wahldurchfuehrungsgebiet erfassungsgebiet,
      Gruppe gruppeSelected) {
    if (gruppeSelected != null && !gruppeSelected.isSystemGruppe()) {
      return getStimmenKumulierer().getSummeListenstimmenEinzelnerWahlvorschlagUnveraendertFuerPolitischeGruppeAufGebietUndDarunter(gruppeSelected, erfassungsgebiet, stimmenPoolFuerDifferenz);
    }

    return null;
  }

  private Long bestimmeStimmzettelNurEinzelnerWahlvorschlagVeraendertFuerDifferenzDerGruppeImGebiet(StimmenPool stimmenPoolFuerDifferenz, Wahldurchfuehrungsgebiet erfassungsgebiet,
      Gruppe gruppeSelected) {
    if (gruppeSelected != null && !gruppeSelected.isSystemGruppe()) {
      return getStimmenKumulierer().getSummeListenstimmenEinzelnerWahlvorschlagVeraendertFuerPolitischeGruppeAufGebietUndDarunter(gruppeSelected, erfassungsgebiet, stimmenPoolFuerDifferenz);
    }

    return null;
  }

  private Long bestimmeListenstimmenDerGruppeImGebiet(StimmenPool stimmenPool, Wahldurchfuehrungsgebiet erfassungsgebiet, Gruppe gruppeSelected) {
    return getStimmenKumulierer().getSummeListenstimmenFuerPolitischeGruppeAufGebietUndDarunter(gruppeSelected, erfassungsgebiet, stimmenPool);
  }

  private Long bestimmeDirektstimmenDerGruppeImGebiet(StimmenPool stimmenPool, Wahldurchfuehrungsgebiet erfassungsgebiet, Gruppe gruppeSelected) {
    return getStimmenKumulierer().getSummeDirektstimmenFuerPolitischeGruppeAufGebietUndDarunter(gruppeSelected, erfassungsgebiet, stimmenPool);
  }

  private Long bestimmeStimmzettelNurEinzelnerWahlvorschlagUnveraendertDerGruppeImGebiet(StimmenPool stimmenPool, Wahldurchfuehrungsgebiet erfassungsgebiet, Gruppe gruppeSelected) {
    if (gruppeSelected != null && !gruppeSelected.isSystemGruppe()) {
      return getStimmenKumulierer().getSummeListenstimmenEinzelnerWahlvorschlagUnveraendertFuerPolitischeGruppeAufGebietUndDarunter(gruppeSelected, erfassungsgebiet, stimmenPool);
    }

    return null;
  }

  private Long bestimmeStimmzettelNurEinzelnerWahlvorschlagVeraendertDerGruppeImGebiet(StimmenPool stimmenPool, Wahldurchfuehrungsgebiet erfassungsgebiet, Gruppe gruppeSelected) {
    if (gruppeSelected != null && !gruppeSelected.isSystemGruppe()) {
      return getStimmenKumulierer().getSummeListenstimmenEinzelnerWahlvorschlagVeraendertFuerPolitischeGruppeAufGebietUndDarunter(gruppeSelected, erfassungsgebiet, stimmenPool);
    }

    return null;
  }

  @Nonnull
  @Override
  protected StimmenPool getStimmenPool(Gebiet gebietSelected, Ergebnis ergebnisFuerDifferenz, Gruppe gruppeGueltige, GruppeNachErfassungsgebietStimmenSearchParameters searchParameters,
      List<Gruppe> alleGruppen, Supplier<Set<Gebiet>> erfassteGebiete) {
    Collection<Gruppe> gruppenFuerStimmen = new HashSet<>(gruppeExtendedDAO.attachAll(searchParameters.getGruppen()));
    gruppenFuerStimmen.add(gruppeGueltige);
    gruppenFuerStimmen.add(AbstractGruppe.findeSystemGruppe(alleGruppen, SystemGruppe.WAEHLER, getSystemGruppeEigenschaften()));
    gruppenFuerStimmen.add(AbstractGruppe.findeSystemGruppe(alleGruppen, SystemGruppe.WAHLBERECHTIGTE, getSystemGruppeEigenschaften()));

    List<Stimmen> stimmen;

    if (gebietSelected.isErfassungsgebiet()) {
      stimmen = stimmenExtendedDAO.findByErgebnisOhneListenkandidaturenFuerGruppenUndGebieteNoCache(ergebnisFuerDifferenz, gruppenFuerStimmen, asList(gebietSelected));
    } else {
      stimmen = stimmenExtendedDAO.findByErgebnisOhneListenkandidaturenFuerGruppenNoCache(ergebnisFuerDifferenz, gruppenFuerStimmen);
    }

    return StimmenPool.createStimmenPoolFuerAlleStimmen(filterStimmen(filterStimmenStimmabgabeart(stimmen, searchParameters.getStimmabgabeart(), erfassteGebiete), searchParameters, erfassteGebiete));
  }

  protected List<Stimmen> filterStimmenStimmabgabeart(List<Stimmen> alleStimmen, Stimmabgabeart stimmabgabeart, Supplier<Set<Gebiet>> erfassteGebiete) {
    return alleStimmen.stream().filter(stimmabgabeart.filterStimmen(erfassteGebiete)).collect(toList());
  }
}
