
package de.ivu.elect.presentation.auffaelligkeit;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.function.Function;

import javax.annotation.Nullable;
import javax.annotation.PostConstruct;
import javax.enterprise.context.RequestScoped;
import javax.enterprise.event.Observes;
import javax.enterprise.event.Reception;
import javax.enterprise.inject.Instance;
import javax.faces.context.FacesContext;
import javax.inject.Inject;
import javax.inject.Named;

import org.apache.commons.lang3.StringUtils;
import org.apache.deltaspike.core.api.scope.ConversationGroup;
import org.apache.deltaspike.core.api.scope.ViewAccessScoped;
import org.primefaces.model.charts.bar.BarChartModel;

import de.ivu.elect.business.auffaelligkeit.entity.ArtBetrachteterStimmen;
import de.ivu.elect.business.auffaelligkeit.entity.AuffaelligkeitBenfordTestChartErgebnis;
import de.ivu.elect.business.ergebnis.boundary.ErgebnisSelectChangeEvent;
import de.ivu.elect.business.ergebnis.boundary.ErgebnisSessionSelect;
import de.ivu.elect.business.ergebnis.control.ErgebnisAuszaehlungsstandErmittler;
import de.ivu.elect.business.ergebnis.entity.Ergebnis;
import de.ivu.elect.business.ergebnis.entity.ErgebnisStatus;
import de.ivu.elect.business.ergebnis.entity.Stimmenart;
import de.ivu.elect.business.gebietsbaum.boundary.GebietSelectChangeEvent;
import de.ivu.elect.business.gebietsbaum.boundary.WahldurchfuehrungsgebietSessionSelect;
import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
import de.ivu.elect.business.gebietsbaum.entity.Wahldurchfuehrungsgebiet;
import de.ivu.elect.business.hochrechnung.entity.AnzahlStimmenProWaehlerNachStimmenartBestimmer;
import de.ivu.elect.business.hochrechnung.entity.EinfacheWahlAnzahlStimmenProWaehlerNachStimmenartBestimmer;
import de.ivu.elect.business.hochrechnung.entity.HochrechneralgorithmusAusfuehrbarkeitStatus;
import de.ivu.elect.business.security.UseCasePermission;
import de.ivu.elect.business.stimmeingabe.boundary.StimmeingabeAuffaelligkeitSearch;
import de.ivu.elect.business.stimmeingabe.boundary.StimmeingabeAuffaelligkeitSearchParameters;
import de.ivu.elect.business.stimmeingabe.boundary.StimmeingabeErfassenDescriptionResolver;
import de.ivu.elect.business.stimmeingabe.entity.StimmeingabeAuffaelligkeit;
import de.ivu.elect.business.stimmzettel.boundary.AnzahlStimmenProWaehlerBestimmer;
import de.ivu.elect.business.wahl.boundary.WahlSelectChangeEvent;
import de.ivu.elect.business.wahl.boundary.WahldurchfuehrungSessionSelect;
import de.ivu.elect.business.wahl.boundary.WahlterminSelectChangeEvent;
import de.ivu.elect.business.wahlvorschlag.boundary.DirektkandidatExtendedDAO;
import de.ivu.elect.business.wahlvorschlag.boundary.ListeExtendedDAO;
import de.ivu.elect.business.wahlvorschlag.entity.Direktkandidat;
import de.ivu.elect.business.wahlvorschlag.entity.Liste;
import de.ivu.elect.jee.entity.EagerLoadingCollector;
import de.ivu.elect.jee.faces.FacesMessageUtil;
import de.ivu.elect.jee.faces.PreConditionCheck;
import de.ivu.elect.jee.presentation.Icons;
import de.ivu.elect.jee.presentation.dynamicform.FormBuilder;
import de.ivu.elect.jee.presentation.dynamicform.LambdaEnumProperty;
import de.ivu.elect.jee.presentation.dynamicform.LambdaLongProperty;
import de.ivu.elect.jee.presentation.dynamictable.ColumnType;
import de.ivu.elect.jee.presentation.redirect.Redirect;
import de.ivu.elect.presentation.AbstractElectSearchView;
import de.ivu.elect.presentation.View;
import de.ivu.elect.presentation.ergebnis.ViewHavingAuszaehlungsstand;
import de.ivu.elect.presentation.security.AbstractGebietSecurityFilter;
import de.ivu.elect.presentation.stimmeingabe.StimmeingabeAuffaelligkeitCache;


import static java.util.Arrays.asList;

@ViewAccessScoped
@View(viewId = StimmeingabeAuffaelligkeitVisualisiertSearchView.VIEW_ID, useCase = UseCasePermission.STIMMEINGABE_AUFFAELIGKEIT_SEARCH)
@Named(StimmeingabeAuffaelligkeitVisualisiertSearchView.COMPONENT_NAME)
public class StimmeingabeAuffaelligkeitVisualisiertSearchView extends AbstractElectSearchView<StimmeingabeAuffaelligkeit> implements ViewHavingAuszaehlungsstand {

  private static final long serialVersionUID = 1L;

  public static final String COMPONENT_NAME = "stimmeingabeAuffaelligkeitVisualisiertSearchView";

  public static final String VIEW_ID = "/auffaelligkeit/stimmeingabe-auffaelligkeit-visualisiert-search.xhtml";

  @Inject
  // ELECT-16131 @ConversationGroup(StimmeingabeAuffaelligkeitCache.class)
  private transient StimmeingabeAuffaelligkeitCache stimmeingabeAuffaelligkeitCache;

  @Inject
  private transient Icons icons;

  @Inject
  @ConversationGroup(StimmeingabeAuffaelligkeitSearchParameters.class)
  private transient StimmeingabeAuffaelligkeitSearchParameters searchParameters;

  @Inject
  private transient StimmeingabeAuffaelligkeitSearch stimmeingabeAuffaelligkeitSearch;

  @Inject
  private transient WahldurchfuehrungsgebietSessionSelect wahldurchfuehrungsgebietSessionSelect;

  @Inject
  private transient RequestHandlerStimmeingabeAuffaelligkeitVisualisiertView requestHandler;

  @Inject
  private transient WahldurchfuehrungSessionSelect wahldurchfuehrungSessionSelect;

  @Inject
  private transient ErgebnisSessionSelect ergebnisSessionSelect;

  @Inject
  private transient ErgebnisAuszaehlungsstandErmittler ergebnisAuszaehlungsstandErmittler;

  @Inject
  private transient StimmeingabeErfassenDescriptionResolver stimmeingabeErfassenDescriptionResolver;

  @Inject
  private transient AnzahlStimmenProWaehlerBestimmer anzahlStimmenProWaehlerBestimmer;

  @Inject
  protected transient DirektkandidatExtendedDAO direktkandidatExtendedDAO;

  @Inject
  protected transient ListeExtendedDAO listeExtendedDAO;

  private int selectedTabIndex = 0;

  private StimmeingabeAuffaelligkeit auffaelligkeitVomSelectedGebiet;

  private AuffaelligkeitBarChartBuilder auffaelligkeitBarChartBuilder;

  private Long anzahlErfassungseinheitenGesamt;

  private Long anzahlErfassungseinheitenAusgezaehlt;

  public StimmeingabeAuffaelligkeitVisualisiertSearchView() {
    super();
  }

  @Nullable
  @PreConditionCheck
  public String checkCondition() {
    if (!wahldurchfuehrungSessionSelect.isSelected()) {
      addErrorMessage("wahl.selected.missing");
      return getStartViewId();
    }

    if (!isGebietSelected()) {
      addErrorMessage("gebiet.selected.missing");

      return getStartViewId();
    }

    if (!ergebnisSessionSelect.isSelected()) {
      addErrorMessage("ergebnis.selected.missing");

      return getStartViewId();
    }

    return null;
  }

  @Override
  protected String getViewId() {
    return VIEW_ID;
  }

  @Override
  public boolean isGebietSelected() {
    return wahldurchfuehrungsgebietSessionSelect.isSelected();
  }

  @Override
  protected Wahldurchfuehrungsgebiet getSelectedGebiet() {
    return wahldurchfuehrungsgebietSessionSelect.getEntity();
  }

  @Override
  public void addProperties(FormBuilder formBuilder) {
    formBuilder.addProperty(new LambdaLongProperty(getMessage("stimmeingabe.auffaelligkeit.visualisiert.anzahl.von.gebiet.vergleichen"), true,
        searchParameters::setAnzahlVonGebietVergleichen,
        searchParameters::getAnzahlVonGebietVergleichen, 0L, 100L));
    formBuilder
        .addProperty(new LambdaLongProperty(getMessage("stimmeingabe.auffaelligkeit.visualisiert.anzahl.von.anzuzeigenden.gruppen.bei.gebietsvergleich"), true,
            searchParameters::setAnzahlVonAnzuzeigendenGruppenBeiGebietsvergleich,
            searchParameters::getAnzahlVonAnzuzeigendenGruppenBeiGebietsvergleich, 0L, 100L));

    /*LambdaEnumProperty lambdaAuffaelligkeitsbemerkungenErzeugerToleranzEnumProperty = new LambdaEnumProperty<>(
        getMessage("stimmeingabe.auffaelligkeit.auffaelligkeit.bemerkungen.erzeuger.toleranz"), true,
        getSearchParameters()::setAuffaelligkeitsbemerkungenErzeugerToleranz, getSearchParameters()::getAuffaelligkeitsbemerkungenErzeugerToleranz,
        AuffaelligkeitsbemerkungenErzeugerToleranz.class, asList(AuffaelligkeitsbemerkungenErzeugerToleranz.values()), true);
    formBuilder.addProperty(lambdaAuffaelligkeitsbemerkungenErzeugerToleranzEnumProperty);*/

    LambdaEnumProperty lambdaAuffaelligkeitArtBetrachteterStimmen = new LambdaEnumProperty<>(
        getMessage("stimmeingabe.auffaelligkeit.auffaelligkeit.art.betrachteter.stimmen"), true,
        getSearchParameters()::setArtBetrachteterStimmen, getSearchParameters()::getArtBetrachteterStimmen, ArtBetrachteterStimmen.class,
        asList(ArtBetrachteterStimmen.values()), true);
    formBuilder.addProperty(lambdaAuffaelligkeitArtBetrachteterStimmen);
  }

  @Override
  public StimmeingabeAuffaelligkeitSearchParameters getSearchParameters() {
    searchParameters.setWahldurchfuehrungsgebiet(getSelectedGebiet());
    searchParameters.setErgebnis(ergebnisSessionSelect.getEntity());
    return searchParameters;
  }

  @Override
  public void presetSearchParameters() {
    // nichts zu tun
  }

  public EagerLoadingCollector<StimmeingabeAuffaelligkeit> getEagerLoadingCollector() {
    return EagerLoadingCollector.<StimmeingabeAuffaelligkeit>create();
  }

  @Override
  protected List<StimmeingabeAuffaelligkeit> performSearch() {
    List<StimmeingabeAuffaelligkeit> searchResult = stimmeingabeAuffaelligkeitSearch.getSearchResult(searchParameters, stimmeingabeAuffaelligkeitCache);
    HochrechneralgorithmusAusfuehrbarkeitStatus algorithmusAusfuehrbarkeitStatus = getAlgorithmusAusfuehrbarkeitStatus(searchResult);
    if (!algorithmusAusfuehrbarkeitStatus.isAlgorithmusAusfuehrbar()) {
      addErrorMessage(FacesMessageUtil.getEnumMessageKey(algorithmusAusfuehrbarkeitStatus));
    }
    return searchResult;
  }

  private HochrechneralgorithmusAusfuehrbarkeitStatus getAlgorithmusAusfuehrbarkeitStatus(List<StimmeingabeAuffaelligkeit> searchResult) {
    StimmeingabeAuffaelligkeit stimmeingabeAuffaelligkeit = searchResult.stream().filter(Objects::nonNull).findAny().orElse(null);
    if (stimmeingabeAuffaelligkeit == null) {
      return HochrechneralgorithmusAusfuehrbarkeitStatus.BERECHNUNG_NOCH_NICHT_AUSGEFUEHRT;
    }
    return stimmeingabeAuffaelligkeit.getGesamtStimmenhochrechnerAusgabedaten().getAusfuehrbarkeitStatus();
  }

  @Override
  protected AbstractGebietSecurityFilter<StimmeingabeAuffaelligkeit> getGebietSecurityFilter() {
    return AbstractGebietSecurityFilter.createEntityMitGebietSecurityFilter(getEinsehenGebietSecurity());
  }

  @Override
  public List<StimmeingabeAuffaelligkeit> getSearchResult() {
    return performSearch();
  }

  @Override
  @Redirect
  public String search() {
    //stimmeingabeAuffaelligkeitCache.reset();
    return super.search();
  }

  @Override
  @Redirect
  public String reset() {
    String result = super.reset();
    stimmeingabeAuffaelligkeitCache.reset();
    return result;
  }

  // ELECT-2231    ELECT-2250
  /*
   * searchParameters sind von der zur Bearbeitung ausgewählten Wahl abhängig.
   */
  public void onWahlSelectChangeEvent(@Observes(notifyObserver = Reception.IF_EXISTS) WahlSelectChangeEvent event) {
    searchParameters.reset();
    reset();
  }

  /*
   * searchParameters sind vom zur Bearbeitung ausgewählten Wahltermin abhängig.
   */
  public void onWahlterminSelectChangeEvent(@Observes(notifyObserver = Reception.IF_EXISTS) WahlterminSelectChangeEvent event) {
    searchParameters.reset();
    reset();
  }

  public void onGebietSelectChangeEvent(@Observes(notifyObserver = Reception.IF_EXISTS) GebietSelectChangeEvent event) {
    resetSearchResult();
  }

  public void onErgebnisSelectChangeEvent(@Observes(notifyObserver = Reception.IF_EXISTS) ErgebnisSelectChangeEvent event) {
    searchParameters.reset();
    reset();
  }

  @Override
  public boolean isBtnGoBackRendered() {
    return true;
  }

  @Override
  public void resetSearchResult() {
    auffaelligkeitVomSelectedGebiet = null;
    auffaelligkeitBarChartBuilder = null;

    anzahlErfassungseinheitenGesamt = null;
    anzahlErfassungseinheitenAusgezaehlt = null;

    super.resetSearchResult();
  }

  //WASDE-1182
  public boolean isDirektstimmenSelected() {
    if (selectedTabIndex == getDirektstimmenTabIndex()) {
      return true;
    }

    return false;
  }

  public boolean isListenstimmenSelected() {
    if (selectedTabIndex == getListenstimmenTabIndex()) {
      return true;
    }

    return false;
  }

  public int getDirektstimmenTabIndex() {
    if (isWahlMitDirektkandidatengebieten()) {
      return getAnzahlTabsVorDirektstimmenTab();
    }
    return -1;
  }

  private int getAnzahlTabsVorDirektstimmenTab() {
    return 0;
  }

  public int getListenstimmenTabIndex() {
    if (isWahlMitListengebieten()) {
      return getAnzahlTabsVorListenstimmenTab();
    }
    return -1;
  }

  private int getAnzahlTabsVorListenstimmenTab() {
    int result = 0;
    if (isWahlMitDirektkandidatengebieten()) {
      result++;
    }
    return result;
  }

  public int getBenfordTestTabIndex() {
    return getAnzahlTabsVorBenfordTestTab();
  }

  private int getAnzahlTabsVorBenfordTestTab() {
    int result = 0;
    if (isWahlMitDirektkandidatengebieten()) {
      result++;
    }
    if (isWahlMitListengebieten()) {
      result++;
    }
    return result;
  }

  public Boolean isBenfordTestSelected() {
    return selectedTabIndex == getBenfordTestTabIndex();
  }

  public int getSelectedTabIndex() {
    return selectedTabIndex;
  }

  public void setSelectedTabIndex(int selectedTabIndex) {
    this.selectedTabIndex = selectedTabIndex;
  }

  private StimmeingabeAuffaelligkeit getAuffaelligkeitVomSelectedGebiet() {
    if (auffaelligkeitVomSelectedGebiet == null) {
      auffaelligkeitVomSelectedGebiet = getSearchResult().stream().filter(auffaelligkeit -> auffaelligkeit.getGebiet().equals(getSelectedGebiet())).findFirst()
          .orElse(null);
    }
    return auffaelligkeitVomSelectedGebiet;
  }

  public boolean isAuffaelligkeitBemerkungVorhanden() {
    return getAuffaelligkeitVomSelectedGebiet() != null && getAuffaelligkeitVomSelectedGebiet()
        .getBemerkungen() != null && !getAuffaelligkeitVomSelectedGebiet().getBemerkungen().isEmpty();
  }

  public List<String> getBemerkungenAlsStrings() {
    checkAuffaelligkeitBarChartBuilderNotNull();
    List<String> result = new ArrayList<>();
    if (getAuffaelligkeitVomSelectedGebiet() != null) {
      getAuffaelligkeitVomSelectedGebiet().getBemerkungen()
          .forEach(bemerkung -> result.add(bemerkung.getAuffaelligkeitBemerkungAlsString(userLocale.getLocale(), getMessages())));
    }
    return result;
  }

  public String getAbweichungDirektstimmenFormatted() {
    return getAbweichungFormatted(Stimmenart.DIREKTSTIMME);
  }

  public String getAbweichungListenstimmenFormatted() {
    return getAbweichungFormatted(Stimmenart.LISTENSTIMME);
  }

  public String getAbweichungFormatted(Stimmenart stimmenart) {
    double abweichung = auffaelligkeitVomSelectedGebiet.getSummeAbsoluteAbweichungEinerStimmenart(stimmenart);
    return getNumberFormattedInDecimalTwoFractionDigitsPercentageForm(abweichung);
  }

  public String getNumberFormattedInDecimalTwoFractionDigitsPercentageForm(Number number) {
    ColumnType columnType = ColumnType.DECIMAL_TWO_FRACTION_DIGITS_PERCENTAGE;
    return columnType.toCsv(number, userLocale.getLocale(), getMessages());
  }

  public BarChartModel getBarChartDirektstimmen() {
    checkAuffaelligkeitBarChartBuilderNotNull();

    return auffaelligkeitBarChartBuilder.getBarChartModelAktuellVsHochgerechnet(Stimmenart.DIREKTSTIMME);
  }

  private void checkAuffaelligkeitBarChartBuilderNotNull() {
    if (auffaelligkeitBarChartBuilder == null) {
      auffaelligkeitBarChartBuilder = new AuffaelligkeitBarChartBuilder(getAuffaelligkeitVomSelectedGebiet(),
          getAnzahlStimmenProWaehlerNachStimmenartBestimmer(), searchParameters.getAnzahlVonAnzuzeigendenGruppenBeiGebietsvergleich(),
          searchParameters.getArtBetrachteterStimmen(), this::getMessage);
    }
  }

  private AnzahlStimmenProWaehlerNachStimmenartBestimmer getAnzahlStimmenProWaehlerNachStimmenartBestimmer() {
    EinfacheWahlAnzahlStimmenProWaehlerNachStimmenartBestimmer einfacheWahlAnzahlStimmenProWaehlerNachStimmenartBestimmer = new EinfacheWahlAnzahlStimmenProWaehlerNachStimmenartBestimmer(
        anzahlStimmenProWaehlerBestimmer, getWahlvorschlaegeInDirektwahlgebietFct(), getWahlvorschlaegeInListenwahlgebietFct());
    return einfacheWahlAnzahlStimmenProWaehlerNachStimmenartBestimmer;
  }

  public Function<Gebiet, Collection<Direktkandidat>> getWahlvorschlaegeInDirektwahlgebietFct() {
    if (direktkandidatExtendedDAO == null) {
      return gebiet -> new ArrayList<>();
    }
    return direktkandidatExtendedDAO::findByGebiet;
  }

  public Function<Gebiet, Collection<Liste>> getWahlvorschlaegeInListenwahlgebietFct() {
    if (listeExtendedDAO == null) {
      return gebiet -> new ArrayList<>();
    }
    return listeExtendedDAO::findByGebiet;
  }

  public BarChartModel getBarChartListenstimmen() {
    checkAuffaelligkeitBarChartBuilderNotNull();

    return auffaelligkeitBarChartBuilder.getBarChartModelAktuellVsHochgerechnet(Stimmenart.LISTENSTIMME);
  }

  public List<BarChartModel> getBarChartModelSelectedGebietVsAehnlicheGebietDirektstimmen() {
    checkAuffaelligkeitBarChartBuilderNotNull();
    return auffaelligkeitBarChartBuilder
        .getBarChartModelSelectedGebietVsAehnlicheGebiet(Stimmenart.DIREKTSTIMME, searchParameters.getAnzahlVonGebietVergleichen());
  }

  public List<BarChartModel> getBarChartModelSelectedGebietVsAehnlicheGebietListenstimmen() {
    checkAuffaelligkeitBarChartBuilderNotNull();
    return auffaelligkeitBarChartBuilder
        .getBarChartModelSelectedGebietVsAehnlicheGebiet(Stimmenart.LISTENSTIMME, searchParameters.getAnzahlVonGebietVergleichen());
  }

  @Override
  public String getGebietName() {
    return getSelectedGebietNameFormatted();
  }

  @Override
  public String formatGebietName(Gebiet gebiet) {
    return super.formatGebietName(gebiet);
  }

  public List<AuffaelligkeitBenfordTestChartErgebnis> getAuffaelligkeitBenfordTestErgebnisse() {
    getAuffaelligkeitVomSelectedGebiet().getBenfordTestErgebnisse().forEach(benfordTestErgebnis -> benfordTestErgebnis.setMessages(getMessages()));
    return getAuffaelligkeitVomSelectedGebiet().getBenfordTestErgebnisse();
  }

  public String getAnzahlVomBenfordTestUntersuchteZahlen(AuffaelligkeitBenfordTestChartErgebnis auffaelligkeitBenfordTestErgebnis) {
    return String.valueOf(auffaelligkeitBenfordTestErgebnis.getUntersuchteZahlenSortiert().size());
  }

  public String getVomBenfordTestUntersuchteZahlen(AuffaelligkeitBenfordTestChartErgebnis auffaelligkeitBenfordTestErgebnis) {
    return convertListOfLongsToString(auffaelligkeitBenfordTestErgebnis.getUntersuchteZahlenSortiert(), ", ");
  }

  private String convertListOfLongsToString(List<Long> zahlen, String separtator) {
    String result = StringUtils.join(zahlen, separtator);
    result = result.replaceAll(separtator + separtator, separtator + "null" + separtator);
    if (result.startsWith(separtator)) {
      return "null" + result;
    }
    return result;
  }

  public String getAnzahlVomBenfordTestNichtBetrachteteZahlen(AuffaelligkeitBenfordTestChartErgebnis auffaelligkeitBenfordTestErgebnis) {
    return String.valueOf(auffaelligkeitBenfordTestErgebnis.getNichtBetrachteZahlenSortiert().size());
  }

  public String getVomBenfordTestNichtBetrachteteZahlen(AuffaelligkeitBenfordTestChartErgebnis auffaelligkeitBenfordTestErgebnis) {
    return convertListOfLongsToString(auffaelligkeitBenfordTestErgebnis.getNichtBetrachteZahlenSortiert(), ", ");
  }

  public boolean isWahlMitDirektkandidatengebieten() {
    if (isErgebnisSelected()) {
      return getErgebnis().getWahl().isWahlMitDirektkandidatengebieten();
    }

    return false;
  }

  public boolean isErgebnisSelected() {
    return ergebnisSessionSelect.isSelected();
  }

  @Override
  public Ergebnis getErgebnis() {
    return ergebnisSessionSelect.getEntity();
  }

  @Override
  public String getErgebnisName() {
    Ergebnis ergebnis = getErgebnis();

    if (ergebnis == null) {
      return ""; // ELECT-8722
    }

    return ergebnis.getName();
  }

  @Override // ELECT-13716
  public String getErgebnisHinweis() {
    Ergebnis ergebnis = getErgebnis();

    if (ergebnis != null) {
      ErgebnisStatus ergebnisStatus = getErgebnis().getErgebnisStatus();
      String hinweisErgebnisStatusMsgKey = "kumulierte.stimmen.search.page.ergebnishinweis.ergebnisstatus." + ergebnisStatus.name();

      if (getMessages().isMessageKeyVorhanden(hinweisErgebnisStatusMsgKey)) {
        return getMessage(hinweisErgebnisStatusMsgKey, getMessage(ergebnisStatus));
      }
    }

    return "";
  }

  private boolean isGesamtstimmenAuswerten() {
    if (isErgebnisSelected()) {
      return getErgebnis().getWahl().isWahlMitGesamtstimmenauswertung();
    }

    return false;
  }

  public String getLabelDirektstimmen() {
    if (isWahlMitListengebieten() || isGesamtstimmenAuswerten()) {
      Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet = getSelectedGebiet();

      if (wahldurchfuehrungsgebiet == null) {
        return "";
      }

      return stimmeingabeErfassenDescriptionResolver.getStimmeingabeErfassenDescriptionByGebiet(wahldurchfuehrungsgebiet)
          .getStimmeingabeGruppeStimmenErfassenLabelGenerator()
          .getStimmenartName(wahldurchfuehrungsgebiet, Stimmenart.DIREKTSTIMME);
    }

    return getMessage("gruppe.kumulierte.stimmen.stimmen");
  }

  public String getLabelListenstimmen() {
    if (isWahlMitDirektkandidatengebieten() || isGesamtstimmenAuswerten()) {
      Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet = getSelectedGebiet();

      if (wahldurchfuehrungsgebiet == null) {
        return "";
      }

      return stimmeingabeErfassenDescriptionResolver.getStimmeingabeErfassenDescriptionByGebiet(wahldurchfuehrungsgebiet)
          .getStimmeingabeGruppeStimmenErfassenLabelGenerator()
          .getStimmenartName(wahldurchfuehrungsgebiet, Stimmenart.LISTENSTIMME);
    }

    return getMessage("gruppe.kumulierte.stimmen.stimmen");
  }

  public String getLabelBenfordTest() {
    return getMessage("stimmeingabe.auffaelligkeit.visualisiert.benford.tab.label");
  }

  public boolean isWahlMitListengebieten() {
    if (isErgebnisSelected()) {
      return getErgebnis().getWahl().isWahlMitListengebieten();
    }

    return false;
  }

  @Override
  public String getStyleClassAuszaehlungsstand() {
    return icons.getIconStatus((double) getAnzahlErfassungseinheitenAusgezaehlt() / (double) getAnzahlErfassungseinheitenGesamt());
  }

  @Override
  public long getAnzahlErfassungseinheitenGesamt() {
    if (anzahlErfassungseinheitenGesamt == null) {
      anzahlErfassungseinheitenGesamt = berechneAnzahlErfassungseinheitenGesamt();
    }

    return anzahlErfassungseinheitenGesamt;
  }

  private long berechneAnzahlErfassungseinheitenGesamt() {
    if (!isGebietSelected() || !isWahlSelected()) {
      return 0;
    }

    if (getSelectedGebiet().isWurzelgebiet()) {
      return ergebnisAuszaehlungsstandErmittler.getAnzahlErfassungsgebieteFuerWurzelgebiet(getSelectedWahl());
    }

    return ergebnisAuszaehlungsstandErmittler.getAnzahlErfassungsgebieteFuerGebiet(getSelectedWahl(), getSelectedGebiet());
  }

  @Override
  public long getAnzahlErfassungseinheitenAusgezaehlt() {
    if (anzahlErfassungseinheitenAusgezaehlt == null) {
      anzahlErfassungseinheitenAusgezaehlt = berechneAnzahlErfassungseinheitenAusgezaehlt();
    }

    return anzahlErfassungseinheitenAusgezaehlt;
  }

  private long berechneAnzahlErfassungseinheitenAusgezaehlt() {
    if (!isGebietSelected() || !isErgebnisSelected()) {
      return 0;
    }

    if (getSelectedGebiet().isWurzelgebiet()) {
      return ergebnisAuszaehlungsstandErmittler.getAnzahlGueltigeErgebniseingaengeFuerErgebnisUndWurzelgebiet(getErgebnis());
    }

    return ergebnisAuszaehlungsstandErmittler.getAnzahlGueltigeErgebniseingaengeFuerErgebnisUndGebiet(getErgebnis(), getSelectedGebiet());
  }

  public String getErfasser() {
    StimmeingabeAuffaelligkeit auffaelligkeitVomSelectedGebiet = getAuffaelligkeitVomSelectedGebiet();
    return auffaelligkeitVomSelectedGebiet != null ? auffaelligkeitVomSelectedGebiet.getErfasser() : null;
  }

  @RequestScoped
  //  Das Suchergebnis soll nur einmal pro Request geladen werden, daher gibt es dieses "Konstrukt" aus "SearchView" und "RequestHandlerAbstract"
  public static class RequestHandlerStimmeingabeAuffaelligkeitVisualisiertView {

    @View
    @Inject
    private transient Instance<StimmeingabeAuffaelligkeitVisualisiertSearchView> searchViews;

    @PostConstruct
    public void init() {
      // ELECT-3543 Doppelte Suchen vermeiden - Suchergebnis für Request nicht beim POST-löschen
      if (!FacesContext.getCurrentInstance().isPostback()) {
        for (StimmeingabeAuffaelligkeitVisualisiertSearchView searchView : searchViews) {
          searchView.resetSearchResult();
        }
      }
    }

    public void nothing() {
      // Do nothing.
    }
  }
}
