
package de.ivu.elect.dokumente.wahl;

import static de.ivu.elect.jee.entity.EntityUtil.castTo;
import static java.util.Collections.emptyList;
import static java.util.Collections.emptySet;
import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toMap;
import static org.apache.commons.lang3.StringUtils.defaultIfBlank;
import static org.apache.commons.lang3.StringUtils.equalsIgnoreCase;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import static org.apache.commons.lang3.StringUtils.length;
import static org.apache.commons.lang3.StringUtils.splitByWholeSeparator;

import java.text.SimpleDateFormat;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.TimeZone;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import de.ivu.document.generator.template.BaseDocument;
import de.ivu.document.generator.template.annotations.DocumentComplexField;
import de.ivu.document.generator.template.annotations.DocumentMethod;
import de.ivu.document.generator.template.annotations.DocumentParam;
import de.ivu.document.generator.template.annotations.DocumentQueue;
import de.ivu.document.generator.template.annotations.DocumentSimpleField;
import de.ivu.elect.business.anschrift.entity.Anschrift;
import de.ivu.elect.business.datei.entity.FileType;
import de.ivu.elect.business.dokumente.control.ElectDokumentContext;
import de.ivu.elect.business.exchange.control.FileFormat;
import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
import de.ivu.elect.business.gebietsbaum.entity.GebietPK;
import de.ivu.elect.business.gruppe.entity.Gruppe;
import de.ivu.elect.business.gruppe.entity.GruppePK;
import de.ivu.elect.business.gruppe.entity.NlGruppeCustomData;
import de.ivu.elect.business.gruppe.entity.NlPolitischeGruppe;
import de.ivu.elect.business.gruppe.entity.PolitischeGruppe;
import de.ivu.elect.business.kandidatur.control.NlStimmzettelFormatter;
import de.ivu.elect.business.kandidatur.control.StimmzettelFormatter;
import de.ivu.elect.business.kandidatur.entity.Kandidatur;
import de.ivu.elect.business.kandidatur.entity.Listenkandidatur;
import de.ivu.elect.business.person.entity.PersonendatenPK;
import de.ivu.elect.business.stimmzettel.entity.EntityAufStimmzettelDaten;
import de.ivu.elect.business.wahl.boundary.NlExportArtefaktNachSystemExportstatusHashCode;
import de.ivu.elect.business.wahl.control.AbstractNlExportArtefaktNachSystemExportstatusExportContext;
import de.ivu.elect.business.wahl.entity.NlWahlCreateData;
import de.ivu.elect.business.wahl.entity.NlWahlart;
import de.ivu.elect.business.wahl.entity.Wahl;
import de.ivu.elect.business.wahl.entity.WahlParameterWerte;
import de.ivu.elect.business.wahlvorschlag.entity.Liste;
import de.ivu.elect.business.wahlvorschlag.entity.StelInformationen;
import de.ivu.elect.business.wahlvorschlag.entity.StelInformationenFormatter;
import de.ivu.elect.business.wahlvorschlag.entity.Wahlvorschlag;
import de.ivu.elect.business.wahlvorschlag.entity.WahlvorschlagPK;
import de.ivu.elect.dokumente.AnschriftDokument;
import de.ivu.elect.dokumente.CoreElectContext;
import de.ivu.elect.dokumente.DokumentFormatter;
import de.ivu.elect.dokumente.PersonBasisDokument;
import de.ivu.elect.dokumente.gebiet.BasicGebietDokument;
import de.ivu.elect.dokumente.kandidatur.KandidaturDokument;
import de.ivu.elect.dokumente.wahlvorschlag.AbstractGruppeDokument;
import de.ivu.elect.dokumente.wahlvorschlag.CoreWahlvorschlagsdatenDocument;
import de.ivu.elect.dokumente.wahlvorschlag.WahlvorschlagCoreDaten;
import de.ivu.elect.jee.faces.Messages;

public class NlCoreWahlCustomDataDocument extends BaseDocument {

  private static final long serialVersionUID = 1L;

  public static final FileFormat FILE_FORMAT_UNBEKANNT = new FileFormat("UNBEKANNT", FileType.ALL);

  // "The country's dependencies, island territories in the Caribbean Sea, all lie in the same time zone: Atlantic Standard Time (AST)...", cf. https://www.timeanddate.com/time/zone/netherlands.
  public static final ZoneId ZONE_ID_NL_UEBERSEEGEBIETE = ZoneId.ofOffset("UTC", ZoneOffset.ofHours(-4)); // == UTC -4 AST, keine Sommer-/Winterzeit

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

  private static final String DATE_TIME_PATTERN = "dd.MM.yyyy HH:mm:ss";

  private final Messages messages;

  private final DokumentFormatter formatter;

  private final EntityAufStimmzettelDaten entityAufStimmzettelDaten;

  private final Map<Long, ? extends Gruppe> gruppePerId;

  private final Map<Long, ? extends Gebiet> gebietPerId;

  private final Map<Long, Liste> listePerId;

  private final Map<Long, List<Liste>> listenPerGruppeId;

  private final Map<Long, Listenkandidatur> listenkandidaturPerId;

  private final TimeZone timeZone;

  private final Map<FileFormat, Collection<NlExportArtefaktNachSystemExportstatusHashCode>> hashCodesPerFileFormat;

  @DocumentSimpleField("Datum-Aktuell")
  public final Date datumAktuell;

  @DocumentSimpleField("Datum-Aktuell-Formatted-DDMMYYYY")
  public final String datumAktuellFormattedDDMMYYYY;

  @DocumentSimpleField("Datum-Aktuell-Formatted-Footer")
  public final String datumAktuellFormattedFooter;

  @DocumentComplexField("Wahlgebiet")
  public final BasicGebietDokument wahlgebiet;

  @DocumentSimpleField("Wahlgebiet-Gebietsart-Name")
  public final String wahlgebietGebietsartName; // ELECT-14227

  @DocumentSimpleField("Gebiet-CSB-Name")
  public final String gebietCSBName;

  @DocumentSimpleField("Wahlebene-CSB")
  public final boolean wahlebeneCSB;

  @DocumentSimpleField("Wahlebene-HSB")
  public final boolean wahlebeneHSB;

  @DocumentSimpleField("Wahlebene-PSB")
  public final boolean wahlebenePSB;

  @DocumentSimpleField("Wahlbehoerde-Wahlleiter-Name")
  public final String wahlbehoerdeWahlleiterName;

  @DocumentComplexField("Wahlbehoerde-Anschrift")
  public final AnschriftDokument wahlbehoerdeAnschrift;

  @DocumentComplexField("Wahlbehoerde-Dokument-Erstellt-Anschrift")
  public final AnschriftDokument wahlbehoerdeDokumentErstelltAnschrift;

  @DocumentSimpleField("Wahlbehoerde-Dokument-Erstellt-Am")
  public final Date wahlbehoerdeDokumentErstelltAm;

  @DocumentSimpleField("Wahlbehoerde-Dokument-Erstellt-Am-Formatted-DDMMYYYY")
  public final String wahlbehoerdeDokumentErstelltAmFormattedDDMMYYYY;

  @DocumentSimpleField("Wahlbehoerde-Dokument-Erstellt-Am-Formatted-Footer")
  public final String wahlbehoerdeDokumentErstelltAmFormattedFooter;

  @DocumentComplexField("Gewaehltes-Gremium")
  public final String gewaehltesGremium;

  @DocumentComplexField("Gewaehltes-Gremium-Von")
  public final String gewaehltesGremiumVon; // ELECT-14237

  @DocumentComplexField("Gewaehltes-Gremium-Von-Wahlgebiet")
  public final String gewaehltesGremiumVonWahlgebiet; // ELECT-14237

  @DocumentComplexField("Gewaehltes-Gremium-Durchfuehrendes-Organ")
  public final String gewaehltesGremiumDurchfuehrendesOrgan;

  @DocumentComplexField("Gewaehltes-Gremium-Anschrift")
  public final AnschriftDokument gewaehltesGremiumAnschrift;

  @DocumentSimpleField("Wahlausschusssitzung-Zuvor-Beginn")
  public final Date wahlausschusssitzungZuvorBeginn;

  @DocumentSimpleField("Wahlausschusssitzung-Zuvor-Beginn-Formatted-Datum")
  public final String wahlausschusssitzungZuvorBeginnFormattedDatum;

  @DocumentSimpleField("Wahlausschusssitzung-Zuvor-Beginn-Formatted-Zeit")
  public final String wahlausschusssitzungZuvorBeginnFormattedZeit;

  @DocumentComplexField("Wahlausschusssitzung-Anschrift")
  public final AnschriftDokument wahlausschusssitzungAnschrift;

  @DocumentSimpleField("Wahlausschusssitzung-Beginn")
  public final Date wahlausschusssitzungBeginn;

  @DocumentSimpleField("Wahlausschusssitzung-Beginn-Formatted-Datum")
  public final String wahlausschusssitzungBeginnFormattedDatum;

  @DocumentSimpleField("Wahlausschusssitzung-Beginn-Formatted-Zeit")
  public final String wahlausschusssitzungBeginnFormattedZeit;

  @DocumentSimpleField("Wahlausschusssitzung-Ende")
  public final Date wahlausschusssitzungEnde;

  @DocumentSimpleField("Wahlausschusssitzung-Ende-Formatted-Datum")
  public final String wahlausschusssitzungEndeFormattedDatum;

  @DocumentSimpleField("Wahlausschusssitzung-Ende-Formatted-Zeit")
  public final String wahlausschusssitzungEndeFormattedZeit;

  @DocumentSimpleField("Wahlausschusssitzung-Beginn-Ueberseegebiete")
  public final Date wahlausschusssitzungBeginnUeberseegebiete;

  @DocumentSimpleField("Wahlausschusssitzung-Beginn-Ueberseegebiete-Formatted-Datum")
  public final String wahlausschusssitzungBeginnUeberseegebieteFormattedDatum;

  @DocumentSimpleField("Wahlausschusssitzung-Beginn-Ueberseegebiete-Formatted-Zeit")
  public final String wahlausschusssitzungBeginnUeberseegebieteFormattedZeit;

  @DocumentSimpleField("Wahlausschusssitzung-Ende-Ueberseegebiete")
  public final Date wahlausschusssitzungEndeUeberseegebiete;

  @DocumentSimpleField("Wahlausschusssitzung-Ende-Ueberseegebiete-Formatted-Datum")
  public final String wahlausschusssitzungEndeUeberseegebieteFormattedDatum;

  @DocumentSimpleField("Wahlausschusssitzung-Ende-Ueberseegebiete-Formatted-Zeit")
  public final String wahlausschusssitzungEndeUeberseegebieteFormattedZeit;

  @DocumentSimpleField("Wahlausschusssitzung-Fehler-Vermutet")
  public final String wahlausschusssitzungFehlerVermutet;

  @DocumentQueue(value = "Wahlausschusssitzung-Fehler-Vermutet-Zeilen", elementClass = String.class)
  public final LinkedList<String> wahlausschusssitzungFehlerVermutetZeilen;

  @DocumentSimpleField("Wahlausschusssitzung-Einwaende")
  public final String wahlausschusssitzungEinwaende;

  @DocumentQueue(value = "Wahlausschusssitzung-Einwaende-Zeilen", elementClass = String.class)
  public final LinkedList<String> wahlausschusssitzungEinwaendeZeilen;

  @DocumentSimpleField("Wahlausschusssitzung-Einwaende-Bemerkungen")
  public final String wahlausschusssitzungBemerkungen;

  @DocumentQueue(value = "Wahlausschusssitzung-Einwaende-Bemerkungen-Zeilen", elementClass = String.class)
  public final LinkedList<String> wahlausschusssitzungBemerkungenZeilen;

  @DocumentSimpleField("Wahlausschusssitzung-Unterzeichner-1-Name")
  public final String wahlausschusssitzungUnterzeichner1Name;

  @DocumentSimpleField("Wahlausschusssitzung-Unterzeichner-1-Rolle")
  public final String wahlausschusssitzungUnterzeichner1Rolle;

  @DocumentSimpleField("Wahlausschusssitzung-Unterzeichner-2-Name")
  public final String wahlausschusssitzungUnterzeichner2Name;

  @DocumentSimpleField("Wahlausschusssitzung-Unterzeichner-2-Rolle")
  public final String wahlausschusssitzungUnterzeichner2Rolle;

  @DocumentSimpleField("Frist-Beginn")
  public final Date fristZuletztGesetztBeginn;

  @DocumentSimpleField("Frist-Beginn-Formatted-Datum")
  public final String fristZuletztGesetztBeginnFormattedDatum;

  @DocumentSimpleField("Frist-Beginn-Formatted-Zeit")
  public final String fristZuletztGesetztBeginnFormattedZeit;

  @DocumentSimpleField("Frist-Ende")
  public final Date fristZuletztGesetztEnde;

  @DocumentSimpleField("Frist-Ende-Formatted-Datum")
  public final String fristZuletztGesetztEndeFormattedDatum;

  @DocumentSimpleField("Frist-Ende-Formatted-Zeit")
  public final String fristZuletztGesetztEndeFormattedZeit;

  @DocumentComplexField("Wahlvorschlag-Einreichung-Anschrift")
  public final AnschriftDokument wahlvorschlagEinreichungAnschrift;

  @DocumentSimpleField("Wahlvorschlag-Einreichung-Abgabefrist")
  public final Date wahlvorschlagEinreichungAbgabefrist;

  @DocumentSimpleField("Wahlvorschlag-Einreichung-Abgabefrist-Formatted-Datum")
  public final String wahlvorschlagEinreichungAbgabefristFormattedDatum;

  @DocumentSimpleField("Wahlvorschlag-Einreichung-Unterzeichner-1-Name")
  public final String wahlvorschlagEinreichungUnterzeichner1Name;

  @DocumentComplexField("Wahlvorschlag-Veroeffentlichung-Anschrift")
  public final AnschriftDokument wahlvorschlagVeroeffentlichungAnschrift;

  @DocumentSimpleField("Wahlvorschlag-Veroeffentlichung-Beginn")
  public final Date wahlvorschlagVeroeffentlichungBeginn;

  @DocumentSimpleField("Wahlvorschlag-Veroeffentlichung-Beginn-Formatted-Datum")
  public final String wahlvorschlagVeroeffentlichungBeginnFormattedDatum;

  @DocumentSimpleField("Wahlvorschlag-Veroeffentlichung-Unterzeichner-1-Name")
  public final String wahlvorschlagVeroeffentlichungUnterzeichner1Name;

  @DocumentSimpleField("Einwohnerzahl-Veroeffentlichung-ID")
  public final String einwohnerzahlVeroeffentlichungId;

  @DocumentSimpleField("Einwohnerzahl-Veroeffentlichung-Datum")
  public final Date einwohnerzahlVeroeffentlichungDatum;

  @DocumentSimpleField("Einwohnerzahl-Veroeffentlichung-Datum-Formatted-DDMMYYYY")
  public final String einwohnerzahlVeroeffentlichungDatumFormattedDDMMYYYY;

  @DocumentSimpleField("Einwohnerzahl-Veroeffentlichung-Datum-Formatted-YYYY")
  public final String einwohnerzahlVeroeffentlichungDatumFormattedYYYY;

  @DocumentSimpleField("Korrekturen-Stimmeingaben-Kopfzahlen-Waehler-Vorhanden")
  public final Boolean korrekturenStimmeingabenKopfzahlenWaehlerVorhanden;

  @DocumentSimpleField("Korrekturen-Stimmeingaben-Kopfzahlen-Stimmzettel-Vorhanden")
  public final Boolean korrekturenStimmeingabenKopfzahlenStimmzettelVorhanden;

  @DocumentSimpleField("Korrekturen-Stimmeingaben-Kopfzahlen-Stimmzettel-Abweichend-Vorhanden")
  public final Boolean korrekturenStimmeingabenKopfzahlenStimmzettelAbweichendVorhanden;

  @DocumentSimpleField("Korrekturen-Stimmeingaben-Stimmen-Listen-Vorhanden")
  public final Boolean korrekturenStimmeingabenStimmenListenVorhanden;

  private final List<Long> korrekturenStimmeingabenStimmenListenIds;

  public NlCoreWahlCustomDataDocument(CoreElectContext coreElectContext, EntityAufStimmzettelDaten entityAufStimmzettelDaten, Supplier<Collection<Gruppe>> gruppenInWahlLoader,
      Supplier<Collection<Gebiet>> gebieteInWahlLoader, Supplier<Collection<Wahlvorschlag>> wahlvorschlaegeInWahlLoader) {
    super();

    this.messages = coreElectContext.getMessages();

    this.formatter = new DokumentFormatter(coreElectContext.getLocale());

    this.entityAufStimmzettelDaten = entityAufStimmzettelDaten;

    Wahl wahl = coreElectContext.getWahl();

    Collection<Gebiet> gebieteInWahl = new HashSet<>(gebieteInWahlLoader.get());

    AbstractNlExportArtefaktNachSystemExportstatusExportContext exportContext = createExportContext(coreElectContext, gebieteInWahl); // ELECT-9946

    this.gruppePerId = gruppenInWahlLoader.get().stream().collect(toMap(Gruppe::getId, Function.identity()));

    this.gebietPerId = gebieteInWahl.stream().collect(toMap(Gebiet::getId, Function.identity()));

    Collection<Liste> listenInWahl = getListenInWahl(wahl, coreElectContext);

    Collection<Listenkandidatur> listenkandidaturenInWahl = getListenkandidaturen(wahl, coreElectContext);

    this.listePerId = listenInWahl.stream().collect(toMap(Liste::getId, Function.identity()));

    this.listenPerGruppeId = listenInWahl.stream().collect(groupingBy(liste -> liste.getGruppe().getId()));

    this.listenkandidaturPerId = listenkandidaturenInWahl.stream().collect(toMap(Listenkandidatur::getId, Function.identity()));

    this.timeZone = coreElectContext.getTimeZone();

    this.hashCodesPerFileFormat = exportContext.getHashCodesPerFileFormat();

    this.datumAktuell = coreElectContext.getNow();
    this.datumAktuellFormattedDDMMYYYY = this.formatter.formatDatumPattern(this.datumAktuell, "dd-MM-yyyy");
    this.datumAktuellFormattedFooter = this.formatter.formatDatumPattern(this.datumAktuell, "dd-MM-yyyy"); // ELECT-11167

    String wahlgebietName = NlWahlCreateData.getWahlgebietName(wahl, gebieteInWahl);
    String wahlgebietNummer = NlWahlCreateData.getWahlgebietNummer(wahl, gebieteInWahl);
    String wahlgebietGebietsartName = NlWahlCreateData.bestimmeDokumentPlatzhalterWahlgebietGebietsartName(this.messages, wahl, gebieteInWahl);

    this.wahlgebiet = new BasicGebietDokument(coreElectContext, wahlgebietName, wahlgebietNummer, wahlgebietGebietsartName);
    this.wahlgebietGebietsartName = wahlgebietGebietsartName;

    this.gebietCSBName = exportContext.getGebietCsbName(() -> gebieteInWahl);

    this.wahlebeneCSB = NlWahlart.equalsWahlebene(wahl, NlWahlart.KUERZEL_WAHLEBENE_CSB);
    this.wahlebeneHSB = NlWahlart.equalsWahlebene(wahl, NlWahlart.KUERZEL_WAHLEBENE_HSB);
    this.wahlebenePSB = NlWahlart.equalsWahlebene(wahl, NlWahlart.KUERZEL_WAHLEBENE_PSB);

    this.wahlbehoerdeWahlleiterName = exportContext.getWahlbehoerdeWahlleiterName();
    this.wahlbehoerdeAnschrift = createAnschriftDokument(coreElectContext, exportContext.getWahlbehoerdeAnschrift());

    this.wahlbehoerdeDokumentErstelltAnschrift = createAnschriftDokument(coreElectContext, exportContext.getWahlbehoerdeDokumentErstelltAnschrift()); // ELECT-11984
    this.wahlbehoerdeDokumentErstelltAm = exportContext.getWahlbehoerdeDokumentErstelltAm() == null ? coreElectContext.getNow() : exportContext.getWahlbehoerdeDokumentErstelltAm();
    this.wahlbehoerdeDokumentErstelltAmFormattedDDMMYYYY = this.formatter.formatDatumPattern(this.wahlbehoerdeDokumentErstelltAm, "dd-MM-yyyy");
    this.wahlbehoerdeDokumentErstelltAmFormattedFooter = this.formatter.formatDatumPattern(this.wahlbehoerdeDokumentErstelltAm, "dd-MM-yyyy"); // ELECT-11167

    this.gewaehltesGremium = coreElectContext.getWahlParameterLoader().resolveStringValue(wahl, WahlParameterWerte.DOKUMENT_PLATZHALTER_ZU_WAEHLENDES_GREMIUM, "");
    this.gewaehltesGremiumVon = NlWahlCreateData.bestimmeDokumentPlatzhalterZuWaehlendesGremiumVon(messages, wahl, gebieteInWahl);
    this.gewaehltesGremiumVonWahlgebiet = NlWahlCreateData.bestimmeDokumentPlatzhalterZuWaehlendesGremiumVonWahlgebiet(messages, wahl, gebieteInWahl);
    this.gewaehltesGremiumDurchfuehrendesOrgan = exportContext.getGewaehltesGremiumDurchfuehrendesOrgan();
    this.gewaehltesGremiumAnschrift = createAnschriftDokument(coreElectContext, exportContext.getGewaehltesGremiumAnschrift());

    this.wahlausschusssitzungZuvorBeginn = exportContext.getWahlausschusssitzungZuvorBeginn();
    this.wahlausschusssitzungZuvorBeginnFormattedDatum = formatDatum(this.wahlausschusssitzungZuvorBeginn);
    this.wahlausschusssitzungZuvorBeginnFormattedZeit = formatZeit(this.wahlausschusssitzungZuvorBeginn);

    this.wahlausschusssitzungAnschrift = createAnschriftDokument(coreElectContext, exportContext.getWahlausschusssitzungAnschrift());

    this.wahlausschusssitzungBeginn = exportContext.getWahlausschusssitzungBeginn();
    this.wahlausschusssitzungBeginnFormattedDatum = formatDatum(this.wahlausschusssitzungBeginn);
    this.wahlausschusssitzungBeginnFormattedZeit = formatZeit(this.wahlausschusssitzungBeginn);

    this.wahlausschusssitzungEnde = exportContext.getWahlausschusssitzungEnde();
    this.wahlausschusssitzungEndeFormattedDatum = formatDatum(this.wahlausschusssitzungEnde);
    this.wahlausschusssitzungEndeFormattedZeit = formatZeit(this.wahlausschusssitzungEnde);

    this.wahlausschusssitzungBeginnUeberseegebiete = calculateDateTimeInZoneId(this.wahlausschusssitzungBeginn, ZONE_ID_NL_UEBERSEEGEBIETE);
    this.wahlausschusssitzungBeginnUeberseegebieteFormattedDatum = formatDatum(this.wahlausschusssitzungBeginnUeberseegebiete);
    this.wahlausschusssitzungBeginnUeberseegebieteFormattedZeit = formatZeit(this.wahlausschusssitzungBeginnUeberseegebiete);

    this.wahlausschusssitzungEndeUeberseegebiete = calculateDateTimeInZoneId(this.wahlausschusssitzungEnde, ZONE_ID_NL_UEBERSEEGEBIETE);
    this.wahlausschusssitzungEndeUeberseegebieteFormattedDatum = formatDatum(this.wahlausschusssitzungEndeUeberseegebiete);
    this.wahlausschusssitzungEndeUeberseegebieteFormattedZeit = formatZeit(this.wahlausschusssitzungEndeUeberseegebiete);

    this.wahlausschusssitzungFehlerVermutet = exportContext.getWahlausschusssitzungFehlerVermutet();
    this.wahlausschusssitzungFehlerVermutetZeilen = collectZeilen(this.wahlausschusssitzungFehlerVermutet);

    this.wahlausschusssitzungEinwaende = defaultIfBlank(exportContext.getWahlausschusssitzungEinwaende(), this.wahlausschusssitzungFehlerVermutet); // ELECTVAPP-2133
    this.wahlausschusssitzungEinwaendeZeilen = collectZeilen(this.wahlausschusssitzungEinwaende);

    this.wahlausschusssitzungBemerkungen = exportContext.getWahlausschusssitzungEinwaendeBemerkungen();
    this.wahlausschusssitzungBemerkungenZeilen = collectZeilen(this.wahlausschusssitzungBemerkungen);

    this.fristZuletztGesetztBeginn = exportContext.getFristZuletztGesetztBeginn();
    this.fristZuletztGesetztBeginnFormattedDatum = formatDatum(this.fristZuletztGesetztBeginn);
    this.fristZuletztGesetztBeginnFormattedZeit = formatZeit(this.fristZuletztGesetztBeginn);

    this.wahlausschusssitzungUnterzeichner1Name = exportContext.getWahlausschusssitzungUnterzeichner1Name();
    this.wahlausschusssitzungUnterzeichner1Rolle = exportContext.getWahlausschusssitzungUnterzeichner1Rolle();
    this.wahlausschusssitzungUnterzeichner2Name = exportContext.getWahlausschusssitzungUnterzeichner2Name();
    this.wahlausschusssitzungUnterzeichner2Rolle = exportContext.getWahlausschusssitzungUnterzeichner2Rolle();

    this.fristZuletztGesetztEnde = exportContext.getFristZuletztGesetztEnde();
    this.fristZuletztGesetztEndeFormattedDatum = formatDatum(this.fristZuletztGesetztEnde);
    this.fristZuletztGesetztEndeFormattedZeit = formatZeit(this.fristZuletztGesetztEnde);

    this.wahlvorschlagEinreichungAbgabefrist = exportContext.getWahlvorschlagEinreichungAbgabefrist();
    this.wahlvorschlagEinreichungAbgabefristFormattedDatum = formatDatum(this.wahlvorschlagEinreichungAbgabefrist);
    this.wahlvorschlagEinreichungAnschrift = createAnschriftDokument(coreElectContext, exportContext.getWahlvorschlagEinreichungAnschrift());
    this.wahlvorschlagEinreichungUnterzeichner1Name = exportContext.getWahlvorschlagEinreichungUnterzeichner1Name();

    this.wahlvorschlagVeroeffentlichungBeginn = exportContext.getWahlvorschlagVeroeffentlichungBeginn();
    this.wahlvorschlagVeroeffentlichungBeginnFormattedDatum = formatDatum(this.wahlvorschlagVeroeffentlichungBeginn);
    this.wahlvorschlagVeroeffentlichungAnschrift = createAnschriftDokument(coreElectContext, exportContext.getWahlvorschlagVeroeffentlichungAnschrift());
    this.wahlvorschlagVeroeffentlichungUnterzeichner1Name = exportContext.getWahlvorschlagVeroeffentlichungUnterzeichner1Name();

    this.einwohnerzahlVeroeffentlichungId = exportContext.getEinwohnerzahlVeroeffentlichungId();
    this.einwohnerzahlVeroeffentlichungDatum = exportContext.getEinwohnerzahlVeroeffentlichungDatum();
    this.einwohnerzahlVeroeffentlichungDatumFormattedDDMMYYYY = this.formatter.formatDatumPattern(this.einwohnerzahlVeroeffentlichungDatum, "dd-MM-yyyy");
    this.einwohnerzahlVeroeffentlichungDatumFormattedYYYY = this.formatter.formatDatumPattern(this.einwohnerzahlVeroeffentlichungDatum, "yyyy");

    // ELECT-15235
    this.korrekturenStimmeingabenKopfzahlenWaehlerVorhanden = exportContext.isKorrekturenStimmeingabenKopfzahlenWaehlerVorhanden();
    this.korrekturenStimmeingabenKopfzahlenStimmzettelVorhanden = exportContext.isKorrekturenStimmeingabenKopfzahlenStimmzettelVorhanden();
    this.korrekturenStimmeingabenKopfzahlenStimmzettelAbweichendVorhanden = exportContext.isKorrekturenStimmeingabenKopfzahlenStimmzettelAbweichendVorhanden();
    this.korrekturenStimmeingabenStimmenListenIds = exportContext.getKorrekturenStimmeingabenStimmenListenIds();
    this.korrekturenStimmeingabenStimmenListenVorhanden = !this.korrekturenStimmeingabenStimmenListenIds.isEmpty();

    dump(this);
  }

  public String formatKandidaturNamenAufStimmzettel(Listenkandidatur listenkandidatur) {
    StimmzettelFormatter stimmzettelFormatter = entityAufStimmzettelDaten.getStimmzettelFormatter();

    if (stimmzettelFormatter instanceof NlStimmzettelFormatter) {
      return ((NlStimmzettelFormatter) stimmzettelFormatter).formatKandidaturNamenInDokument(listenkandidatur);
    }

    return stimmzettelFormatter.formatKandidaturNamen(listenkandidatur);
  }

  public String formatKandidaturVornamenAufStimmzettel(Listenkandidatur listenkandidatur) {
    StimmzettelFormatter stimmzettelFormatter = entityAufStimmzettelDaten.getStimmzettelFormatter();

    if (stimmzettelFormatter instanceof NlStimmzettelFormatter) {
      return ((NlStimmzettelFormatter) stimmzettelFormatter).formatKandidaturVornamenInDokument(listenkandidatur);
    }

    return stimmzettelFormatter.formatKandidaturVornamen(listenkandidatur);
  }

  private Collection<Listenkandidatur> getListenkandidaturen(Wahl wahl, CoreElectContext coreElectContext) {
    String key = "LISTENKANDIDATUREN_" + wahl.getId();
    List result = coreElectContext.computeIfAbsent(key, List.class, x -> ladeListenkandidaturen(wahl, coreElectContext));

    return result;
  }

  private List<Listenkandidatur> ladeListenkandidaturen(Wahl wahl, CoreElectContext coreElectContext) {
    if (this.entityAufStimmzettelDaten.getListenkandidaturDAO() == null) {
      List<Listenkandidatur> result = new ArrayList<>(10000);

      for (Liste liste : getListenInWahl(wahl, coreElectContext)) {
        result.addAll(liste.getListenkandidaturen());
      }

      return result;
    } else {
      return this.entityAufStimmzettelDaten.getListenkandidaturDAO().findByWahl(wahl);
    }
  }

  private Collection<Liste> getListenInWahl(Wahl wahl, CoreElectContext coreElectContext) {
    String key = "LISTEN_" + wahl.getId();
    Set result = coreElectContext.computeIfAbsent(key, Set.class, x -> this.entityAufStimmzettelDaten.collectListenInWahl(wahl));

    return result;
  }

  @DocumentMethod
  public Long getAnzahlListenstimmenVorperiodeByGruppe(@DocumentParam("gruppe") Object gruppeReference) {
    return findGruppe(gruppeReference).map(this::getAnzahlListenstimmenVorperiode).orElse(0L);
  }

  public long getAnzahlListenstimmenVorperiode(Gruppe gruppe) {
    return gruppe.getCustomData(NlGruppeCustomData.class).getAnzahlStimmenVorperiodeNotNull();
  }

  @DocumentMethod
  public Long getAnzahlSitzeVorperiodeByGruppe(@DocumentParam("gruppe") Object gruppeReference) {
    return findGruppe(gruppeReference).map(this::getAnzahlSitzeVorperiode).orElse(0L);
  }

  public long getAnzahlSitzeVorperiode(Gruppe gruppe) {
    return gruppe.getCustomData(NlGruppeCustomData.class).getAnzahlSitzeVorperiodeNotNull();
  }

  public boolean existsHashCode(FileFormat fileFormat) {
    return fileFormat != null && hashCodesPerFileFormat.containsKey(fileFormat);
  }

  public String getHashCode(FileFormat fileFormat) {
    if (fileFormat == null || FILE_FORMAT_UNBEKANNT.equals(fileFormat)) {
      return "";
    }

    Collection<NlExportArtefaktNachSystemExportstatusHashCode> hashCodes = hashCodesPerFileFormat.getOrDefault(fileFormat, emptySet());

    if (hashCodes.isEmpty()) {
      return "";
    }

    String hashCodeUnique = hashCodes.iterator().next().getHashCode();

    for (NlExportArtefaktNachSystemExportstatusHashCode hashCode : hashCodes) {
      if (!equalsIgnoreCase(hashCodeUnique, hashCode.getHashCode())) {
        warn("Hash-Code konnte NICHT EINEINDEUTIG gefunden werden [fileFormat = {}, hashCodes = [count = {}, {}]]", fileFormat, hashCodes.size(),
            hashCodes.stream().map(NlExportArtefaktNachSystemExportstatusHashCode::toStringIgnoreFileFormat).collect(joining(", ")));

        return "";
      }
    }

    return hashCodeUnique;
  }

  public String getHashCode(FileFormat fileFormat, Gebiet gebiet) {
    if (fileFormat != null && gebiet != null) {
      return hashCodesPerFileFormat.getOrDefault(fileFormat, emptySet()).stream() //
          .filter(hashCode -> gebiet.getTk().equals(hashCode.getGebietTk())) //
          .findFirst() //
          .map(NlExportArtefaktNachSystemExportstatusHashCode::getHashCode) //
          .orElse("");
    }

    return "";
  }

  @DocumentMethod
  public Date calculateDateTimeInZoneId(@DocumentParam("dateTime") Date date, Object zoneId) {
    if (date != null && (zoneId instanceof ZoneId || zoneId instanceof String)) {
      try {
        ZoneId sourceZoneId = timeZone.toZoneId();
        ZoneId targetZoneId = ZoneId.of(zoneId.toString());

        ZonedDateTime sourceZonedDateTime = ZonedDateTime.ofInstant(date.toInstant(), sourceZoneId);
        ZonedDateTime targetZonedDateTime = sourceZonedDateTime.withZoneSameInstant(targetZoneId);

        return new SimpleDateFormat(DATE_TIME_PATTERN).parse(targetZonedDateTime.format(DateTimeFormatter.ofPattern(DATE_TIME_PATTERN)));
      } catch (Exception ex) {
        error("Datum konnte NICHT berechnet werden [date = {}, zoneId = {}]", date, zoneId, ex);
      }
    }

    return date;
  }

  protected String formatDatum(Date date) {
    return formatter.formatDatumLang(date);
  }

  protected String formatZeit(Date date) {
    return formatter.formatZeit(date);
  }

  protected LinkedList<String> collectZeilen(String value) {
    LinkedList<String> result = new LinkedList<>();

    if (isNotBlank(value)) {
      for (String line : splitByWholeSeparator(value, System.lineSeparator())) {
        if (length(line) > 0) {
          result.add(line);
        }
      }
    }

    return result;
  }

  private AnschriftDokument createAnschriftDokument(CoreElectContext coreElectContext, Anschrift anschrift) {
    return new AnschriftDokument(coreElectContext, anschrift);
  }

  private AbstractNlExportArtefaktNachSystemExportstatusExportContext createExportContext(CoreElectContext coreElectContext, Collection<Gebiet> gebieteInWahl) {
    ElectDokumentContext electDokumentContext = new ElectDokumentContext(coreElectContext.getAnwender(), coreElectContext.getMessages(), coreElectContext.getWahltermin(), coreElectContext.getWahl());
    electDokumentContext.putAll(coreElectContext.getContextParameters()); // ELECT-10233

    return new AbstractNlExportArtefaktNachSystemExportstatusExportContext(electDokumentContext, coreElectContext.getWahlParameterLoader(), gebieteInWahl) {

      private static final long serialVersionUID = 1L;
    };
  }

  public List<String> formatPolitischeGruppeStelWahlgebietePerStel(Object gruppeReference, BiFunction<StelInformationen, List<Gebiet>, String> stelInformationenFormatter) {
    return new StelInformationenFormatter(entityAufStimmzettelDaten).formatGruppeStelWahlgebietePerStel(findListenByGruppe(gruppeReference), stelInformationenFormatter, stelInformationenFormatter,
        stelInformationenFormatter);
  }

  public Collection<StelInformationen> collectPolitischeGruppeStelInformationen(Object gruppeReference) {
    return NlPolitischeGruppe.bestimmeStelInformationen(findListenByGruppe(gruppeReference), entityAufStimmzettelDaten);
  }

  public Optional<PolitischeGruppe> findPolitischeGruppe(Object gruppeReference) {
    return findGruppe(gruppeReference).filter(Gruppe::isPolitischeGruppe).map(gruppe -> castTo(gruppe, PolitischeGruppe.class));
  }

  public Optional<Gruppe> findGruppe(Object gruppeReference) {
    Optional<Gruppe> result = Optional.empty();

    if (gruppeReference instanceof AbstractGruppeDokument) {
      result = findGruppeById(((AbstractGruppeDokument) gruppeReference).id);
    } else if (gruppeReference instanceof GruppePK) {
      result = findGruppeById(((GruppePK) gruppeReference).getId());
    } else if (gruppeReference instanceof Number) {
      result = findGruppeById(((Number) gruppeReference).longValue());
    } else if (gruppeReference instanceof Gruppe) {
      result = Optional.of((Gruppe) gruppeReference);
    }

    if (!result.isPresent()) {
      warn("KEINE Gruppe zur Gruppe-Referenz gefunden [gruppeReference = {}]", gruppeReference);
    }

    return result;
  }

  private Optional<Gruppe> findGruppeById(Long id) {
    return Optional.ofNullable(gruppePerId.get(id));
  }

  public Optional<Gebiet> findGebiet(Object gebietReference) {
    Optional<Gebiet> result = Optional.empty();

    if (gebietReference instanceof BasicGebietDokument) {
      result = findGebietById(((BasicGebietDokument) gebietReference).id);
    } else if (gebietReference instanceof GebietPK) {
      result = findGebietById(((GebietPK) gebietReference).getId());
    } else if (gebietReference instanceof Number) {
      result = findGebietById(((Number) gebietReference).longValue());
    } else if (gebietReference instanceof Gebiet) {
      result = Optional.of((Gebiet) gebietReference);
    }

    if (!result.isPresent()) {
      warn("KEIN Gebiet zur Gebiet-Referenz gefunden [gebietReference = {}]", gebietReference);
    }

    return result;
  }

  private Optional<Gebiet> findGebietById(Long id) {
    return Optional.ofNullable(gebietPerId.get(id));
  }

  public Collection<Liste> findListenByGruppe(Object gruppeReference) {
    return findPolitischeGruppe(gruppeReference).map(politischeGruppe -> listenPerGruppeId.getOrDefault(politischeGruppe.getId(), emptyList())).orElse(emptyList());
  }

  public Optional<Liste> findListe(Object wahlvorschlagReference) {
    Optional<Liste> result = Optional.empty();

    if (wahlvorschlagReference instanceof CoreWahlvorschlagsdatenDocument) {
      result = findListeById(((CoreWahlvorschlagsdatenDocument) wahlvorschlagReference).id);
    } else if (wahlvorschlagReference instanceof WahlvorschlagPK) {
      result = findListeById(((WahlvorschlagPK) wahlvorschlagReference).getId());
    } else if (wahlvorschlagReference instanceof Number) {
      result = findListeById(((Number) wahlvorschlagReference).longValue());
    } else if (wahlvorschlagReference instanceof Liste) {
      return Optional.of((Liste) wahlvorschlagReference);
    }

    if (!result.isPresent()) {
      warn("KEINE Liste zur Wahlvorschlag-Referenz gefunden [wahlvorschlagReference = {}]", wahlvorschlagReference);
    }

    return result;
  }

  private Optional<Liste> findListeById(Long id) {
    return Optional.ofNullable(listePerId.get(id));
  }

  public Optional<Listenkandidatur> findListenkandidatur(Object kandidaturReference) {
    Optional<Listenkandidatur> result = Optional.empty();

    if (kandidaturReference instanceof PersonBasisDokument) { // Inklusive KandidaturDokument, etc...
      result = findListenkandidaturById(((PersonBasisDokument) kandidaturReference).id);
    } else if (kandidaturReference instanceof PersonendatenPK) {
      result = findListenkandidaturById(((PersonendatenPK) kandidaturReference).getId());
    } else if (kandidaturReference instanceof Number) {
      result = findListenkandidaturById(((Number) kandidaturReference).longValue());
    } else if (kandidaturReference instanceof Listenkandidatur) {
      return Optional.of((Listenkandidatur) kandidaturReference);
    }

    if (!result.isPresent()) {
      warn("KEINE Listenkandidatur zur Kandidatur-Referenz gefunden [kandidaturReference = {}]", kandidaturReference);
    }

    return result;
  }

  private Optional<Listenkandidatur> findListenkandidaturById(Long id) {
    return Optional.ofNullable(listenkandidaturPerId.get(id));
  }

  public KandidaturDokument createKandidaturOhneWahlvorschlagDokument(CoreElectContext coreElectContext, WahlvorschlagCoreDaten wahlvorschlagCoreDaten, Kandidatur kandidatur) {
    return new KandidaturDokument(coreElectContext, CoreWahlvorschlagsdatenDocument.EMPTY_OBJECT, wahlvorschlagCoreDaten.getKandidaturCoreDaten(), kandidatur);
  }

  private static void dump(NlCoreWahlCustomDataDocument document) {
    dump("timeZone: {}", document.timeZone);

    dump("hashCodesPerFileFormat: {}", document.hashCodesPerFileFormat.entrySet().size());

    for (Entry<FileFormat, Collection<NlExportArtefaktNachSystemExportstatusHashCode>> entry : document.hashCodesPerFileFormat.entrySet()) {
      dump("\t {}: [count = {}, {}]", entry.getKey(), entry.getValue().size(),
          entry.getValue().stream().map(NlExportArtefaktNachSystemExportstatusHashCode::toStringIgnoreFileFormat).collect(joining(", ")));
    }

    dump("datumAktuell: {}, {}, {}", document.datumAktuell, document.datumAktuellFormattedDDMMYYYY, document.datumAktuellFormattedFooter);

    dump("wahlgebiet: {}, {}, {}", document.wahlgebiet.nummer, document.wahlgebiet.name, document.wahlgebiet.art);
    dump("wahlgebietGebietsartName: {}", document.wahlgebietGebietsartName);

    dump("gebietCSBName: {}", document.gebietCSBName);

    dump("wahlebeneXSB (PSB/HSB/CSB): {}, {}, {}", document.wahlebenePSB, document.wahlebeneHSB, document.wahlebeneCSB);

    dump("wahlbehoerdeWahlleiterName: {}", document.wahlbehoerdeWahlleiterName);
    dump("wahlbehoerdeAnschrift: {}, {}, {}, {}", document.wahlbehoerdeAnschrift.strasse, document.wahlbehoerdeAnschrift.plz, document.wahlbehoerdeAnschrift.ort,
        document.wahlbehoerdeAnschrift.strasseMitHausnummerUndZusatz);

    dump("wahlbehoerdeDokumentErstelltAnschrift: {}, {}, {}, {}", document.wahlbehoerdeDokumentErstelltAnschrift.strasse, document.wahlbehoerdeDokumentErstelltAnschrift.plz,
        document.wahlbehoerdeDokumentErstelltAnschrift.ort, document.wahlbehoerdeDokumentErstelltAnschrift.strasseMitHausnummerUndZusatz);
    dump("wahlbehoerdeDokumentErstelltAm: {}, {}, {}", document.wahlbehoerdeDokumentErstelltAm, document.wahlbehoerdeDokumentErstelltAmFormattedDDMMYYYY,
        document.wahlbehoerdeDokumentErstelltAmFormattedFooter);

    dump("gewaehltesGremium: {}", document.gewaehltesGremium);
    dump("gewaehltesGremiumVon: {}", document.gewaehltesGremiumVon);
    dump("gewaehltesGremiumVonWahlgebiet: {}", document.gewaehltesGremiumVonWahlgebiet);
    dump("gewaehltesGremiumDurchfuehrendesOrgan: {}", document.gewaehltesGremiumDurchfuehrendesOrgan);
    dump("gewaehltesGremiumAnschrift: {}, {}, {}, {}", document.gewaehltesGremiumAnschrift.strasse, document.gewaehltesGremiumAnschrift.plz, document.gewaehltesGremiumAnschrift.ort,
        document.gewaehltesGremiumAnschrift.strasseMitHausnummerUndZusatz);

    dump("wahlausschusssitzungZuvorBeginn: {}", document.wahlausschusssitzungZuvorBeginn, document.wahlausschusssitzungZuvorBeginnFormattedDatum,
        document.wahlausschusssitzungZuvorBeginnFormattedZeit);

    dump("wahlausschusssitzungAnschrift: {}, {}, {}, {}", document.wahlausschusssitzungAnschrift.strasse, document.wahlausschusssitzungAnschrift.plz, document.wahlausschusssitzungAnschrift.ort,
        document.wahlausschusssitzungAnschrift.strasseMitHausnummerUndZusatz);

    dump("wahlausschusssitzungBeginn: {}, {}, {}", document.wahlausschusssitzungBeginn, document.wahlausschusssitzungBeginnFormattedDatum, document.wahlausschusssitzungBeginnFormattedZeit);
    dump("wahlausschusssitzungEnde: {}, {}, {}", document.wahlausschusssitzungEnde, document.wahlausschusssitzungEndeFormattedDatum, document.wahlausschusssitzungEndeFormattedZeit);

    dump("wahlausschusssitzungBeginnUeberseegebiete: {}, {}, {}", document.wahlausschusssitzungBeginnUeberseegebiete, document.wahlausschusssitzungBeginnUeberseegebieteFormattedDatum,
        document.wahlausschusssitzungBeginnUeberseegebieteFormattedZeit);
    dump("wahlausschusssitzungEndeUeberseegebiete: {}, {}, {}", document.wahlausschusssitzungEndeUeberseegebiete, document.wahlausschusssitzungEndeUeberseegebieteFormattedDatum,
        document.wahlausschusssitzungEndeUeberseegebieteFormattedZeit);

    dump("wahlausschusssitzungFehlerVermutet: {}", document.wahlausschusssitzungFehlerVermutet);

    dump("wahlausschusssitzungEinwaende: {}", document.wahlausschusssitzungEinwaende);
    dump("wahlausschusssitzungEinwaendeBemerkungen: {}", document.wahlausschusssitzungBemerkungen);

    dump("wahlausschusssitzungUnterzeichner1: {}, {}", document.wahlausschusssitzungUnterzeichner1Name, document.wahlausschusssitzungUnterzeichner1Rolle);
    dump("wahlausschusssitzungUnterzeichner2: {}, {}", document.wahlausschusssitzungUnterzeichner2Name, document.wahlausschusssitzungUnterzeichner2Rolle);

    dump("fristZuletztGesetztBeginn: {}, {}, {}", document.fristZuletztGesetztBeginn, document.fristZuletztGesetztBeginnFormattedDatum, document.fristZuletztGesetztBeginnFormattedZeit);
    dump("fristZuletztGesetztEnde: {}, {}, {}", document.fristZuletztGesetztEnde, document.fristZuletztGesetztEndeFormattedDatum, document.fristZuletztGesetztEndeFormattedZeit);

    dump("wahlvorschlagEinreichungAbgabefrist: {}, {}", document.wahlvorschlagEinreichungAbgabefrist, document.wahlvorschlagEinreichungAbgabefristFormattedDatum);
    dump("wahlvorschlagEinreichungAnschrift: {}, {}, {}, {}", document.wahlvorschlagEinreichungAnschrift.strasse, document.wahlvorschlagEinreichungAnschrift.plz,
        document.wahlvorschlagEinreichungAnschrift.ort, document.wahlvorschlagEinreichungAnschrift.strasseMitHausnummerUndZusatz);
    dump("wahlvorschlagEinreichungUnterzeichner1Name: {}", document.wahlvorschlagEinreichungUnterzeichner1Name);

    dump("wahlvorschlagVeroeffentlichungBeginn: {}, {}", document.wahlvorschlagVeroeffentlichungBeginn, document.wahlvorschlagVeroeffentlichungBeginnFormattedDatum);
    dump("wahlvorschlagVeroeffentlichungAnschrift: {}, {}, {}, {}", document.wahlvorschlagVeroeffentlichungAnschrift.strasse, document.wahlvorschlagVeroeffentlichungAnschrift.plz,
        document.wahlvorschlagVeroeffentlichungAnschrift.ort, document.wahlvorschlagVeroeffentlichungAnschrift.strasseMitHausnummerUndZusatz);
    dump("wahlvorschlagVeroeffentlichungUnterzeichner1Name: {}", document.wahlvorschlagVeroeffentlichungUnterzeichner1Name);

    dump("einwohnerzahlVeroeffentlichungId: {}", document.einwohnerzahlVeroeffentlichungId);
    dump("einwohnerzahlVeroeffentlichungDatum: {}, {}, {}", document.einwohnerzahlVeroeffentlichungDatum, document.einwohnerzahlVeroeffentlichungDatumFormattedDDMMYYYY,
        document.einwohnerzahlVeroeffentlichungDatumFormattedYYYY);

    dump("korrekturenStimmeingabenKopfzahlenWaehler: {}", document.korrekturenStimmeingabenKopfzahlenWaehlerVorhanden);
    dump("korrekturenStimmeingabenKopfzahlenStimmzettel: {}", document.korrekturenStimmeingabenKopfzahlenStimmzettelVorhanden);
    dump("korrekturenStimmeingabenKopfzahlenStimmzettelAbweichend: {}", document.korrekturenStimmeingabenKopfzahlenStimmzettelAbweichendVorhanden);
    dump("korrekturenStimmeingabenStimmenListen: {} {}", document.korrekturenStimmeingabenStimmenListenVorhanden, document.korrekturenStimmeingabenStimmenListenIds);
  }

  private static void dump(String format, Object... params) {
    LOG.info(format, params);
  }

  private static void warn(String format, Object... params) {
    LOG.warn(format, params);
  }

  private static void error(String format, Object... params) {
    LOG.error(format, params);
  }

  public String getMessage(String msgKey, Object... params) {
    return messages.getMessageForMessageKey(msgKey, params);
  }
}
