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

import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.util.Calendar;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;

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

import org.apache.commons.lang3.time.DateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import de.ivu.elect.business.gebietsbaum.boundary.WahldurchfuehrungsgebietDAO;
import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
import de.ivu.elect.business.gebietsbaum.entity.Wahldurchfuehrungsgebiet;
import de.ivu.elect.business.wahl.entity.Wahl;
import de.ivu.elect.business.wahl.entity.WahlParameterLoader;
import de.ivu.elect.business.wahl.entity.WahlParameterWerte;
import de.ivu.elect.business.wahlbeteiligung.boundary.WahlbeteiligungAuswertungDAO;
import de.ivu.elect.business.wahlbeteiligung.boundary.WahlbeteiligungDAO;
import de.ivu.elect.business.wahlbeteiligung.boundary.WahlbeteiligungVerlaufDAO;
import de.ivu.elect.business.wahlbeteiligung.control.WahlbeteiligungVerlaufCreate;
import de.ivu.elect.business.wahlbeteiligung.entity.Wahlbeteiligung;
import de.ivu.elect.business.wahlbeteiligung.entity.WahlbeteiligungAuswertung;
import de.ivu.elect.business.wahlbeteiligung.entity.WahlbeteiligungAuswertungStatus;
import de.ivu.elect.business.wahlbeteiligung.entity.WahlbeteiligungAuswertungTyp;
import de.ivu.elect.business.wahlbeteiligung.entity.WahlbeteiligungAuswertungWahllokalsystemCreateParameters;
import de.ivu.elect.business.wahlbeteiligung.entity.WahlbeteiligungVerlauf;
import de.ivu.elect.business.wahlbeteiligung.entity.WahlbeteiligungVerlaufGebiet;
import de.ivu.elect.business.wahlbeteiligung.entity.WahlbeteiligungVerlaufStatus;
import de.ivu.elect.business.wahlbeteiligung.entity.WahlbeteiligungVerlaufTyp;
import de.ivu.elect.business.wahllokalsystem.entity.WahllokalsystemStatus;
import de.ivu.elect.business.wahllokalsystem.entity.WahllokalsystenWahlbeteiligungUebernehmenBriefwahl;
import de.ivu.elect.jee.security.AbstractPrincipal;


import static java.util.stream.Collectors.toSet;

@Stateless
public class WahllokalsystemWahlbeteiligungUebernehmen {

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

  @Inject
  private transient WahlbeteiligungAuswertungDAO wahlbeteiligungAuswertungDAO;

  @Inject
  private transient WahllokalsystemStatusDAO wahllokalsystemStatusDAO;

  @Inject
  private transient WahlbeteiligungDAO wahlbeteiligungDAO;

  @Inject
  private transient WahlParameterLoader wahlParameterLoader;

  @Inject
  private transient WahlbeteiligungVerlaufCreate wahlbeteiligungVerlaufCreate;

  @Inject
  private transient WahldurchfuehrungsgebietDAO wahldurchfuehrungsgebietDAO;

  @Inject
  private transient WahlbeteiligungVerlaufDAO wahlbeteiligungVerlaufDAO;

  public boolean isEtwasZuSenden() {
    return getNaechstenZuVerarbeiten() != null;
  }

  private WahlbeteiligungAuswertung getNaechstenZuVerarbeiten() {
    long now = System.currentTimeMillis();

    return wahlbeteiligungAuswertungDAO.findByTypAndStatus(WahlbeteiligungAuswertungTyp.AUTOMATISCH,
            WahlbeteiligungAuswertungStatus.AUTOMATISCHE_UEBERNAHME_VORGESEHEN).stream()
        .filter(wahlbeteiligungAuswertung -> wahlbeteiligungAuswertung.getDatum().getTime() + TimeUnit.MINUTES
            .toMillis(bestimmePufferInMinuten(wahlbeteiligungAuswertung.getWahl())) <= now)
        .findFirst()
        .orElse(null);
  }

  public void verarbeite() {
    WahlbeteiligungAuswertung wahlbeteiligungAuswertung = getNaechstenZuVerarbeiten();
    if (wahlbeteiligungAuswertung != null) {
      WahlbeteiligungAuswertungWahllokalsystemCreateParameters parameter = new WahlbeteiligungAuswertungWahllokalsystemCreateParameters(
          AbstractPrincipal.SYSTEM_BENUTZERKENNUNG);
      Wahl wahl = wahlbeteiligungAuswertung.getWahl();
      parameter.setNeuereWerteBeruecksichtigen(
          wahlParameterLoader.resolveBooleanValue(wahl, WahlParameterWerte.WAHLLOKALSYSTEM_WAHLBETEILIGUNG_UEBERNEHMEN_ZUKUENFTIGE_WERTE));
      parameter.setMaximaleAbweichungZurueckliegendeWerteInMinuten(
          wahlParameterLoader.resolveLongValue(wahl, WahlParameterWerte.WAHLLOKALSYSTEM_WAHLBETEILIGUNG_UEBERNEHMEN_MAXIMALE_ABWEICHUNG_IN_MINUTEN));
      parameter.setVerzoegerungInMinutenBisUebernahme(bestimmePufferInMinuten(wahl));
      uebernehmeAuswertungAusWahllokalsystemStatus(wahlbeteiligungAuswertung, parameter);
      wahlbeteiligungAuswertung.setStatus(WahlbeteiligungAuswertungStatus.AUTOMATISCHE_UEBERNAHME_ERFOLGT);

      if (isWahlbeteiligungVerlaufVorhanden(wahl, wahlbeteiligungAuswertung)) {
        LOG.warn(
            "WahlbeteiligungVerlauf bereits vorhanden: " + wahlbeteiligungAuswertung + ", " + wahlbeteiligungAuswertung.getWahl());
      } else {
        WahlbeteiligungVerlauf wahlbeteiligungsverlauf = new WahlbeteiligungVerlauf();

        wahlbeteiligungsverlauf.setTyp(WahlbeteiligungVerlaufTyp.TAGESVERLAUF);
        wahlbeteiligungsverlauf.setDatum(wahlbeteiligungAuswertung.getDatum());
        wahlbeteiligungsverlauf.setStatus(WahlbeteiligungVerlaufStatus.AUTOMATISCH_ERMITTELT);
        wahlbeteiligungsverlauf.setName(wahlbeteiligungAuswertung.getWahl().getName());
        wahlbeteiligungsverlauf.setWahl(wahlbeteiligungAuswertung.getWahl());

        wahlbeteiligungVerlaufCreate.save(wahlbeteiligungsverlauf);

        berechneWahlbeteiligungen(wahlbeteiligungAuswertung, wahlbeteiligungsverlauf);
      }
    }
  }

  private boolean isWahlbeteiligungVerlaufVorhanden(Wahl wahl, WahlbeteiligungAuswertung wahlbeteiligungAuswertung) {
    return wahlbeteiligungVerlaufDAO.findByWahl(wahl).stream().anyMatch(
        wahlbeteiligungVerlauf -> DateUtils.truncatedEquals(wahlbeteiligungVerlauf.getDatum(), wahlbeteiligungAuswertung.getDatum(), Calendar.SECOND));
  }

  private long bestimmePufferInMinuten(Wahl wahl) {
    Long longValue = wahlParameterLoader
        .resolveLongValue(wahl, WahlParameterWerte.WAHLLOKALSYSTEM_WAHLBETEILIGUNG_UEBERNEHMEN_PUFFER_BIS_UEBERNAHME_IN_MINUTEN);
    if (longValue == null) {
      return 0;
    }
    return longValue;
  }

  public void uebernehmeAuswertungAusWahllokalsystemStatus(WahlbeteiligungAuswertung wahlbeteiligungAuswertung, WahlbeteiligungAuswertungWahllokalsystemCreateParameters parameters) {

    LOG.info("Übernehme Wahlbeteiligungen: " + wahlbeteiligungAuswertung + ", parameter: " + parameters);
    List<WahllokalsystemStatus> wahllokalsystemStatusList = wahllokalsystemStatusDAO.findByWahl(wahlbeteiligungAuswertung.getWahl());

    long referenzZeitpunkt = wahlbeteiligungAuswertung.getDatum().getTime();
    long referenzZeitpunktMitPuffer = referenzZeitpunkt + TimeUnit.MINUTES.toMillis(parameters.getVerzoegerungInMinutenBisUebernahme());
    Map<Gebiet, WahllokalsystemStatus> wahllokalsystemStatusNachGebiet = wahllokalsystemStatusList.stream()
        .filter(wahllokalsystemStatus -> wahllokalsystemStatus.getAnzahlWaehler() != null)
        .filter(wahllokalsystemStatus -> wahllokalsystemStatus.getAnzahlWaehlerZeitpunkt() != null)
        .filter(wahllokalsystemStatus -> parameters.isNeuereWerteBeruecksichtigen() || referenzZeitpunktMitPuffer >= wahllokalsystemStatus
            .getAnzahlWaehlerZeitpunkt().getTime())
        .filter(wahllokalsystemStatus -> referenzZeitpunkt < wahllokalsystemStatus.getAnzahlWaehlerZeitpunkt().getTime() || Math
            .abs(referenzZeitpunkt - wahllokalsystemStatus.getAnzahlWaehlerZeitpunkt().getTime()) <= TimeUnit.MINUTES
            .toMillis(parameters.getMaximaleAbweichungZurueckliegendeWerteInMinuten()))
        .collect(Collectors.toMap(WahllokalsystemStatus::getGebiet, wahllokalsystemStatus -> wahllokalsystemStatus, (a, b) -> b));

    LOG.info("Übernehme Wahlbeteiligungen: " + wahlbeteiligungAuswertung + ", size: " + wahllokalsystemStatusNachGebiet.size());

    for (Map.Entry<Gebiet, WahllokalsystemStatus> entry : wahllokalsystemStatusNachGebiet.entrySet()) {
      Gebiet gebiet = entry.getKey();
      WahllokalsystemStatus wahllokalsystemStatus = entry.getValue();
      Wahlbeteiligung wahlbeteiligung = new Wahlbeteiligung();
      wahlbeteiligung.setWahlbeteiligungAuswertung(wahlbeteiligungAuswertung);
      wahlbeteiligung.setWahldurchfuehrungsgebiet(gebiet.castTo(Wahldurchfuehrungsgebiet.class).getEntity());
      wahlbeteiligung.setAnzahlWaehlerGesamt(wahllokalsystemStatus.getAnzahlWaehler());
      wahlbeteiligung.setAnzahlWaehlerMitWahlschein(null);
      wahlbeteiligung.setErfasser(parameters.getErfasser());
      wahlbeteiligung.setZeitstempel(wahllokalsystemStatus.getAnzahlWaehlerZeitpunkt());
      wahlbeteiligungDAO.persist(wahlbeteiligung);
    }
    LOG.info("Übernehme Wahlbeteiligungen: " + wahlbeteiligungAuswertung + " - done");

  }

  public void berechneWahlbeteiligungen(WahlbeteiligungAuswertung wahlbeteiligungAuswertung, WahlbeteiligungVerlauf wahlbeteiligungVerlauf) {
    Map<Wahldurchfuehrungsgebiet, Wahlbeteiligung> waehlerProGebiet = wahlbeteiligungAuswertung.getWahlbeteiligungsByWahlbeteiligungAuswertung().stream()
        .collect(Collectors.toMap(Wahlbeteiligung::getWahldurchfuehrungsgebiet, Function.identity()));

    Map<Gebiet, WahlbeteiligungVerlaufGebiet> verlaufProGebiet = wahlbeteiligungVerlauf.getVerlaufProGebiet();

    Wahl wahl = wahlbeteiligungAuswertung.getWahl();
    BigDecimal briefwahlRuecklaufQuote = wahlParameterLoader.resolveBigDecimalValue(wahl,
        WahlParameterWerte.WAHLLOKALSYSTEM_WAHLBETEILIGUNG_UEBERNEHMEN_ERWARTETE_RUECKLAUFQUOTE_BRIEFWAHL);
    BigDecimal anteilGebieteNiedrig = wahlParameterLoader.resolveBigDecimalValue(wahl,
        WahlParameterWerte.WAHLLOKALSYSTEM_WAHLBETEILIGUNG_UEBERNEHMEN_ANTEIL_NICHT_BETRACHTETER_GEBIETE_NIEDRIGSTE_QUOTE);
    BigDecimal anteilGebieteHoechst = wahlParameterLoader.resolveBigDecimalValue(wahl,
        WahlParameterWerte.WAHLLOKALSYSTEM_WAHLBETEILIGUNG_UEBERNEHMEN_ANTEIL_NICHT_BETRACHTETER_GEBIETE_HOECHSTE_QUOTE);

    WahllokalsystenWahlbeteiligungUebernehmenBriefwahl uebernehmenBriefwahl = wahlParameterLoader
        .resolveEnumValue(wahl, WahlParameterWerte.WAHLLOKALSYSTEM_WAHLBETEILIGUNG_UEBERNEHMEN_BRIEFWAHL_OHNE_EINGAENGE_BERUECKSICHTIGEN,
            WahllokalsystenWahlbeteiligungUebernehmenBriefwahl.class);

    LOG.info(
        "berechneWahlbeteiligungen, uebernehmenBriefwahl=" + uebernehmenBriefwahl + ", briefwahlRuecklaufQuote=" + briefwahlRuecklaufQuote + ", anteilGebieteNiedrig=" + anteilGebieteNiedrig + ", anteilGebieteHoechst=" + anteilGebieteHoechst);

    Map<Gebiet, BigDecimal> zuErmittelndeGebieteMitWahlbeteiligung = new HashMap<>();
    for (Map.Entry<Wahldurchfuehrungsgebiet, Wahlbeteiligung> entry : waehlerProGebiet.entrySet()) {
      Wahldurchfuehrungsgebiet key = entry.getKey();
      Wahlbeteiligung wahlbeteiligung = entry.getValue();

      if (wahlbeteiligung.getAnzahlWaehlerGesamt() != null && wahlbeteiligung.getAnzahlWaehlerGesamt() > 0L) {
        if (key.getAnzahlWahlberechtigteOhneWahlschein() != null && key.getAnzahlWahlberechtigteOhneWahlschein() > 0L) {
          if (key.isUrnenwahlgebiet()) {
            BigDecimal wahlbeteiligungGebietOhneBriefwahl = berechneWahlbeteiligung(key, waehlerProGebiet, null);
            if (wahlbeteiligungGebietOhneBriefwahl !=null) {
              zuErmittelndeGebieteMitWahlbeteiligung.put(key, wahlbeteiligungGebietOhneBriefwahl);
            }
          }
        }
      }
    }

    LOG.info("zuErmittelndeGebieteMitWahlbeteiligung="+zuErmittelndeGebieteMitWahlbeteiligung.size());
    if (zuErmittelndeGebieteMitWahlbeteiligung.isEmpty() && (uebernehmenBriefwahl==null || !uebernehmenBriefwahl.isBriefwahlBeruecksichtigenWennKeinGebietErfasst())) {
      return;
    }

    int anzahlGebieteGesamt = zuErmittelndeGebieteMitWahlbeteiligung.size();

    if (anteilGebieteNiedrig==null) {
      anteilGebieteNiedrig = BigDecimal.ZERO; // keine Ahnung ob gut
    }
    if (anteilGebieteHoechst==null) {
      anteilGebieteHoechst = BigDecimal.ZERO; // keine Ahnung ob gut
    }
    long anzahlZuEntfernenNiedrig = anteilGebieteNiedrig.multiply(BigDecimal.valueOf(anzahlGebieteGesamt), new MathContext(0, RoundingMode.FLOOR))
        .longValue();
    long anzahlZuEntfernenHoechst = anteilGebieteHoechst.multiply(BigDecimal.valueOf(anzahlGebieteGesamt), new MathContext(0, RoundingMode.FLOOR))
        .longValue();

    long gebietInAuswertung = anzahlGebieteGesamt - (anzahlZuEntfernenNiedrig + anzahlZuEntfernenHoechst);
    LOG.info(
        "berechneWahlbeteiligungen, anzahlGebieteGesamt=" + anzahlGebieteGesamt + ", anzahlZuEntfernenNiedrig=" + anzahlZuEntfernenNiedrig + ", " +
            "anzahlZuEntfernenHoechst=" + anzahlZuEntfernenHoechst+", gebietInAuswertung="+gebietInAuswertung);



    if (anzahlGebieteGesamt==0 || gebietInAuswertung<1) {
      // ELECTMUC-2306
      if (briefwahlRuecklaufQuote == null || BigDecimal.ZERO.equals(briefwahlRuecklaufQuote)) {
        return;
      }

      for (Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet : wahldurchfuehrungsgebietDAO.findByWahl(wahl)) {
        WahlbeteiligungVerlaufGebiet wahlbeteiligungVerlaufGebiet = verlaufProGebiet.get(wahldurchfuehrungsgebiet);
        if (wahlbeteiligungVerlaufGebiet != null) {
          wahlbeteiligungVerlaufGebiet.setWahlbeteilung(berechneBriefwahlbeteiligung(wahldurchfuehrungsgebiet, briefwahlRuecklaufQuote));
        }
      }
    } else {

//      List<Gebiet> gebieteSortiertNachWahlbeteiligung = new ArrayList<>(zuErmittelndeGebieteMitWahlbeteiligung.keySet());
//      gebieteSortiertNachWahlbeteiligung.sort(Comparator.comparing(zuErmittelndeGebieteMitWahlbeteiligung::get));

      Set<Gebiet> gebieteFinal = zuErmittelndeGebieteMitWahlbeteiligung.keySet().stream()
          .sorted(Comparator.comparing(zuErmittelndeGebieteMitWahlbeteiligung::get))
          .skip(anzahlZuEntfernenNiedrig)
          .limit(gebietInAuswertung)
          .collect(toSet());

      Set<Gebiet> zuErmittelndeGebiete = addUebergeordneteGebiete(gebieteFinal);

      for (Gebiet gebiet : zuErmittelndeGebiete) {
        if (verlaufProGebiet.containsKey(gebiet)) {
          WahlbeteiligungVerlaufGebiet wahlbeteiligungVerlaufGebiet = verlaufProGebiet.get(gebiet);
          wahlbeteiligungVerlaufGebiet.setWahlbeteilung(berechneWahlbeteiligung(gebiet, waehlerProGebiet, briefwahlRuecklaufQuote));
        }
      }


      // ELECTMUC-2864 Briefwahlsockel nicht betrachteter Gebiete immer addieren, damit Wahlbeteiligung nicht fällt
      if (briefwahlRuecklaufQuote != null && !BigDecimal.ZERO.equals(briefwahlRuecklaufQuote)) {
        Set<Gebiet> gebieteNichtErmittelt = new HashSet<>(zuErmittelndeGebieteMitWahlbeteiligung.keySet());
        gebieteNichtErmittelt.removeAll(gebieteFinal);
        LOG.info("Briefwahlbeteiligungen für nicht ermittelte Gebiete hinzufügen: "+gebieteNichtErmittelt.size());
        for (Gebiet gebiet : gebieteNichtErmittelt) {
          WahlbeteiligungVerlaufGebiet wahlbeteiligungVerlaufGebiet = verlaufProGebiet.get(gebiet);
          if (wahlbeteiligungVerlaufGebiet != null) {
            wahlbeteiligungVerlaufGebiet.setWahlbeteilung(berechneBriefwahlbeteiligung(gebiet.castTo(Wahldurchfuehrungsgebiet.class).getEntity(), briefwahlRuecklaufQuote));
          }
        }
      }

    }
  }


  private Set<Gebiet> addUebergeordneteGebiete(Set<Gebiet> zuErmittelndeGebiete) {
    Set<Gebiet> result = new HashSet<>(zuErmittelndeGebiete);
    for (Gebiet gebiet : zuErmittelndeGebiete) {
      addGebiet(gebiet, result);
    }
    return result;

  }

  private BigDecimal berechneBriefwahlbeteiligung(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, BigDecimal briefwahlRuecklaufQuote) {
    Long anzahlWahlberechtigterA2 = wahldurchfuehrungsgebiet.getAnzahlWahlberechtigteMitWahlschein();
    if (anzahlWahlberechtigterA2 == null || anzahlWahlberechtigterA2 == 0) {
      return null;
    }

    Long anzahlWahlberechtigterA1A2 = wahldurchfuehrungsgebiet.getAnzahlWahlberechtigterA1A2();
    if (anzahlWahlberechtigterA1A2 == null || anzahlWahlberechtigterA1A2 == 0) {
      return null;
    }

    BigDecimal result = BigDecimal.valueOf(bestimmeBriefWaehler(briefwahlRuecklaufQuote, anzahlWahlberechtigterA2)).divide(BigDecimal.valueOf(anzahlWahlberechtigterA1A2), MathContext.DECIMAL64).multiply(BigDecimal.valueOf(100));

    if (result.longValue() >= 100L) {
      return null;
    }
    return result;

  }

  @Nullable
  private BigDecimal berechneWahlbeteiligung(Gebiet gebiet, Map<Wahldurchfuehrungsgebiet, Wahlbeteiligung> waehlerProGebiet, BigDecimal briefwahlRuecklauf) {
    Long anzahlWaehler = null;
    Long anzahlWahlberechtigterA1 = null;
    Long anzahlWahlberechtigterA2 = null;

    Wahlbeteiligung wahlbeteiligung = waehlerProGebiet.get(gebiet);
    if (wahlbeteiligung == null) {
      anzahlWaehler = 0L;
      anzahlWahlberechtigterA1 = 0L;
      anzahlWahlberechtigterA2 = 0L;

      for (Map.Entry<Wahldurchfuehrungsgebiet, Wahlbeteiligung> entry : waehlerProGebiet.entrySet()) {
        Wahldurchfuehrungsgebiet key = entry.getKey();
        Wahlbeteiligung value = entry.getValue();
        if (value.getAnzahlWaehlerGesamt() != null && value.getAnzahlWaehlerGesamt() > 0L && key.getAnzahlWahlberechtigteOhneWahlschein() != null && key
            .getAnzahlWahlberechtigteOhneWahlschein() > value.getAnzahlWaehlerGesamt()) {
          if (key.isInGebietshierarchieUntergeordnetesGebietVon(gebiet)) {
            anzahlWaehler += value.getAnzahlWaehlerGesamt();
            anzahlWahlberechtigterA1 += key.getAnzahlWahlberechtigteOhneWahlschein();
            if (key.getAnzahlWahlberechtigteMitWahlschein()!=null) {
              anzahlWahlberechtigterA2 += key.getAnzahlWahlberechtigteMitWahlschein();
            }
          }
        }
      }

    } else {
      anzahlWaehler = wahlbeteiligung.getAnzahlWaehlerGesamt();
      anzahlWahlberechtigterA1 = wahlbeteiligung.getWahldurchfuehrungsgebiet().getAnzahlWahlberechtigteOhneWahlschein();
      anzahlWahlberechtigterA2 = wahlbeteiligung.getWahldurchfuehrungsgebiet().getAnzahlWahlberechtigteMitWahlschein();
    }

    if (anzahlWaehler == null || anzahlWahlberechtigterA1 == null || anzahlWahlberechtigterA1 == 0) {
      return null;
    }

    long anzahlWaehlerGesamt = anzahlWaehler;
    long anzahlWahlberechtigterGesamt = anzahlWahlberechtigterA1;
    if (briefwahlRuecklauf!=null && !BigDecimal.ZERO.equals(briefwahlRuecklauf) && anzahlWahlberechtigterA2!=null) {
      anzahlWaehlerGesamt = anzahlWaehler + bestimmeBriefWaehler(briefwahlRuecklauf, anzahlWahlberechtigterA2);
      anzahlWahlberechtigterGesamt = anzahlWahlberechtigterA1 + anzahlWahlberechtigterA2;
    }

    BigDecimal result = BigDecimal.valueOf(anzahlWaehlerGesamt).divide(BigDecimal.valueOf(anzahlWahlberechtigterGesamt), MathContext.DECIMAL64).multiply(BigDecimal.valueOf(100));

    if (result.longValue() >= 100L) {
      return null;
    }
    return result;

  }

  private long bestimmeBriefWaehler(BigDecimal briefwahlRuecklauf, Long anzahlWahlberechtigterA2) {
    return briefwahlRuecklauf.multiply(BigDecimal.valueOf(anzahlWahlberechtigterA2), new MathContext(0, RoundingMode.FLOOR)).longValue();
  }

  private void addGebiet(Gebiet gebiet, Set<Gebiet> zuErmittelndeGebiete) {
    if (gebiet == null) {
      return;
    }
    zuErmittelndeGebiete.add(gebiet);
    addGebiet(gebiet.getUebergeordnetesGebiet(), zuErmittelndeGebiete);

  }
}
