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

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

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

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

import de.ivu.elect.business.ergebnis.entity.Stimmenart;
import de.ivu.elect.business.gebietsbaum.entity.Wahldurchfuehrungsgebiet;
import de.ivu.elect.business.gruppeimgebiet.entity.GruppeImGebiet;
import de.ivu.elect.business.stimmeingabe.control.StimmeingabeErfassenContext;
import de.ivu.elect.business.stimmeingabe.control.StimmeingabeKopfzahlErfassenLabelGenerator;
import de.ivu.elect.business.stimmeingabe.control.StimmeingabeKopfzahlKonfiguration;
import de.ivu.elect.business.stimmeingabe.entity.Ergebniseingang;
import de.ivu.elect.business.stimmeingabe.entity.Stimmeingabe;
import de.ivu.elect.business.stimmeingabe.entity.StimmeingabeKopfzahl;
import de.ivu.elect.business.stimmeingabe.entity.StimmeingabeKopfzahlErfassenModel;
import de.ivu.elect.business.stimmeingabe.entity.Stimmeingabefehler;
import de.ivu.elect.business.wahl.entity.WahlartErgebnisart;
import de.ivu.elect.util.CaseAndDiacriticIgnoringComparator;

public abstract class AbstractStimmeingabeKopfzahlModelBuilder extends AbstractStimmeingabeModelBuilder {

  private static final long serialVersionUID = 1L;

  @Inject
  private transient StimmeingabeKopfzahlKonfiguration stimmeingabeKopfzahlKonfiguration;

  protected AbstractStimmeingabeKopfzahlModelBuilder() {
    super();
  }

  protected StimmeingabeKopfzahlKonfiguration getStimmeingabeKopfzahlKonfiguration() {
    return stimmeingabeKopfzahlKonfiguration;
  }

  public StimmeingabeKopfzahlErfassenModel createModelStimmeingabeKopfzahl(StimmeingabeErfassenContext context) {
    StimmeingabeKopfzahlErfassenModel result = createStimmeingabeKopfzahlErfassenModel(context.getWahldurchfuehrungsgebiet(), context.getErgebniseingang());

    for (GruppeImGebiet gruppeImGebiet : context.getGruppenImGebiet()) {
      initializeModelStimmeingabeKopfzahl(result, gruppeImGebiet, context);
    }

    context.getErgebniseingang().setStimmeingabeKopfzahlErfassenModel(result);

    return result;
  }

  protected StimmeingabeKopfzahlErfassenModel createStimmeingabeKopfzahlErfassenModel(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, Ergebniseingang ergebniseingang) {
    return new StimmeingabeKopfzahlErfassenModel(wahldurchfuehrungsgebiet, ergebniseingang);
  }

  protected void initializeModelStimmeingabeKopfzahl(StimmeingabeKopfzahlErfassenModel result, GruppeImGebiet gruppeImGebiet, StimmeingabeErfassenContext context) {
    if (isSystemGruppeWahlberechtigte(gruppeImGebiet)) {
      initializeModelStimmeingabeWahlberechtigte(result, gruppeImGebiet, context);
    }

    if (isSystemGruppeWaehler(gruppeImGebiet)) {
      initializeModelStimmeingabeWaehler(result, gruppeImGebiet, context);
    }
  }

  protected void initializeModelStimmeingabeWahlberechtigte(StimmeingabeKopfzahlErfassenModel result, GruppeImGebiet gruppeImGebiet, StimmeingabeErfassenContext context) {
    if (isPruefkennzeichenWahlberechtigteCreated(context)) {
      result.setPruefkennzeichenWahlberechtigte(createStimmeingabeKopfzahlPruefkennzeichenWahlberechtigte(context, gruppeImGebiet));
    }

    List<StimmeingabeKopfzahl> stimmeingabenWahlberechtigteCreated = new ArrayList<>();

    if (isWahlberechtigteOhneWahlscheinCreated(context)) {
      StimmeingabeKopfzahl stimmeingabeWahlberechtigteOhneWahlschein = createStimmeingabeKopfzahlWahlberechtigteOhneWahlschein(context, gruppeImGebiet);
      stimmeingabenWahlberechtigteCreated.add(stimmeingabeWahlberechtigteOhneWahlschein);

      result.setWahlberechtigteOhneWahlschein(stimmeingabeWahlberechtigteOhneWahlschein);
    }

    if (isWahlberechtigteMitWahlscheinCreated(context)) {
      StimmeingabeKopfzahl stimmeingabeWahlberechtigteMitWahlschein = createStimmeingabeKopfzahlWahlberechtigteMitWahlschein(context, gruppeImGebiet);
      stimmeingabenWahlberechtigteCreated.add(stimmeingabeWahlberechtigteMitWahlschein);

      result.setWahlberechtigteMitWahlschein(stimmeingabeWahlberechtigteMitWahlschein);
    }

    if (isWahlberechtigteNichtImWaehlerverzeichnisCreated(context)) {
      StimmeingabeKopfzahl stimmeingabeWahlberechtigteNichtImWaehlerverzeichnis = createStimmeingabeKopfzahlWahlberechtigteNichtImWaehlerverzeichnis(context, gruppeImGebiet);
      stimmeingabenWahlberechtigteCreated.add(stimmeingabeWahlberechtigteNichtImWaehlerverzeichnis);

      result.setWahlberechtigteNichtImWaehlerverzeichnis(stimmeingabeWahlberechtigteNichtImWaehlerverzeichnis);
    }

    if (isWahlberechtigteGesamtCreated(context)) {
      result.setWahlberechtigteGesamt(createStimmeingabeKopfzahlWahlberechtigteGesamt(context, gruppeImGebiet, stimmeingabenWahlberechtigteCreated));
    }
  }

  protected boolean isWahlberechtigteStimmenartCreated(StimmeingabeErfassenContext context, Stimmenart stimmenart) {
    return isWahlberechtigteStimmenartCreated(context.getWahldurchfuehrungsgebiet(), context.getErgebniseingangsart(), stimmenart);
  }

  protected boolean isWahlberechtigteStimmenartCreated(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, WahlartErgebnisart ergebnisart, Stimmenart stimmenart) {
    if (stimmenart != null) {
      switch (stimmenart) {
        case PRUEFKENNZEICHEN :
          return isPruefkennzeichenWahlberechtigteCreated(wahldurchfuehrungsgebiet, ergebnisart);
        case OHNE_WAHLSCHEIN :
          return isWahlberechtigteOhneWahlscheinCreated(wahldurchfuehrungsgebiet, ergebnisart);
        case MIT_WAHLSCHEIN :
          return isWahlberechtigteMitWahlscheinCreated(wahldurchfuehrungsgebiet, ergebnisart);
        case NICHT_IM_WAEHLERVERZEICHNIS :
          return isWahlberechtigteNichtImWaehlerverzeichnisCreated(wahldurchfuehrungsgebiet, ergebnisart);
        case GESAMT :
          return isWaehlerGesamtCreated(wahldurchfuehrungsgebiet, ergebnisart);

        default :
          break;
      }
    }

    return false;
  }

  private boolean isPruefkennzeichenWahlberechtigteCreated(StimmeingabeErfassenContext context) {
    return isPruefkennzeichenWahlberechtigteCreated(context.getWahldurchfuehrungsgebiet(), context.getErgebniseingangsart());
  }

  protected boolean isPruefkennzeichenWahlberechtigteCreated(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, WahlartErgebnisart ergebnisart) {
    return getStimmeingabeKopfzahlKonfiguration().isPruefkennzeichenWahlberechtigteBeiStimmeingabeRelevant(wahldurchfuehrungsgebiet, ergebnisart);
  }

  private boolean isWahlberechtigteOhneWahlscheinCreated(StimmeingabeErfassenContext context) {
    return isWahlberechtigteOhneWahlscheinCreated(context.getWahldurchfuehrungsgebiet(), context.getErgebniseingangsart());
  }

  protected boolean isWahlberechtigteOhneWahlscheinCreated(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, WahlartErgebnisart ergebnisart) {
    return getStimmeingabeKopfzahlKonfiguration().isWahlberechtigteOhneWahlscheinBeiStimmeingabeRelevant(wahldurchfuehrungsgebiet, ergebnisart);
  }

  private boolean isWahlberechtigteMitWahlscheinCreated(StimmeingabeErfassenContext context) {
    return isWahlberechtigteMitWahlscheinCreated(context.getWahldurchfuehrungsgebiet(), context.getErgebniseingangsart());
  }

  protected boolean isWahlberechtigteMitWahlscheinCreated(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, WahlartErgebnisart ergebnisart) {
    return getStimmeingabeKopfzahlKonfiguration().isWahlberechtigteMitWahlscheinBeiStimmeingabeRelevant(wahldurchfuehrungsgebiet, ergebnisart);
  }

  private boolean isWahlberechtigteNichtImWaehlerverzeichnisCreated(StimmeingabeErfassenContext context) {
    return isWahlberechtigteNichtImWaehlerverzeichnisCreated(context.getWahldurchfuehrungsgebiet(), context.getErgebniseingangsart());
  }

  protected boolean isWahlberechtigteNichtImWaehlerverzeichnisCreated(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, WahlartErgebnisart ergebnisart) {
    return getStimmeingabeKopfzahlKonfiguration().isWahlberechtigteNichtImWaehlerverzeichnisBeiStimmeingabeRelevant(wahldurchfuehrungsgebiet, ergebnisart);
  }

  private boolean isWahlberechtigteGesamtCreated(StimmeingabeErfassenContext context) {
    return isWahlberechtigteGesamtCreated(context.getWahldurchfuehrungsgebiet(), context.getErgebniseingangsart());
  }

  protected boolean isWahlberechtigteGesamtCreated(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, WahlartErgebnisart ergebnisart) {
    return getStimmeingabeKopfzahlKonfiguration().isWahlberechtigteGesamtBeiStimmeingabeRelevant(wahldurchfuehrungsgebiet, ergebnisart);
  }

  protected void initializeModelStimmeingabeWaehler(StimmeingabeKopfzahlErfassenModel result, GruppeImGebiet gruppeImGebiet, StimmeingabeErfassenContext context) {
    if (isWaehlerOhneWahlscheinCreated(context)) {
      result.setWaehlerOhneWahlschein(createStimmeingabeKopfzahlWaehlerOhneWahlschein(context, gruppeImGebiet));
    }

    if (isWaehlerMitWahlscheinCreated(context)) {
      result.setWaehlerMitWahlschein(createStimmeingabeKopfzahlWaehlerMitWahlschein(context, gruppeImGebiet));
    }

    if (isWaehlerMitWahlscheinInUrnenwahlAnzahlAusUrnenwahlCreated(context)) {
      result.setWaehlerMitWahlscheinInUrnenwahlAnzahlAusUrnenwahl(createStimmeingabeKopfzahlWaehlerMitWahlscheinInUrnenwahlAnzahlAusUrnenwahl(context, gruppeImGebiet));
    }

    if (isWaehlerMitWahlscheinInUrnenwahlAnzahlAusBriefwahlCreated(context)) {
      result.setWaehlerMitWahlscheinInUrnenwahlAnzahlAusBriefwahl(createStimmeingabeKopfzahlWaehlerMitWahlscheinInUrnenwahlAnzahlAusBriefwahl(context, gruppeImGebiet));
    }

    if (isWaehlerMitVollmachtCreated(context)) {
      result.setWaehlerMitVollmacht(createStimmeingabeKopfzahlWaehlerMitVollmacht(context, gruppeImGebiet));
    }

    if (isWaehlerGesamtCreated(context)) {
      result.setWaehlerGesamt(createStimmeingabeKopfzahlWaehlerGesamt(context, gruppeImGebiet));
    }
  }

  protected boolean isWaehlerStimmenartCreated(StimmeingabeErfassenContext context, Stimmenart stimmenart) {
    return isWaehlerStimmenartCreated(context.getWahldurchfuehrungsgebiet(), context.getErgebniseingangsart(), stimmenart);
  }

  protected boolean isWaehlerStimmenartCreated(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, WahlartErgebnisart ergebnisart, Stimmenart stimmenart) {
    if (stimmenart != null) {
      switch (stimmenart) {
        case OHNE_WAHLSCHEIN :
          return isWaehlerOhneWahlscheinCreated(wahldurchfuehrungsgebiet, ergebnisart);
        case MIT_WAHLSCHEIN :
          return isWaehlerMitWahlscheinCreated(wahldurchfuehrungsgebiet, ergebnisart);
        case ALS_URNENWAHL_IN_URNE :
          return isWaehlerMitWahlscheinInUrnenwahlAnzahlAusUrnenwahlCreated(wahldurchfuehrungsgebiet, ergebnisart);
        case ALS_BRIEFWAHL_IN_URNE :
          return isWaehlerMitWahlscheinInUrnenwahlAnzahlAusBriefwahlCreated(wahldurchfuehrungsgebiet, ergebnisart);
        case MIT_VOLLMACHT :
          return isWaehlerGesamtCreated(wahldurchfuehrungsgebiet, ergebnisart);
        case GESAMT :
          return isWaehlerGesamtCreated(wahldurchfuehrungsgebiet, ergebnisart);

        default :
          break;
      }
    }

    return false;
  }

  private boolean isWaehlerOhneWahlscheinCreated(StimmeingabeErfassenContext context) {
    return isWaehlerOhneWahlscheinCreated(context.getWahldurchfuehrungsgebiet(), context.getErgebniseingangsart());
  }

  protected boolean isWaehlerOhneWahlscheinCreated(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, WahlartErgebnisart ergebnisart) {
    return getStimmeingabeKopfzahlKonfiguration().isWaehlerOhneWahlscheinBeiStimmeingabeRelevant(wahldurchfuehrungsgebiet, ergebnisart);
  }

  private boolean isWaehlerMitWahlscheinCreated(StimmeingabeErfassenContext context) {
    return isWaehlerMitWahlscheinCreated(context.getWahldurchfuehrungsgebiet(), context.getErgebniseingangsart());
  }

  protected boolean isWaehlerMitWahlscheinCreated(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, WahlartErgebnisart ergebnisart) {
    return getStimmeingabeKopfzahlKonfiguration().isWaehlerMitWahlscheinBeiStimmeingabeRelevant(wahldurchfuehrungsgebiet, ergebnisart);
  }

  private boolean isWaehlerMitWahlscheinInUrnenwahlAnzahlAusUrnenwahlCreated(StimmeingabeErfassenContext context) {
    return isWaehlerMitWahlscheinInUrnenwahlAnzahlAusUrnenwahlCreated(context.getWahldurchfuehrungsgebiet(), context.getErgebniseingangsart());
  }

  protected boolean isWaehlerMitWahlscheinInUrnenwahlAnzahlAusUrnenwahlCreated(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, WahlartErgebnisart ergebnisart) {
    return getStimmeingabeKopfzahlKonfiguration().isWaehlerMitWahlscheinInUrnenwahlAnzahlAusUrnenwahlBeiStimmeingabeRelevant(wahldurchfuehrungsgebiet, ergebnisart);
  }

  private boolean isWaehlerMitWahlscheinInUrnenwahlAnzahlAusBriefwahlCreated(StimmeingabeErfassenContext context) {
    return isWaehlerMitWahlscheinInUrnenwahlAnzahlAusBriefwahlCreated(context.getWahldurchfuehrungsgebiet(), context.getErgebniseingangsart());
  }

  protected boolean isWaehlerMitWahlscheinInUrnenwahlAnzahlAusBriefwahlCreated(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, WahlartErgebnisart ergebnisart) {
    return getStimmeingabeKopfzahlKonfiguration().isWaehlerMitWahlscheinInUrnenwahlAnzahlAusBriefwahlBeiStimmeingabeRelevant(wahldurchfuehrungsgebiet, ergebnisart);
  }

  private boolean isWaehlerMitVollmachtCreated(StimmeingabeErfassenContext context) {
    return isWaehlerMitVollmachtCreated(context.getWahldurchfuehrungsgebiet(), context.getErgebniseingangsart());
  }

  protected boolean isWaehlerMitVollmachtCreated(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, WahlartErgebnisart ergebnisart) {
    return false /* ELECT-9630, momentan NL-spezifisch! getStimmeingabeKopfzahlKonfiguration().isWaehlerMitVollmachtBeiStimmeingabeRelevant(wahldurchfuehrungsgebiet, ergebnisart) */;
  }

  private boolean isWaehlerGesamtCreated(StimmeingabeErfassenContext context) {
    return isWaehlerGesamtCreated(context.getWahldurchfuehrungsgebiet(), context.getErgebniseingangsart());
  }

  protected boolean isWaehlerGesamtCreated(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, WahlartErgebnisart ergebnisart) {
    return getStimmeingabeKopfzahlKonfiguration().isWaehlerGesamtBeiStimmeingabeRelevant(wahldurchfuehrungsgebiet, ergebnisart);
  }

  public List<StimmeingabeKopfzahl> layoutModelStimmeingabeKopfzahl(StimmeingabeKopfzahlErfassenModel model) {
    List<StimmeingabeKopfzahl> result = new ArrayList<>();

    result.addAll(layoutModelStimmeingabeKopfzahlWahlberechtigte(model));
    result.addAll(layoutModelStimmeingabeKopfzahlWaehler(model));

    // ELECT-14589
    result.forEach(stimmeingabeKopfzahl -> stimmeingabeKopfzahl.setVisible(isStimmeingabeKopfzahlVisible(model, stimmeingabeKopfzahl)));

    return result;
  }

  // Gibt die Reihenfolge der anzuzeigenden A-Werte vor.
  public List<StimmeingabeKopfzahl> layoutModelStimmeingabeKopfzahlWahlberechtigte(StimmeingabeKopfzahlErfassenModel model) {
    List<StimmeingabeKopfzahl> result = new ArrayList<>();

    List<StimmeingabeKopfzahl> stimmeingabenWahlberechtigteRendered = new ArrayList<>();

    if (isPruefkennzeichenWahlberechtigteRendered(model)) {
      stimmeingabenWahlberechtigteRendered.add(model.getPruefkennzeichenWahlberechtigte());
    }

    if (isWahlberechtigteOhneWahlscheinRendered(model)) {
      stimmeingabenWahlberechtigteRendered.add(model.getWahlberechtigteOhneWahlschein());
    }

    if (isWahlberechtigteMitWahlscheinRendered(model)) {
      stimmeingabenWahlberechtigteRendered.add(model.getWahlberechtigteMitWahlschein());
    }

    if (isWahlberechtigteNichtImWaehlerverzeichnisRendered(model)) {
      stimmeingabenWahlberechtigteRendered.add(model.getWahlberechtigteNichtImWaehlerverzeichnis());
    }

    if (isWahlberechtigteGesamtRendered(model)) {
      stimmeingabenWahlberechtigteRendered.add(model.getWahlberechtigteGesamt());
    }

    sortModelStimmeingabeWahlberechtigte(stimmeingabenWahlberechtigteRendered);

    result.addAll(stimmeingabenWahlberechtigteRendered);

    return result;
  }

  protected void sortModelStimmeingabeWahlberechtigte(List<StimmeingabeKopfzahl> stimmeingabenWahlberechtigteRendered) {
    // To be overridden...
  }

  // Gibt die Reihenfolge der anzuzeigenden B-Werte vor.
  public List<StimmeingabeKopfzahl> layoutModelStimmeingabeKopfzahlWaehler(StimmeingabeKopfzahlErfassenModel model) {
    List<StimmeingabeKopfzahl> result = new ArrayList<>();

    List<StimmeingabeKopfzahl> stimmeingabenWaehlerRendered = new ArrayList<>();

    if (isWaehlerOhneWahlscheinRendered(model)) {
      stimmeingabenWaehlerRendered.add(model.getWaehlerOhneWahlschein());
    }

    if (isWaehlerMitVollmachtRendered(model)) {
      stimmeingabenWaehlerRendered.add(model.getWaehlerMitVollmacht());
    }

    if (isWaehlerMitWahlscheinRendered(model)) {
      stimmeingabenWaehlerRendered.add(model.getWaehlerMitWahlschein());
    }

    if (isWaehlerMitWahlscheinInUrnenwahlAnzahlAusUrnenwahlRendered(model)) {
      stimmeingabenWaehlerRendered.add(model.getWaehlerMitWahlscheinInUrnenwahlAnzahlAusUrnenwahl());
    }

    if (isWaehlerMitWahlscheinInUrnenwahlAnzahlAusBriefwahlRendered(model)) {
      stimmeingabenWaehlerRendered.add(model.getWaehlerMitWahlscheinInUrnenwahlAnzahlAusBriefwahl());
    }

    sortModelStimmeingabeWaehler(stimmeingabenWaehlerRendered);

    result.addAll(stimmeingabenWaehlerRendered);

    if (isWaehlerGesamtRendered(model)) {
      StimmeingabeKopfzahl stimmeingabeWaehlerGesamt = model.getWaehlerGesamt();

      if (isWaehlerGesamtAlsErstesZuErfassen(model)) {
        result.add(0, stimmeingabeWaehlerGesamt);
      } else {
        result.add(stimmeingabeWaehlerGesamt);
      }
    }

    return result;
  }

  protected void sortModelStimmeingabeWaehler(List<StimmeingabeKopfzahl> stimmeingaben) {
    stimmeingaben.sort((one, another) -> CaseAndDiacriticIgnoringComparator.DEFAULT_INSTANCE.compare(one.getStimmeingabeKennung(), another.getStimmeingabeKennung()));
  }

  private boolean isStimmeingabeKopfzahlVisible(StimmeingabeKopfzahlErfassenModel model, StimmeingabeKopfzahl stimmeingabeKopfzahl) {
    Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet = model.getBezugGebiet();
    boolean sonderstimmbezirkSelected = wahldurchfuehrungsgebiet != null && Boolean.TRUE.equals(wahldurchfuehrungsgebiet.getSonderstimmbezirk());

    if (sonderstimmbezirkSelected) { // ELECT-14589
      if (stimmeingabeKopfzahl.hasAnzahl() && stimmeingabeKopfzahl.getAnzahl() != 0L) {
        return true;
      }

      GruppeImGebiet gruppeImGebiet = stimmeingabeKopfzahl.getGruppeImGebiet();

      if (isSystemGruppeWahlberechtigte(gruppeImGebiet)) {
        return false;
      }

      Stimmenart stimmenart = stimmeingabeKopfzahl.getStimmenart();

      if (isSystemGruppeWaehler(gruppeImGebiet)) {
        return Stimmenart.MIT_WAHLSCHEIN.equals(stimmenart) || Stimmenart.GESAMT.equals(stimmenart);
      }
    }

    return true;
  }

  private boolean isPruefkennzeichenWahlberechtigteRendered(StimmeingabeKopfzahlErfassenModel model) {
    return isPruefkennzeichenWahlberechtigteCreated(model.getBezugGebiet(), model.getErgebniseingangsart()) && model.hasPruefkennzeichenWahlberechtigte();
  }

  private boolean isWahlberechtigteOhneWahlscheinRendered(StimmeingabeKopfzahlErfassenModel model) {
    return isWahlberechtigteOhneWahlscheinCreated(model.getBezugGebiet(), model.getErgebniseingangsart()) && model.hasWahlberechtigteOhneWahlschein();
  }

  private boolean isWahlberechtigteMitWahlscheinRendered(StimmeingabeKopfzahlErfassenModel model) {
    return isWahlberechtigteMitWahlscheinCreated(model.getBezugGebiet(), model.getErgebniseingangsart()) && model.hasWahlberechtigteMitWahlschein();
  }

  private boolean isWahlberechtigteNichtImWaehlerverzeichnisRendered(StimmeingabeKopfzahlErfassenModel model) {
    return isWahlberechtigteNichtImWaehlerverzeichnisCreated(model.getBezugGebiet(), model.getErgebniseingangsart()) && model.hasWahlberechtigteNichtImWaehlerverzeichnis();
  }

  private boolean isWahlberechtigteGesamtRendered(StimmeingabeKopfzahlErfassenModel model) {
    return isWahlberechtigteGesamtCreated(model.getBezugGebiet(), model.getErgebniseingangsart()) && model.hasWahlberechtigteGesamt();
  }

  private boolean isWaehlerOhneWahlscheinRendered(StimmeingabeKopfzahlErfassenModel model) {
    return isWaehlerOhneWahlscheinCreated(model.getBezugGebiet(), model.getErgebniseingangsart()) && model.hasWaehlerOhneWahlschein();
  }

  private boolean isWaehlerMitWahlscheinRendered(StimmeingabeKopfzahlErfassenModel model) {
    return isWaehlerMitWahlscheinCreated(model.getBezugGebiet(), model.getErgebniseingangsart()) && model.hasWaehlerMitWahlschein();
  }

  private boolean isWaehlerMitWahlscheinInUrnenwahlAnzahlAusUrnenwahlRendered(StimmeingabeKopfzahlErfassenModel model) {
    return isWaehlerMitWahlscheinInUrnenwahlAnzahlAusUrnenwahlCreated(model.getBezugGebiet(), model.getErgebniseingangsart()) && model.hasWaehlerMitWahlscheinInUrnenwahlAnzahlAusUrnenwahl();
  }

  private boolean isWaehlerMitWahlscheinInUrnenwahlAnzahlAusBriefwahlRendered(StimmeingabeKopfzahlErfassenModel model) {
    return isWaehlerMitWahlscheinInUrnenwahlAnzahlAusBriefwahlCreated(model.getBezugGebiet(), model.getErgebniseingangsart()) && model.hasWaehlerMitWahlscheinInUrnenwahlAnzahlAusBriefwahl();
  }

  private boolean isWaehlerMitVollmachtRendered(StimmeingabeKopfzahlErfassenModel model) {
    return isWaehlerMitVollmachtCreated(model.getBezugGebiet(), model.getErgebniseingangsart()) && model.hasWaehlerMitVollmacht();
  }

  private boolean isWaehlerGesamtRendered(StimmeingabeKopfzahlErfassenModel model) {
    return isWaehlerGesamtCreated(model.getBezugGebiet(), model.getErgebniseingangsart()) && model.hasWaehlerGesamt();
  }

  private boolean isWaehlerGesamtAlsErstesZuErfassen(StimmeingabeKopfzahlErfassenModel model) {
    return getStimmeingabeKopfzahlKonfiguration().isWaehlerGesamtBeiStimmeingabeAlsErstesZuErfassen(model.getBezugGebiet());
  }

  private StimmeingabeKopfzahl createStimmeingabeKopfzahlWaehlerGesamt(StimmeingabeErfassenContext context, GruppeImGebiet gruppeImGebiet) {
    Stimmenart stimmenart = Stimmenart.GESAMT;
    Long anzahlExpected = getStimmeingabeKopfzahlGesamtAnzahlExpected(context);

    return createStimmeingabeKopfzahl(context, gruppeImGebiet, stimmenart, anzahlExpected);
  }

  private StimmeingabeKopfzahl createStimmeingabeKopfzahlWahlberechtigte(StimmeingabeErfassenContext context, GruppeImGebiet gruppeImGebiet, Stimmenart stimmenart, @Nullable Long anzahlExpected,
      boolean anzahlEingabeInitialVorbelegen, boolean anzahlEingabeReadonly) {
    return createStimmeingabeKopfzahlWahlberechtigte(context, gruppeImGebiet, stimmenart, anzahlExpected == null ? null : anzahlExpected.toString(), anzahlExpected, anzahlEingabeInitialVorbelegen,
        anzahlEingabeReadonly);
  }

  private StimmeingabeKopfzahl createStimmeingabeKopfzahlWahlberechtigte(StimmeingabeErfassenContext context, GruppeImGebiet gruppeImGebiet, Stimmenart stimmenart,
      @Nullable String anzahlEingabeExpected, @Nullable Long anzahlExpected, boolean anzahlEingabeInitialVorbelegen, boolean anzahlEingabeReadonly) {
    StimmeingabeKopfzahl stimmeingabeKopfzahl = createStimmeingabeKopfzahl(context, gruppeImGebiet, stimmenart, anzahlEingabeExpected, anzahlExpected);

    boolean ergebniseingangIntial = !context.hasLetzterErgebniseingangAktuell();
    boolean anzahlEingabeIntialVorbelegbar = ergebniseingangIntial && !stimmeingabeKopfzahl.hasAnzahlEingabe();

    if (anzahlEingabeInitialVorbelegen && anzahlEingabeIntialVorbelegbar) { // ELECT-7628
      String anzahlEingabe = anzahlEingabeExpected; // ELECT-11813

      if (anzahlExpected != null) {
        anzahlEingabe = anzahlExpected.toString();
      }

      if (isNotBlank(anzahlEingabe)) {
        stimmeingabeKopfzahl.setAnzahlEingabe(anzahlEingabe);
      }
    }

    stimmeingabeKopfzahl.setReadonly(anzahlEingabeReadonly);

    return stimmeingabeKopfzahl;
  }

  private StimmeingabeKopfzahl createStimmeingabeKopfzahlPruefkennzeichenWahlberechtigte(StimmeingabeErfassenContext context, GruppeImGebiet gruppeImGebiet) {
    Stimmenart stimmenart = Stimmenart.PRUEFKENNZEICHEN;
    Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet = context.getWahldurchfuehrungsgebiet();
    WahlartErgebnisart ergebnisart = context.getErgebniseingangsart();
    String anzahlEingabeExpected = wahldurchfuehrungsgebiet.getPruefkennzeichen();
    Long anzahlExpected = null;

    boolean anzahlEingabeInitialVorbelegen = getStimmeingabeKopfzahlKonfiguration().isPruefkennzeichenWahlberechtigteBeiStimmeingabeInitialVorzubelegen(wahldurchfuehrungsgebiet, ergebnisart);
    boolean anzahlEingabeReadonly = getStimmeingabeKopfzahlKonfiguration().isPruefkennzeichenWahlberechtigteBeiStimmeingabeNurAnzuzeigen(wahldurchfuehrungsgebiet, ergebnisart);

    return createStimmeingabeKopfzahlWahlberechtigte(context, gruppeImGebiet, stimmenart, anzahlEingabeExpected, anzahlExpected, anzahlEingabeInitialVorbelegen, anzahlEingabeReadonly);
  }

  private StimmeingabeKopfzahl createStimmeingabeKopfzahlWahlberechtigteOhneWahlschein(StimmeingabeErfassenContext context, GruppeImGebiet gruppeImGebiet) {
    Stimmenart stimmenart = Stimmenart.OHNE_WAHLSCHEIN;
    Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet = context.getWahldurchfuehrungsgebiet();
    WahlartErgebnisart ergebnisart = context.getErgebniseingangsart();
    Long anzahlExpected = getStimmeingabeKopfzahlOhneWahlscheinAnzahlExpected(context);

    boolean anzahlEingabeInitialVorbelegen = getStimmeingabeKopfzahlKonfiguration().isWahlberechtigteOhneWahlscheinBeiStimmeingabeInitialVorzubelegen(wahldurchfuehrungsgebiet, ergebnisart, context);
    boolean anzahlEingabeReadonly = getStimmeingabeKopfzahlKonfiguration().isWahlberechtigteOhneWahlscheinBeiStimmeingabeNurAnzuzeigen(wahldurchfuehrungsgebiet, ergebnisart, context);

    return createStimmeingabeKopfzahlWahlberechtigte(context, gruppeImGebiet, stimmenart, anzahlExpected, anzahlEingabeInitialVorbelegen, anzahlEingabeReadonly);
  }

  private StimmeingabeKopfzahl createStimmeingabeKopfzahlWahlberechtigteMitWahlschein(StimmeingabeErfassenContext context, GruppeImGebiet gruppeImGebiet) {
    Stimmenart stimmenart = Stimmenart.MIT_WAHLSCHEIN;
    Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet = context.getWahldurchfuehrungsgebiet();
    WahlartErgebnisart ergebnisart = context.getErgebniseingangsart();
    Long anzahlExpected = getStimmeingabeKopfzahlMitWahlscheinAnzahlExpected(context);

    boolean anzahlEingabeInitialVorbelegen = getStimmeingabeKopfzahlKonfiguration().isWahlberechtigteMitWahlscheinBeiStimmeingabeInitialVorzubelegen(wahldurchfuehrungsgebiet, ergebnisart, context);
    boolean anzahlEingabeReadonly = getStimmeingabeKopfzahlKonfiguration().isWahlberechtigteMitWahlscheinBeiStimmeingabeNurAnzuzeigen(wahldurchfuehrungsgebiet, ergebnisart, context);

    return createStimmeingabeKopfzahlWahlberechtigte(context, gruppeImGebiet, stimmenart, anzahlExpected, anzahlEingabeInitialVorbelegen, anzahlEingabeReadonly);
  }

  private StimmeingabeKopfzahl createStimmeingabeKopfzahlWahlberechtigteNichtImWaehlerverzeichnis(StimmeingabeErfassenContext context, GruppeImGebiet gruppeImGebiet) {
    Stimmenart stimmenart = Stimmenart.NICHT_IM_WAEHLERVERZEICHNIS;
    Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet = context.getWahldurchfuehrungsgebiet();
    WahlartErgebnisart ergebnisart = context.getErgebniseingangsart();
    Long anzahlExpected = getStimmeingabeKopfzahlNichtImWaehlerverzeichnisAnzahlExpected(context);

    boolean anzahlEingabeInitialVorbelegen = getStimmeingabeKopfzahlKonfiguration().isWahlberechtigteNichtImWaehlerverzeichnisBeiStimmeingabeInitialVorzubelegen(wahldurchfuehrungsgebiet, ergebnisart, context);
    boolean anzahlEingabeReadonly = getStimmeingabeKopfzahlKonfiguration().isWahlberechtigteNichtImWaehlerverzeichnisBeiStimmeingabeNurAnzuzeigen(wahldurchfuehrungsgebiet, ergebnisart, context);

    return createStimmeingabeKopfzahlWahlberechtigte(context, gruppeImGebiet, stimmenart, anzahlExpected, anzahlEingabeInitialVorbelegen, anzahlEingabeReadonly);
  }

  private StimmeingabeKopfzahl createStimmeingabeKopfzahlWahlberechtigteGesamt(StimmeingabeErfassenContext context, GruppeImGebiet gruppeImGebiet,
      List<StimmeingabeKopfzahl> stimmeingabenWahlberechtigteCreated) {
    Stimmenart stimmenart = Stimmenart.GESAMT;
    Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet = context.getWahldurchfuehrungsgebiet();
    WahlartErgebnisart ergebnisart = context.getErgebniseingangsart();
    Long anzahlExpected = getStimmeingabeKopfzahlGesamtAnzahlExpected(context);

    boolean anzahlEingabeInitialVorbelegen = getStimmeingabeKopfzahlKonfiguration().isWahlberechtigteGesamtBeiStimmeingabeInitialVorzubelegen(wahldurchfuehrungsgebiet, ergebnisart, context);
    boolean anzahlEingabeReadonly = getStimmeingabeKopfzahlKonfiguration().isWahlberechtigteGesamtBeiStimmeingabeNurAnzuzeigen(wahldurchfuehrungsgebiet, ergebnisart, context);

    return createStimmeingabeKopfzahlWahlberechtigte(context, gruppeImGebiet, stimmenart, anzahlExpected, anzahlEingabeInitialVorbelegen, anzahlEingabeReadonly);
  }

  private StimmeingabeKopfzahl createStimmeingabeKopfzahlWaehlerOhneWahlschein(StimmeingabeErfassenContext context, GruppeImGebiet gruppeImGebiet) {
    Stimmenart stimmenart = Stimmenart.OHNE_WAHLSCHEIN;
    Long anzahlExpected = getStimmeingabeKopfzahlOhneWahlscheinAnzahlExpected(context);

    return createStimmeingabeKopfzahl(context, gruppeImGebiet, stimmenart, anzahlExpected);
  }

  private StimmeingabeKopfzahl createStimmeingabeKopfzahlWaehlerMitWahlschein(StimmeingabeErfassenContext context, GruppeImGebiet gruppeImGebiet) {
    Stimmenart stimmenart = Stimmenart.MIT_WAHLSCHEIN;
    Long anzahlExpected = getStimmeingabeKopfzahlMitWahlscheinAnzahlExpected(context);

    return createStimmeingabeKopfzahl(context, gruppeImGebiet, stimmenart, anzahlExpected);
  }

  private StimmeingabeKopfzahl createStimmeingabeKopfzahlWaehlerMitWahlscheinInUrnenwahlAnzahlAusUrnenwahl(StimmeingabeErfassenContext context, GruppeImGebiet gruppeImGebiet) {
    return createStimmeingabeKopfzahl(context, gruppeImGebiet, Stimmenart.ALS_URNENWAHL_IN_URNE);
  }

  private StimmeingabeKopfzahl createStimmeingabeKopfzahlWaehlerMitWahlscheinInUrnenwahlAnzahlAusBriefwahl(StimmeingabeErfassenContext context, GruppeImGebiet gruppeImGebiet) {
    return createStimmeingabeKopfzahl(context, gruppeImGebiet, Stimmenart.ALS_BRIEFWAHL_IN_URNE);
  }

  private StimmeingabeKopfzahl createStimmeingabeKopfzahlWaehlerMitVollmacht(StimmeingabeErfassenContext context, GruppeImGebiet gruppeImGebiet) {
    return createStimmeingabeKopfzahl(context, gruppeImGebiet, Stimmenart.MIT_VOLLMACHT);
  }

  @Nullable
  private Long getStimmeingabeKopfzahlOhneWahlscheinAnzahlExpected(StimmeingabeErfassenContext context) {
    return getStimmeingabeKopfzahlKonfiguration().extractWahlberechtigteOhneWahlscheinAmGebietHinterlegtOrNull(context.getWahldurchfuehrungsgebiet(), context.getErgebniseingangsart(), context);
  }

  @Nullable
  private Long getStimmeingabeKopfzahlMitWahlscheinAnzahlExpected(StimmeingabeErfassenContext context) {
    return getStimmeingabeKopfzahlKonfiguration().extractWahlberechtigteMitWahlscheinAmGebietHinterlegtOrNull(context.getWahldurchfuehrungsgebiet(), context.getErgebniseingangsart(), context);
  }

  @Nullable
  private Long getStimmeingabeKopfzahlNichtImWaehlerverzeichnisAnzahlExpected(StimmeingabeErfassenContext context) {
    return getStimmeingabeKopfzahlKonfiguration().extractWahlberechtigteNichtImWaehlerverzeichnisAmGebietHinterlegtOrNull(context.getWahldurchfuehrungsgebiet(), context.getErgebniseingangsart(), context);
  }

  @Nullable
  private Long getStimmeingabeKopfzahlGesamtAnzahlExpected(StimmeingabeErfassenContext context) {
    return getStimmeingabeKopfzahlKonfiguration().extractWahlberechtigteGesamtAmGebietHinterlegtOrNull(context.getWahldurchfuehrungsgebiet(), context.getErgebniseingangsart(), context);
  }

  protected StimmeingabeKopfzahl createStimmeingabeKopfzahl(StimmeingabeErfassenContext context, GruppeImGebiet gruppeImGebiet, Stimmenart stimmenart) {
    return createStimmeingabeKopfzahl(context, gruppeImGebiet, stimmenart, null);
  }

  protected StimmeingabeKopfzahl createStimmeingabeKopfzahl(StimmeingabeErfassenContext context, GruppeImGebiet gruppeImGebiet, Stimmenart stimmenart, @Nullable Long anzahlExpected) {
    return createStimmeingabeKopfzahl(context, gruppeImGebiet, stimmenart, anzahlExpected == null ? null : anzahlExpected.toString(), anzahlExpected);
  }

  private StimmeingabeKopfzahl createStimmeingabeKopfzahl(StimmeingabeErfassenContext context, GruppeImGebiet gruppeImGebiet, Stimmenart stimmenart, @Nullable String anzahlEingabeExpected,
      @Nullable Long anzahlExpected) {
    Stimmeingabe stimmeingabe = createStimmeingabeIfStimmenartApplicable(gruppeImGebiet, stimmenart, context);

    StimmeingabeKopfzahlErfassenLabelGenerator stimmeingabeKopfzahlErfassenLabelGenerator = context.getStimmeingabeKopfzahlErfassenLabelGenerator();
    Collection<GruppeImGebiet> gruppenImGebiet = context.getGruppenImGebiet();

    String kennung = stimmeingabeKopfzahlErfassenLabelGenerator.getKopfzahlKennung(gruppeImGebiet, gruppenImGebiet);
    String bezeichnung = stimmeingabeKopfzahlErfassenLabelGenerator.getKopfzahlBezeichnung(gruppeImGebiet, gruppenImGebiet);

    String stimmeingabeKennung = stimmeingabeKopfzahlErfassenLabelGenerator.getKopfzahlStimmeingabeKennung(stimmeingabe, gruppenImGebiet);
    String stimmeingabeBezeichnung = stimmeingabeKopfzahlErfassenLabelGenerator.getKopfzahlStimmeingabeBezeichnung(stimmeingabe, gruppenImGebiet);

    List<Stimmeingabefehler> stimmeingabefehlerLetzterErgebniseingang = getStimmeingabefehlerLetzterErgebniseingang(stimmeingabe, context);

    Stimmeingabe stimmeingabeVergleich = getStimmeingabeVergleich(stimmeingabe, context);

    return new StimmeingabeKopfzahl(kennung, bezeichnung, stimmeingabe, stimmeingabeKennung, stimmeingabeBezeichnung, anzahlEingabeExpected, anzahlExpected, stimmeingabefehlerLetzterErgebniseingang,
        stimmeingabeVergleich);
  }
}
