
package de.ivu.elect.dokumente.ergebnis.gesamtergebnis;

import static java.util.Collections.singletonList;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toSet;
import static org.apache.commons.lang3.StringUtils.defaultIfBlank;
import static org.apache.commons.lang3.StringUtils.isBlank;

import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.function.BiFunction;
import java.util.function.Function;

import javax.inject.Inject;

import de.ivu.elect.business.dokumente.entity.NoDokumentExtraFileOutput;
import de.ivu.elect.business.ergebnis.control.ErgebnisDaten;
import de.ivu.elect.business.ergebnis.control.ErgebnisdokumentTypExportFiles;
import de.ivu.elect.business.ergebnis.control.gesamtergebnis.ErgebnisGesamtergebnisTabellarischDocumentExportContext;
import de.ivu.elect.business.ergebnis.entity.Ergebnisart;
import de.ivu.elect.business.gebietsbaum.boundary.WahlverwaltungGebietDescriptor;
import de.ivu.elect.business.gebietsbaum.boundary.WahlverwaltungGebietInformation;
import de.ivu.elect.business.gebietsbaum.boundary.WahlverwaltungGemeindeInformation;
import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
import de.ivu.elect.business.gebietsbaum.entity.Verwaltungsart;
import de.ivu.elect.business.gebietsbaum.entity.Wahldurchfuehrungsgebiet;
import de.ivu.elect.business.wahl.entity.Wahldurchfuehrung;
import de.ivu.elect.dokumente.BaseElectContext;
import de.ivu.elect.dokumente.ergebnis.AbstractErgebnisDocumentExportMapper;
import de.ivu.elect.dokumente.ergebnis.ErgebnisDocumentDatenGebietDocument;
import de.ivu.elect.dokumente.ergebnis.ErgebnisDocumentGebietDocument;
import de.ivu.elect.dokumente.ergebnis.zusammenstellung.ErgebnisTabellarischAufteilung;
import de.ivu.elect.dokumente.ergebnis.zusammenstellung.ErgebnisTabellarischAufteilungBlatt;
import de.ivu.elect.dokumente.ergebnis.zusammenstellung.ErgebnisTabellarischContext;
import de.ivu.elect.dokumente.ergebnis.zusammenstellung.ErgebnisTabellarischDocument;
import de.ivu.elect.dokumente.ergebnis.zusammenstellung.ErgebnisTabellarischTabelle;
import de.ivu.elect.was.exchange.ergebnis.gesamtergebnis.ErgebnisGesamtergebnisExchange;
import de.ivu.elect.was.exchange.ergebnis.gesamtergebnis.ErgebnisGesamtergebnisTabellarischDocumentExportMapper;
import de.ivu.elect.was.exchange.ergebnis.zusammenstellung.ErgebnisZusammenstellung.GemeindeTabelle;
import de.ivu.elect.was.exchange.ergebnis.zusammenstellung.ErgebnisZusammenstellung.StimmzettelgebietTabelle;
import de.ivu.elect.was.exchange.ergebnis.zusammenstellung.ErgebnisZusammenstellungDokumentFactory;
import de.ivu.elect.was.exchange.ergebnis.zusammenstellung.ErgebnisZusammenstellungTabelle;

/**
 * Zur Nomenklatur SEITEN und TEILE...
 *
 * <pre>
 * ____________________________________________________________________________________________________________________________
 * | SEITE 1/3, TEIL 1/10           | SEITE 2/3, TEIL 1/10                          | SEITE 3/3, TEIL 1/10                     |
 * |_Gemeinde A Kopfzahlen (Urne)___|_Gemeinde A Gültige/Ungültige/Stimmen (Urne)___|_Gemeinde A Stimmen_(Urne, Fortsetzung)___|
 * ____________________________________________________________________________________________________________________________
 * | SEITE 1/3, TEIL 2/10           | SEITE 2/3, TEIL 2/10                          | SEITE 3/3, TEIL 2/10                     |
 * |_Gemeinde A Kopfzahlen (Brief)__|_Gemeinde A Gültige/Ungültige/Stimmen (Brief)__|_Gemeinde A Stimmen_(Brief, Fortsetzung)__|
 * ____________________________________________________________________________________________________________________________
 * | SEITE 1/3, TEIL 3/10           | SEITE 2/3, TEIL 3/10                          | SEITE 3/3, TEIL 3/10                     |
 * |_Gemeinde A Kopfzahlen (Gesamt)_|_Gemeinde A Gültige/Ungültige/Stimmen (Gesamt)_|_Gemeinde A Stimmen_(Gesamt, Fortsetzung)_|
 * ...
 * ____________________________________________________________________________________________________________________________
 * | SEITE 1/3, TEIL 8/10           | SEITE 2/3, TEIL 8/10                          | SEITE 3/3, TEIL 8/10                     |
 * |_Gemeinde X Kopfzahlen (Urne)___|_Gemeinde X Gültige/Ungültige/Stimmen (Urne)___|_Gemeinde X Stimmen_(Urne, Fortsetzung)___|
 * ____________________________________________________________________________________________________________________________
 * | SEITE 1/3, TEIL 9/10           | SEITE 2/3, TEIL 9/10                          | SEITE 3/3, TEIL 9/10                     |
 * |_Gemeinde X Kopfzahlen (Brief)__|_Gemeinde X Gültige/Ungültige/Stimmen (Brief)__|_Gemeinde X Stimmen_(Brief, Fortsetzung)__|
 * ____________________________________________________________________________________________________________________________
 * | SEITE 1/3, TEIL 10/10          | SEITE 2/3, TEIL 10/10                         | SEITE 3/3, TEIL 10/10                    |
 * |_Gemeinde X Kopfzahlen (Gesamt)_|_Gemeinde X Gültige/Ungültige/Stimmen (Gesamt)_|_Gemeinde X Stimmen_(Gesamt, Fortsetzung)_|
 *
 * </pre>
 *
 * <ul>
 * <li>TEILE
 * <ul>
 * <li>nummerieren die Zeilen in der oberen Matrix</li>
 * <li>passen die Zeilen einer Tabelle per Stimmenart nicht auf eine ODT-Seite, entsteht ein neuer TEIL</li>
 * </ul>
 * </li>
 * <li>SEITEN
 * <ul>
 * <li>nummerieren die Spalten in der oberen Matrix</li>
 * <li>passen die Spalten einer Tabelle per Stimmenart nicht auf eine ODT-Seite, entsteht eine neue SEITE</li>
 * </ul>
 * </li>
 * </ul>
 *
 * @see ReferendumErgebnisGesamtergebnisTabellarischDocumentExportMapper!!!
 * @see AbstractGruppenErgebnistabelleExportMapper !!!
 */
public abstract class AbstractErgebnisGesamtergebnisTabellarischDocumentExportMapper
    extends
      AbstractErgebnisDocumentExportMapper<ErgebnisGesamtergebnisTabellarischDocumentExportContext, Wahldurchfuehrungsgebiet, WahlverwaltungGebietInformation, ErgebnisGesamtergebnisTabellarischDocument>
    implements
      ErgebnisGesamtergebnisTabellarischDocumentExportMapper,
      ErgebnisGesamtergebnisExchange {

  @Inject
  private transient ErgebnisZusammenstellungDokumentFactory ergebnisZusammenstellungDokumentFactory;

  @Inject
  private transient ErgebnisGesamtergebnisTabellarischDocumentCreator documentCreator;

  @Inject
  private transient ErgebnisGesamtergebnisTabellarischTabelleSortierung ergebnisGesamtergebnisTabellarischTabelleSortierung;

  protected AbstractErgebnisGesamtergebnisTabellarischDocumentExportMapper(String erfassungsgebietGebietsreferenzFormat) {
    super(erfassungsgebietGebietsreferenzFormat);
  }

  @Override
  protected List<Wahldurchfuehrungsgebiet> collectAuswertungseinheiten(ErgebnisGesamtergebnisTabellarischDocumentExportContext context, ErgebnisDaten ergebnisDaten) {
    return collectAuswertungsgebieteOderWurzelgebiet(context, ergebnisDaten);
  }

  @Override
  protected Function<ErgebnisGesamtergebnisTabellarischDocument, File> getTemplateFileByDocumentFinder(ErgebnisGesamtergebnisTabellarischDocumentExportContext context, ErgebnisDaten ergebnisDaten) {
    Map<ErgebnisGesamtergebnisTabellarischDocumentDokumentVorlageArt, File> templateFilePerDokumentVorlageArt = collectTemplateFilePerDokumentVorlageArt(context);

    return document -> {
      ErgebnisGesamtergebnisTabellarischDocumentDokumentVorlageArt dokumentVorlageArt = document.dokumentVorlageArt;

      if (templateFilePerDokumentVorlageArt.containsKey(dokumentVorlageArt)) {
        return templateFilePerDokumentVorlageArt.get(dokumentVorlageArt);
      }

      throw new RuntimeException(
          "Kein Template zum Generieren eines ODT-Dokumentes gefunden [seite = " + document.seitenNummer + ", teil = " + document.teilNummer + ", dokumentVorlageArt = " + dokumentVorlageArt + "]!");
    };
  }

  private Map<ErgebnisGesamtergebnisTabellarischDocumentDokumentVorlageArt, File> collectTemplateFilePerDokumentVorlageArt(ErgebnisGesamtergebnisTabellarischDocumentExportContext context) {
    Map<ErgebnisGesamtergebnisTabellarischDocumentDokumentVorlageArt, File> result = new HashMap<>();

    if (context.hasDokumentVorlageGesamtergebnis()) {
      result.put(ErgebnisGesamtergebnisTabellarischDocumentDokumentVorlageArt.GESAMTERGEBNIS, getTemplateFile(context.getDokumentVorlageGesamtergebnis()));
    }

    if (context.hasDokumentVorlageErsteSeiteStimmzettel()) {
      result.put(ErgebnisGesamtergebnisTabellarischDocumentDokumentVorlageArt.STIMMZETTEL_ERSTE_SEITE_ANDERS, getTemplateFile(context.getDokumentVorlageErsteSeiteStimmzettel()));
    }

    if (context.hasDokumentVorlageWeitereSeitenStimmzettel()) {
      result.put(ErgebnisGesamtergebnisTabellarischDocumentDokumentVorlageArt.STIMMZETTEL, getTemplateFile(context.getDokumentVorlageWeitereSeitenStimmzettel()));
    } else if (context.hasDokumentVorlageErsteSeiteStimmzettel()) {
      result.put(ErgebnisGesamtergebnisTabellarischDocumentDokumentVorlageArt.STIMMZETTEL, getTemplateFile(context.getDokumentVorlageErsteSeiteStimmzettel()));
    }

    if (context.hasDokumentVorlageErsteSeiteDirektstimmen()) {
      result.put(ErgebnisGesamtergebnisTabellarischDocumentDokumentVorlageArt.DIREKTSTIMMEN_ERSTE_SEITE_ANDERS, getTemplateFile(context.getDokumentVorlageErsteSeiteDirektstimmen()));
    }

    if (context.hasDokumentVorlageWeitereSeitenDirektstimmen()) {
      result.put(ErgebnisGesamtergebnisTabellarischDocumentDokumentVorlageArt.DIREKTSTIMMEN, getTemplateFile(context.getDokumentVorlageWeitereSeitenDirektstimmen()));
    } else if (context.hasDokumentVorlageErsteSeiteDirektstimmen()) {
      result.put(ErgebnisGesamtergebnisTabellarischDocumentDokumentVorlageArt.DIREKTSTIMMEN, getTemplateFile(context.getDokumentVorlageErsteSeiteDirektstimmen()));
    }

    if (context.hasDokumentVorlageErsteSeiteListenstimmen()) {
      result.put(ErgebnisGesamtergebnisTabellarischDocumentDokumentVorlageArt.LISTENSTIMMEN_ERSTE_SEITE_ANDERS, getTemplateFile(context.getDokumentVorlageErsteSeiteListenstimmen()));
    }

    if (context.hasDokumentVorlageWeitereSeitenListenstimmen()) {
      result.put(ErgebnisGesamtergebnisTabellarischDocumentDokumentVorlageArt.LISTENSTIMMEN, getTemplateFile(context.getDokumentVorlageWeitereSeitenListenstimmen()));
    } else if (context.hasDokumentVorlageErsteSeiteListenstimmen()) {
      result.put(ErgebnisGesamtergebnisTabellarischDocumentDokumentVorlageArt.LISTENSTIMMEN, getTemplateFile(context.getDokumentVorlageErsteSeiteListenstimmen()));
    }

    return result;
  }

  @Override // ELECTKOM-409, ELECTKOM-481
  protected LinkedHashMap<WahlverwaltungGebietInformation, List<ErgebnisGesamtergebnisTabellarischDocument>> createDocuments(ErgebnisGesamtergebnisTabellarischDocumentExportContext context,
      ErgebnisDaten ergebnisDaten, List<Wahldurchfuehrungsgebiet> auswertungsgebiete) {
    return createDocumentsByErgebnisZusammenstellung(context, ergebnisDaten, auswertungsgebiete);
  }

  // ELECT-14601
  protected LinkedHashMap<WahlverwaltungGebietInformation, List<ErgebnisGesamtergebnisTabellarischDocument>> createDocumentsByErgebnisZusammenstellung(
      ErgebnisGesamtergebnisTabellarischDocumentExportContext context, ErgebnisDaten ergebnisDaten, List<Wahldurchfuehrungsgebiet> auswertungsgebiete) {
    LinkedHashMap<WahlverwaltungGebietInformation, List<ErgebnisGesamtergebnisTabellarischDocument>> result = new LinkedHashMap<>();

    ErgebnisGesamtergebnisTabellarisch ergebnisGesamtergebnisTabellarisch = new ErgebnisGesamtergebnisTabellarisch(
        ergebnisZusammenstellungDokumentFactory.createByAuswertungsgebiete(ergebnisDaten, auswertungsgebiete));

    List<WahlverwaltungGebietInformation> stimmzettelgebietInformationen = ergebnisGesamtergebnisTabellarisch.collectStimmzettelgebiete();

    boolean documentsCreatedPerStimmzettelgebiet = context.isDocumentsCreatedPerStimmzettelgebiet();
    boolean documentsCreatedPerStimmzettelgebietKompakt = context.isDocumentsCreatedPerStimmzettelgebietKompakt();

    boolean documentsCreatedPerGemeinde = context.isDocumentsCreatedPerGemeinde();

    ErgebnisdokumentTypExportFiles exportFiles = new ErgebnisdokumentTypExportFiles(auswertungsgebiete, stimmzettelgebietInformationen, ergebnisGesamtergebnisTabellarisch::collectGemeinden);

    for (WahlverwaltungGebietInformation stimmzettelgebietInformation : stimmzettelgebietInformationen) {
      List<GemeindeTabelle> gemeindeTabellen = ergebnisGesamtergebnisTabellarisch.collectGemeindeTabellen(stimmzettelgebietInformation);

      if (documentsCreatedPerStimmzettelgebiet && (!documentsCreatedPerGemeinde || exportFiles.isStimmzettelgebietExported(stimmzettelgebietInformation))) {
        info("\tErzeuge Dokumente für Stimmzettelgebiet [art = " + stimmzettelgebietInformation.getArt() + "/" + stimmzettelgebietInformation.getArtIndex() + ", nummer = "
            + stimmzettelgebietInformation.getNummer() + ", name = " + stimmzettelgebietInformation.getName() + ", gebiet in gebietsbaum = " + stimmzettelgebietInformation.getGebiet().isPresent()
            + "]");

        List<ErgebnisGesamtergebnisTabellarischDocument> stimmzettelgebietOdtSeiten = new ArrayList<>();

        if (documentsCreatedPerStimmzettelgebietKompakt) { // ELECT-16479
          List<StimmzettelgebietTabelle> stimmzettelgebietTabellen = ergebnisGesamtergebnisTabellarisch.collectStimmzettelgebietTabellen(stimmzettelgebietInformation);

          stimmzettelgebietOdtSeiten.addAll(createOdtSeitenStimmzettelgebiet(context, ergebnisDaten, stimmzettelgebietInformation, stimmzettelgebietTabellen));
        } else {
          stimmzettelgebietOdtSeiten.addAll(createOdtSeitenGemeinden(context, ergebnisDaten, stimmzettelgebietInformation, gemeindeTabellen));
        }

        WahlverwaltungGebietInformation stimmzettelgebietOdtKey = exportFiles.createDocumentKeyStimmzettelgebiet(stimmzettelgebietInformation);
        result.put(stimmzettelgebietOdtKey, postProcessOdtSeiten(context, stimmzettelgebietOdtSeiten));
      }

      if (documentsCreatedPerGemeinde) {
        for (GemeindeTabelle tabelle : gemeindeTabellen) {
          WahlverwaltungGemeindeInformation gemeindeInformation = tabelle.getGemeinde();

          if (exportFiles.isGemeindeExported(gemeindeInformation)) {
            info("\tErzeuge Dokumente für Gemeinde [art = " + gemeindeInformation.getArt() + "/" + gemeindeInformation.getArtIndex() + ", nummer = " + gemeindeInformation.getNummer() + ", name = "
                + gemeindeInformation.getName() + ", gebiet in gebietsbaum = " + gemeindeInformation.getGebiet().isPresent() + "]");

            List<ErgebnisGesamtergebnisTabellarischDocument> gemeindeOdtSeiten = createOdtSeitenGemeinden(context, ergebnisDaten, stimmzettelgebietInformation, singletonList(tabelle));

            WahlverwaltungGebietInformation gemeindeOdtKey = exportFiles.createDocumentKeyGemeinde(stimmzettelgebietInformation, gemeindeInformation);
            result.put(gemeindeOdtKey, postProcessOdtSeiten(context, gemeindeOdtSeiten));
          }
        }
      }
    }

    boolean documentsCreatedPerAuswertungsgebiet = documentsCreatedPerStimmzettelgebiet && exportFiles.isAuswertungsgebieteExported(); // ELECT-13117, bspw. alle Wahlkreise eines einzelnen (im Gebietsbaum ausgewählten) Gebietes in einem ODT...

    if (documentsCreatedPerAuswertungsgebiet) {
      Wahldurchfuehrungsgebiet auswertungsgebiet = auswertungsgebiete.get(0);
      WahlverwaltungGebietInformation auswertungsgebietInformation = new WahlverwaltungGebietInformation(auswertungsgebiet);

      WahlverwaltungGebietInformation auswertungsgebietOdtKey = exportFiles.createDocumentKeyAuswertungsgebiet(auswertungsgebietInformation);

      if (!result.containsKey(auswertungsgebietOdtKey)) { // Vorhandenen Key (ergibt ODT-Dateiname) NICHT überschreiben!!!
        info("\tErzeuge Dokumente für Auswertungsgebiet [art = " + auswertungsgebietInformation.getArt() + "/" + auswertungsgebietInformation.getArtIndex() + ", nummer = "
            + auswertungsgebietInformation.getNummer() + ", name = " + auswertungsgebietInformation.getName() + ", gebiet in gebietsbaum = " + auswertungsgebietInformation.getGebiet().isPresent()
            + "]");

        List<ErgebnisGesamtergebnisTabellarischDocument> auswertungsgebietOdtSeiten = new ArrayList<>();

        for (WahlverwaltungGebietInformation stimmzettelgebietInformation : stimmzettelgebietInformationen) {
          List<ErgebnisGesamtergebnisTabellarischDocument> stimmzettelgebietOdtSeiten = new ArrayList<>();

          if (documentsCreatedPerStimmzettelgebietKompakt) { // ELECT-16479
            stimmzettelgebietOdtSeiten.addAll(createOdtSeitenStimmzettelgebiet(context, ergebnisDaten, stimmzettelgebietInformation,
                ergebnisGesamtergebnisTabellarisch.collectStimmzettelgebietTabellen(stimmzettelgebietInformation)));
          } else {
            stimmzettelgebietOdtSeiten
                .addAll(createOdtSeitenGemeinden(context, ergebnisDaten, stimmzettelgebietInformation, ergebnisGesamtergebnisTabellarisch.collectGemeindeTabellen(stimmzettelgebietInformation)));
          }

          auswertungsgebietOdtSeiten.addAll(postProcessOdtSeiten(context, stimmzettelgebietOdtSeiten)); // Hier sortieren, um nur Seiten/Teile desselben Stimmzettelgebietes zu sortieren!
        }

        result.put(auswertungsgebietOdtKey, auswertungsgebietOdtSeiten);
      }
    }

    return result;
  }

  private List<ErgebnisGesamtergebnisTabellarischDocument> createOdtSeitenStimmzettelgebiet(ErgebnisGesamtergebnisTabellarischDocumentExportContext context, ErgebnisDaten ergebnisDaten,
      WahlverwaltungGebietInformation stimmzettelgebietInformation, Collection<StimmzettelgebietTabelle> values) {
    Map<WahlverwaltungGebietInformation, ErgebnisZusammenstellungTabelle> tabellePerDatenGebietInformation = new TreeMap<>(ErgebnisTabellarischTabelle.GEBIET_INFORMATION_DEFAULT_COMPARATOR);

    for (StimmzettelgebietTabelle value : values) {
      WahlverwaltungGebietInformation datenGebietInformation = value.getStimmzettelgebiet();

      if (value.getGemeinden().size() == 1) { // ELECT-16557
        datenGebietInformation = value.getGemeinden().iterator().next();
      }

      tabellePerDatenGebietInformation.put(datenGebietInformation, value.getTabelle());
    }

    return createOdtSeiten(context, ergebnisDaten, stimmzettelgebietInformation, tabellePerDatenGebietInformation);
  }

  private List<ErgebnisGesamtergebnisTabellarischDocument> createOdtSeitenGemeinden(ErgebnisGesamtergebnisTabellarischDocumentExportContext context, ErgebnisDaten ergebnisDaten,
      WahlverwaltungGebietInformation stimmzettelgebietInformation, Collection<GemeindeTabelle> values) {
    Map<WahlverwaltungGebietInformation, ErgebnisZusammenstellungTabelle> tabellePerGemeindeInformation = new TreeMap<>(ErgebnisTabellarischTabelle.GEMEINDE_INFORMATION_DEFAULT_COMPARATOR);

    for (GemeindeTabelle value : values) {
      tabellePerGemeindeInformation.put(value.getGemeinde(), value.getTabelle());
    }

    return createOdtSeiten(context, ergebnisDaten, stimmzettelgebietInformation, tabellePerGemeindeInformation);
  }

  private List<ErgebnisGesamtergebnisTabellarischDocument> createOdtSeiten(ErgebnisGesamtergebnisTabellarischDocumentExportContext context, ErgebnisDaten ergebnisDaten,
      WahlverwaltungGebietInformation stimmzettelgebietInformation, Map<WahlverwaltungGebietInformation, ErgebnisZusammenstellungTabelle> tabellePerDatenGebietInformation) {
    ErgebnisTabellarischContext documentContext = new ErgebnisTabellarischContext( //
        getErfassungsgebietGebietsreferenzFormat(), //

        context.isTabellenCreatedPerDatenArt(), //
        context.isTabellenCreatedGemeindenGesamt(), //

        ergebnisGesamtergebnisTabellarischTabelleSortierung, //

        true, // gesamtergebnis-v7-V7 Gesamtergebnis.odt enthält nur Kopfzahlen...

        context.getAnzahlZeilenProSeite(), //
        context.getAnzahlZeilenProSeite(), //

        context.getDokumentVorlageErsteSeiteAnzahlSpalten(), //
        context.getDokumentVorlageWeitereSeitenAnzahlSpalten(), //
        context.getDokumentVorlageWeitereSeitenAnzahlSpalten() // Kein Unterschied zwischen "weiterer" und "letzter" Seite...
    );

    return createErgebnisTabellarischDocument(context).createOdtSeiten(documentContext, ergebnisDaten, stimmzettelgebietInformation, tabellePerDatenGebietInformation);
  }

  private List<ErgebnisGesamtergebnisTabellarischDocument> postProcessOdtSeiten(ErgebnisGesamtergebnisTabellarischDocumentExportContext context,
      List<ErgebnisGesamtergebnisTabellarischDocument> odtSeiten) {
    List<ErgebnisGesamtergebnisTabellarischDocument> result = createErgebnisTabellarischDocument(context).postProcessOdtSeiten(odtSeiten);

    Collection<ErgebnisGesamtergebnisTabellarischDocumentDokumentVorlageArt> dokumentVorlageArtenVorhanden = collectTemplateFilePerDokumentVorlageArt(context).keySet();

    for (ErgebnisGesamtergebnisTabellarischDocument odtSeite : odtSeiten) {
      ErgebnisGesamtergebnisTabellarischDocumentDokumentVorlageArt dokumentVorlageArt = odtSeite.dokumentVorlageArt;

      if (!dokumentVorlageArtenVorhanden.contains(dokumentVorlageArt)) {
        if (ErgebnisGesamtergebnisTabellarischDocumentDokumentVorlageArt.STIMMZETTEL_ERSTE_SEITE_ANDERS.equals(dokumentVorlageArt)) {
          odtSeite.dokumentVorlageArt = ErgebnisGesamtergebnisTabellarischDocumentDokumentVorlageArt.STIMMZETTEL;
        }

        if (ErgebnisGesamtergebnisTabellarischDocumentDokumentVorlageArt.DIREKTSTIMMEN_ERSTE_SEITE_ANDERS.equals(dokumentVorlageArt)) {
          odtSeite.dokumentVorlageArt = ErgebnisGesamtergebnisTabellarischDocumentDokumentVorlageArt.DIREKTSTIMMEN;
        }

        if (ErgebnisGesamtergebnisTabellarischDocumentDokumentVorlageArt.LISTENSTIMMEN_ERSTE_SEITE_ANDERS.equals(dokumentVorlageArt)) {
          odtSeite.dokumentVorlageArt = ErgebnisGesamtergebnisTabellarischDocumentDokumentVorlageArt.LISTENSTIMMEN;
        }
      }
    }

    return result;
  }

  private ErgebnisTabellarischDocument<ErgebnisGesamtergebnisTabellarischDocument, ErgebnisGesamtergebnisTabellarischDocumentZeile> createErgebnisTabellarischDocument(
      ErgebnisGesamtergebnisTabellarischDocumentExportContext context) {
    Wahldurchfuehrung wahldurchfuehrung = context.getWahl();

    boolean wahlMitStimmzettel = context.hasDokumentVorlageStimmzettel();

    boolean wahlMitDirektstimmen = wahldurchfuehrung.isWahlMitDirektkandidatengebieten();
    boolean wahlMitListenstimmen = wahldurchfuehrung.isWahlMitListengebieten();

    int anzahlDokumentVorlagenVorhanden = collectTemplateFilePerDokumentVorlageArt(context).values().stream().filter(Objects::nonNull).collect(toSet()).size();

    // Bspw. gesamtergebnis-v7-V7 Gesamtergebnis.odt
    BiFunction<ErgebnisTabellarischContext, ErgebnisTabellarischTabelle, List<ErgebnisTabellarischAufteilungBlatt>> blaetterMapper = new ErgebnisTabellarischAufteilung()::collectBlaetterNurKopfzahlen;

    if (anzahlDokumentVorlagenVorhanden == 2) {
      // Bspw. gesamtergebnis-v7-V7 Gesamtergebnis.odt
      //       gesamtergebnis-v7-V7 Gesamtergebnis (Stimmen).odt
      if (wahlMitDirektstimmen) {
        blaetterMapper = new ErgebnisTabellarischAufteilung()::collectBlaetterNurDirektstimmen;
      } else if (wahlMitListenstimmen) {
        blaetterMapper = new ErgebnisTabellarischAufteilung()::collectBlaetterNurListenstimmen;
      }
    } else if (anzahlDokumentVorlagenVorhanden == 3) {
      // Bspw. gesamtergebnis-v7-V7 Gesamtergebnis.odt
      //       gesamtergebnis-v7-V7 Gesamtergebnis (Stimmen).odt
      //       gesamtergebnis-v7-V7 Gesamtergebnis (Stimmzettel).odt
      // Bspw. gesamtergebnis-v7-V7 Gesamtergebnis.odt
      //       gesamtergebnis-v7-V7 Gesamtergebnis (Stimmen, Erste Seite).odt, gesamtergebnis-v7-V7 Gesamtergebnis (Stimmen, Weitere Seiten).odt
      if (wahlMitDirektstimmen) {
        if (wahlMitStimmzettel) {
          blaetterMapper = new ErgebnisTabellarischAufteilung()::collectBlaetterNurDirektstimmenStimmzettel;
        } else {
          blaetterMapper = new ErgebnisTabellarischAufteilung()::collectBlaetterNurDirektstimmen;
        }
      } else if (wahlMitListenstimmen) {
        if (wahlMitStimmzettel) {
          blaetterMapper = new ErgebnisTabellarischAufteilung()::collectBlaetterNurListenstimmenStimmzettel;
        } else {
          blaetterMapper = new ErgebnisTabellarischAufteilung()::collectBlaetterNurListenstimmen;
        }
      }
    } else if (anzahlDokumentVorlagenVorhanden > 3) {
      // Bspw. gesamtergebnis-v7-V7 Gesamtergebnis.odt
      //       gesamtergebnis-v7-V7 Gesamtergebnis (Direktstimmen, Erste Seite).odt, gesamtergebnis-v7-V7 Gesamtergebnis (Direktstimmen, Weitere Seiten).odt
      //       gesamtergebnis-v7-V7 Gesamtergebnis (Listenstimmen, Erste Seite).odt, gesamtergebnis-v7-V7 Gesamtergebnis (Listenstimmen, Weitere Seiten).odt
      if (wahlMitDirektstimmen && wahlMitListenstimmen) {
        blaetterMapper = new ErgebnisTabellarischAufteilung()::collectBlaetterDirektstimmenListenstimmen;
      }
    }

    return new ErgebnisTabellarischDocument<>(createBaseElectContext(context, NoDokumentExtraFileOutput.INSTANCE), documentCreator, blaetterMapper);
  }

  /*
   *
   *
   *
   *
   *
   * ELECT-15337, LEGACY-Methoden -> NICHT mehr erweitern!!!
   *
   *
   *
   *
   *
   */

  @Inject
  private transient ErgebnisGesamtergebnisTabellarischDocumentZeileCreator documentZeileCreator;

  @Inject
  private transient ErgebnisGesamtergebnisTabellarischDocumentAufteiler documentAufteiler;

  protected LinkedHashMap<WahlverwaltungGebietInformation, List<ErgebnisGesamtergebnisTabellarischDocument>> createDocumentsLegacy(ErgebnisGesamtergebnisTabellarischDocumentExportContext context,
      ErgebnisDaten ergebnisDaten, List<Wahldurchfuehrungsgebiet> auswertungsgebiete) {
    return createDocuments(context, ergebnisDaten, auswertungsgebiete, context.isDocumentsCreatedPerStimmzettelgebiet(), context.isDocumentsCreatedPerGemeinde());
  }

  // Cf. ReferendumErgebnisGesamtergebnisTabellarischDocumentExportMapper#createDocuments!!!
  private LinkedHashMap<WahlverwaltungGebietInformation, List<ErgebnisGesamtergebnisTabellarischDocument>> createDocuments(ErgebnisGesamtergebnisTabellarischDocumentExportContext context,
      ErgebnisDaten ergebnisDaten, List<Wahldurchfuehrungsgebiet> auswertungsgebiete, boolean documentsCreatedPerStimmzettelgebiet, boolean documentsCreatedPerGemeinde) {
    LinkedHashMap<WahlverwaltungGebietInformation, List<ErgebnisGesamtergebnisTabellarischDocument>> result = new LinkedHashMap<>();

    Ergebnisart ergebnisart = ergebnisDaten.getErgebnis().getErgebnisart();

    List<ErgebnisGesamtergebnisTabellarischDocument> auswertungsgebietOdtSeiten = new ArrayList<>();
    Function<Gebiet, String> auswertungsgebietSuffixFct = this::getDateiSimpleName;

    SortedMap<WahlverwaltungGebietInformation, SortedMap<WahlverwaltungGebietInformation, List<ErgebnisGesamtergebnisTabellarischDocumentZeile>>> zeilenPerGemeindePerStimmzettelgebiet = collectErfassungsgebieteInAuswertungsgebietenMappedPerGemeindePerStimmzettelgebiet(
        context, ergebnisDaten, auswertungsgebiete);

    for (Entry<WahlverwaltungGebietInformation, SortedMap<WahlverwaltungGebietInformation, List<ErgebnisGesamtergebnisTabellarischDocumentZeile>>> entryPerStimmzettelgebiet : zeilenPerGemeindePerStimmzettelgebiet
        .entrySet()) {
      WahlverwaltungGebietInformation stimmzettelgebietInformation = entryPerStimmzettelgebiet.getKey();
      SortedMap<WahlverwaltungGebietInformation, List<ErgebnisGesamtergebnisTabellarischDocumentZeile>> zeilenPerGemeindeInStimmzettelgebiet = entryPerStimmzettelgebiet.getValue();

      info("\tErzeuge Dokumente für Stimmzettelgebiet [ergebnisart = " + ergebnisart + ", art = " + stimmzettelgebietInformation.getArt() + "/" + stimmzettelgebietInformation.getArtIndex()
          + ", nummer = " + stimmzettelgebietInformation.getNummer() + ", name = " + stimmzettelgebietInformation.getName() + ", gebiet in gebietsbaum = "
          + stimmzettelgebietInformation.getGebiet().isPresent() + ", anzahl gemeinden = " + zeilenPerGemeindeInStimmzettelgebiet.size() + ", odt-seiten erzeugt = pro"
          + (documentsCreatedPerStimmzettelgebiet ? " stimmzettelgebiet" : "") + (documentsCreatedPerGemeinde ? " gemeinde" : "") + "]");

      if (documentsCreatedPerStimmzettelgebiet) {
        WahlverwaltungGebietInformation stimmzettelgebietOdtKey = createDocumentKeyStimmzettelgebiet(stimmzettelgebietInformation, auswertungsgebiete, auswertungsgebietSuffixFct);
        List<ErgebnisGesamtergebnisTabellarischDocument> stimmzettelgebietOdtSeiten = createDocuments(context, stimmzettelgebietInformation, zeilenPerGemeindeInStimmzettelgebiet);

        result.put(stimmzettelgebietOdtKey, stimmzettelgebietOdtSeiten);

        auswertungsgebietOdtSeiten.addAll(stimmzettelgebietOdtSeiten.stream().map(ErgebnisGesamtergebnisTabellarischDocument::copy).collect(toList())); // ELECT-13178
      }

      if (documentsCreatedPerGemeinde) {
        for (Entry<WahlverwaltungGebietInformation, List<ErgebnisGesamtergebnisTabellarischDocumentZeile>> entryPerGemeinde : zeilenPerGemeindeInStimmzettelgebiet.entrySet()) {
          WahlverwaltungGebietInformation gemeindeInformation = entryPerGemeinde.getKey();

          info("\tErzeuge Dokumente für Gemeinde [ergebnisart = " + ergebnisart + ", art = " + gemeindeInformation.getArt() + "/" + gemeindeInformation.getArtIndex() + ", nummer = "
              + gemeindeInformation.getNummer() + ", name = " + gemeindeInformation.getName() + ", gebiet in gebietsbaum = " + gemeindeInformation.getGebiet().isPresent()
              + ", anzahl erfassungsgebiete = " + entryPerGemeinde.getValue().size() + ", odt-seiten erzeugt = pro" + (documentsCreatedPerStimmzettelgebiet ? " stimmzettelgebiet" : "")
              + (documentsCreatedPerGemeinde ? " gemeinde" : "") + "]");

          WahlverwaltungGebietInformation gemeindeOdtKey = createDocumentKeyGemeinde(gemeindeInformation, auswertungsgebiete, auswertungsgebietSuffixFct);
          List<ErgebnisGesamtergebnisTabellarischDocument> gemeindeOdtSeiten = createDocuments(context, stimmzettelgebietInformation, asSingletonSortedMap(entryPerGemeinde));

          result.put(gemeindeOdtKey, gemeindeOdtSeiten);
        }
      }
    }

    if (auswertungsgebiete.size() == 1 && zeilenPerGemeindePerStimmzettelgebiet.size() > 1 && !auswertungsgebietOdtSeiten.isEmpty()) { // ELECT-13117, bspw. alle AGH-Wahlkreise eines (im Gebietsbaum ausgewählten) Berliner Stadtbezirkes in einem ODT...
      Wahldurchfuehrungsgebiet auswertungsgebiet = auswertungsgebiete.get(0);
      WahlverwaltungGebietInformation auswertungsgebietInformation = new WahlverwaltungGebietInformation(auswertungsgebiet);

      WahlverwaltungGebietInformation auswertungsgebietOdtKey = createDocumentKeyAuswertungsgebiet(auswertungsgebietInformation, auswertungsgebiete, auswertungsgebietSuffixFct);

      if (!result.containsKey(auswertungsgebietOdtKey)) { // Vorhandenen Key (ergibt ODT-Dateiname) NICHT überschreiben!!!
        info("\tErzeuge Dokumente für Auswertungsgebiet [ergebnisart = " + ergebnisart + ", art = " + auswertungsgebietInformation.getArt() + "/" + auswertungsgebietInformation.getArtIndex()
            + ", nummer = " + auswertungsgebietInformation.getNummer() + ", name = " + auswertungsgebietInformation.getName() + ", gebiet in gebietsbaum = "
            + auswertungsgebietInformation.getGebiet().isPresent() + "]");

        result.put(auswertungsgebietOdtKey, auswertungsgebietOdtSeiten);
      }
    }

    return result;
  }

  // Cf. ReferendumErgebnisGesamtergebnisTabellarischDocumentExportMapper#collectErfassungsgebieteInAuswertungsgebietenMappedPerGemeindePerStimmzettelgebiet!!!
  private SortedMap<WahlverwaltungGebietInformation, SortedMap<WahlverwaltungGebietInformation, List<ErgebnisGesamtergebnisTabellarischDocumentZeile>>> collectErfassungsgebieteInAuswertungsgebietenMappedPerGemeindePerStimmzettelgebiet(
      ErgebnisGesamtergebnisTabellarischDocumentExportContext context, ErgebnisDaten ergebnisDaten, List<Wahldurchfuehrungsgebiet> auswertungsgebiete) {
    boolean erfassungsgebietErfasstInAnderemGebietIgnored = false; // ELECT-12557

    return collectErfassungsgebieteInAuswertungsgebietenMappedPerGemeindePerStimmzettelgebiet(ergebnisDaten, auswertungsgebiete, erfassungsgebietErfasstInAnderemGebietIgnored,
        (stimmzettelgebietInformation, gemeindeInformation, erfassungsgebietDescriptor) -> createDocumentZeile(context, ergebnisDaten, stimmzettelgebietInformation, gemeindeInformation,
            erfassungsgebietDescriptor));
  }

  private List<ErgebnisGesamtergebnisTabellarischDocument> createDocuments(ErgebnisGesamtergebnisTabellarischDocumentExportContext context,
      WahlverwaltungGebietInformation stimmzettelgebietInformation, SortedMap<WahlverwaltungGebietInformation, List<ErgebnisGesamtergebnisTabellarischDocumentZeile>> zeilenPerGemeinde) {
    return createDocuments(context, stimmzettelgebietInformation, zeilenPerGemeinde, context.isTabellenCreatedPerDatenArt(), context.isTabellenCreatedKreiseGesamt(),
        context.isTabellenCreatedGemeindenGesamt());
  }

  // Cf. ReferendumErgebnisGesamtergebnisTabellarischDocumentExportMapper#createDocuments!!!
  private List<ErgebnisGesamtergebnisTabellarischDocument> createDocuments(ErgebnisGesamtergebnisTabellarischDocumentExportContext context,
      WahlverwaltungGebietInformation stimmzettelgebietInformation, SortedMap<WahlverwaltungGebietInformation, List<ErgebnisGesamtergebnisTabellarischDocumentZeile>> zeilenPerGemeinde,
      boolean tabellenCreatedPerDatenArt, boolean tabellenCreatedKreiseGesamt, boolean tabellenCreatedGemeindenGesamt) {
    List<ErgebnisGesamtergebnisTabellarischDocument> result = new ArrayList<>();

    for (Entry<WahlverwaltungGebietInformation, List<ErgebnisGesamtergebnisTabellarischDocumentZeile>> entryPerGemeinde : zeilenPerGemeinde.entrySet()) {
      WahlverwaltungGebietInformation gemeindeInformation = entryPerGemeinde.getKey();
      List<ErgebnisGesamtergebnisTabellarischDocumentZeile> zeilenInGemeinde = entryPerGemeinde.getValue();

      info("\t\tErzeuge Tabellen für Gemeinde [art = " + gemeindeInformation.getArt() + "/" + gemeindeInformation.getArtIndex() + ", nummer = " + gemeindeInformation.getNummer() + ", name = "
          + gemeindeInformation.getName() + ", gebiet in gebietsbaum = " + gemeindeInformation.getGebiet().isPresent() + ", anzahl zeilen = " + zeilenInGemeinde.size() + ", tabellen = "
          + (tabellenCreatedPerDatenArt ? "per stimmabgabeart" : "kompakt") + ", summentabellen =" + (tabellenCreatedKreiseGesamt ? " kreise" : "")
          + (tabellenCreatedGemeindenGesamt ? " gemeinden" : "") + "]");

      // Debugging...
      //      for (ErgebnisGesamtergebnisTabellarischDocumentZeile zeile : zeilenInGemeinde) {
      //        info("\t\t\t " + zeile);
      //      }

      result.addAll(createTabellen(context, tabellenCreatedPerDatenArt, stimmzettelgebietInformation, gemeindeInformation, zeilenInGemeinde));
    }

    // NOTE, dass, falls mehr als eine Gemeinde existiert, die Werte aller Gemeinden/Stadtbezirke nochmals zu Pseudo-Gemeinden aggregiert werden
    // und Tabellen für diese Pseudo-Gemeinden zusätzlich ins ODT-Dokument geschrieben werden!
    if (zeilenPerGemeinde.size() > 1) {
      List<ErgebnisGesamtergebnisTabellarischDocumentZeile> zeilenInStimmzettelgebiet = zeilenPerGemeinde.values().stream().flatMap(List::stream).collect(toList());

      if (tabellenCreatedKreiseGesamt) { // ELECTKOM-1697
        SortedMap<WahlverwaltungGebietInformation, List<ErgebnisGesamtergebnisTabellarischDocumentZeile>> zeilenInStimmzettelgebietPerKreis = new TreeMap<>(GEBIET_INFORMATION_DEFAULT_COMPARATOR);

        for (ErgebnisGesamtergebnisTabellarischDocumentZeile zeile : zeilenInStimmzettelgebiet) {
          ErgebnisDocumentGebietDocument kreis = zeile.landkreis;
          WahlverwaltungGebietInformation kreisInformation = new WahlverwaltungGebietInformation(Verwaltungsart.fromString(kreis.art), kreis.name, kreis.nummer, kreis.kuerzel);

          if (!kreisInformation.isUnbekannt() && !isBlank(kreisInformation.getName())) {
            zeilenInStimmzettelgebietPerKreis.computeIfAbsent(kreisInformation, ignored -> new ArrayList<>()).add(zeile);
          }
        }

        if (zeilenInStimmzettelgebietPerKreis.size() > 1 || !zeilenInStimmzettelgebietPerKreis.containsKey(stimmzettelgebietInformation)) {
          for (Entry<WahlverwaltungGebietInformation, List<ErgebnisGesamtergebnisTabellarischDocumentZeile>> entryPerKreis : zeilenInStimmzettelgebietPerKreis.entrySet()) {
            WahlverwaltungGebietInformation kreisInformation = entryPerKreis.getKey();

            WahlverwaltungGebietInformation summenGebietKreisGesamt = ErgebnisDocumentDatenGebietDocument.createSummenGebietKreisGesamt(kreisInformation);
            List<ErgebnisGesamtergebnisTabellarischDocumentZeile> zeilenInSummenGebietKreisGesamt = entryPerKreis.getValue();

            info("\t\tErzeuge Summentabellen (Pseudo-Gemeinde \"Gesamt (Kreis)\") [art = Pseudo-Gemeinde \"Gesamt (Kreis)\", nummer = " + summenGebietKreisGesamt.getNummer() + ", name = "
                + summenGebietKreisGesamt.getName() + ", tabellen = " + (tabellenCreatedPerDatenArt ? "per stimmabgabeart" : "kompakt") + "]");

            result.addAll(
                createSummenTabellen(context, tabellenCreatedPerDatenArt, stimmzettelgebietInformation, summenGebietKreisGesamt, stimmzettelgebietInformation, zeilenInSummenGebietKreisGesamt));
          }
        }
      }

      if (tabellenCreatedGemeindenGesamt) {
        WahlverwaltungGebietInformation summenGebietGemeindenGesamt = ErgebnisDocumentDatenGebietDocument.createSummenGebietGemeindenGesamt();
        List<ErgebnisGesamtergebnisTabellarischDocumentZeile> zeilenInSummenGebietGemeindenGesamt = zeilenInStimmzettelgebiet;

        info("\t\tErzeuge Summentabellen (Pseudo-Gemeinde \"Gesamt\") [art = Pseudo-Gemeinde \"Gesamt\", nummer = " + summenGebietGemeindenGesamt.getNummer() + ", name = "
            + summenGebietGemeindenGesamt.getName() + ", tabellen = " + (tabellenCreatedPerDatenArt ? "per stimmabgabeart" : "kompakt") + "]");

        result.addAll(
            createSummenTabellen(context, tabellenCreatedPerDatenArt, stimmzettelgebietInformation, summenGebietGemeindenGesamt, stimmzettelgebietInformation, zeilenInSummenGebietGemeindenGesamt));
      }
    }

    postProcessSeitenUndTeile(result);

    return result;
  }

  protected void postProcessSeitenUndTeile(List<ErgebnisGesamtergebnisTabellarischDocument> documents) {
    documentAufteiler.postProcessSeitenUndTeile(documents);
  }

  protected ErgebnisGesamtergebnisTabellarischDocumentZeile createDocumentZeile(ErgebnisGesamtergebnisTabellarischDocumentExportContext context, ErgebnisDaten ergebnisDaten,
      WahlverwaltungGebietInformation stimmzettelgebietInformation, WahlverwaltungGebietInformation gemeindeInformation, WahlverwaltungGebietDescriptor erfassungsgebietDescriptor) {
    ErgebnisGesamtergebnisTabellarischDocumentZeile result = documentZeileCreator.createZeile(ergebnisDaten, getErfassungsgebietGebietsreferenzFormat(), stimmzettelgebietInformation,
        gemeindeInformation, erfassungsgebietDescriptor);

    if (!result.isDatenArtZwischensumme() && result.wahlbezirk.isSonderstimmbezirk) {
      result.name = addErfassungsgebietGebietsreferenzSuffixSonderstimmbezirk(context, result.name);

      result.wahlbezirk.nummerFormatiert = addErfassungsgebietGebietsreferenzSuffixSonderstimmbezirk(context, defaultIfBlank(result.wahlbezirk.nummerFormatiert, result.wahlbezirk.nummer));

      result.wahlbezirkGebietsreferenz10Stellig = addErfassungsgebietGebietsreferenzSuffixSonderstimmbezirk(context, result.wahlbezirkGebietsreferenz10Stellig);
      result.wahlbezirkGebietsreferenz10StelligFormatiert = addErfassungsgebietGebietsreferenzSuffixSonderstimmbezirk(context, result.wahlbezirkGebietsreferenz10StelligFormatiert);
      result.wahlbezirkGebietsreferenz13Stellig = addErfassungsgebietGebietsreferenzSuffixSonderstimmbezirk(context, result.wahlbezirkGebietsreferenz13Stellig);
      result.wahlbezirkGebietsreferenz13StelligFormatiert = addErfassungsgebietGebietsreferenzSuffixSonderstimmbezirk(context, result.wahlbezirkGebietsreferenz13StelligFormatiert);
    }

    return result;
  }

  protected List<ErgebnisGesamtergebnisTabellarischDocument> createTabellen(ErgebnisGesamtergebnisTabellarischDocumentExportContext context, boolean tabellenCreatedPerDatenArt,
      WahlverwaltungGebietInformation stimmzettelgebietInformation, WahlverwaltungGebietInformation datenGebiet, List<ErgebnisGesamtergebnisTabellarischDocumentZeile> zeilenInDatenGebiet) {
    List<ErgebnisGesamtergebnisTabellarischDocument> result = new ArrayList<>();

    BaseElectContext baseElectContext = createBaseElectContext(context, NoDokumentExtraFileOutput.INSTANCE);
    Ergebnisart ergebnisart = context.getErgebnisart();

    if (tabellenCreatedPerDatenArt) {
      result
          .addAll(teileAufSeitenUndTeile(documentCreator.createDocumentFuerDatenArtUrnenwahl(baseElectContext, ergebnisart, stimmzettelgebietInformation, datenGebiet, zeilenInDatenGebiet), context));
      result
          .addAll(teileAufSeitenUndTeile(documentCreator.createDocumentFuerDatenArtBriefwahl(baseElectContext, ergebnisart, stimmzettelgebietInformation, datenGebiet, zeilenInDatenGebiet), context));
      result.addAll(teileSummenAufSeitenAuf(documentCreator.createDocumentFuerDatenArtGesamt(baseElectContext, ergebnisart, stimmzettelgebietInformation, datenGebiet, zeilenInDatenGebiet), context));
    } else {
      result.addAll(teileAufSeitenUndTeile(documentCreator.createDocumentAlleDatenArten(baseElectContext, ergebnisart, stimmzettelgebietInformation, datenGebiet, zeilenInDatenGebiet), context));
    }

    return result;
  }

  protected List<ErgebnisGesamtergebnisTabellarischDocument> createSummenTabellen(ErgebnisGesamtergebnisTabellarischDocumentExportContext context, boolean tabellenCreatedPerDatenArt,
      WahlverwaltungGebietInformation stimmzettelgebietInformation, WahlverwaltungGebietInformation summenGebiet, WahlverwaltungGebietInformation datenGebiet,
      List<ErgebnisGesamtergebnisTabellarischDocumentZeile> zeilenInDatenGebiet) {
    List<ErgebnisGesamtergebnisTabellarischDocument> result = new ArrayList<>();

    BaseElectContext baseElectContext = createBaseElectContext(context, NoDokumentExtraFileOutput.INSTANCE);
    Ergebnisart ergebnisart = context.getErgebnisart();

    if (tabellenCreatedPerDatenArt) {
      result.addAll(teileSummenAufSeitenAuf(
          documentCreator.createSummenDocumentFuerDatenArtUrnenwahl(baseElectContext, ergebnisart, stimmzettelgebietInformation, summenGebiet, datenGebiet, zeilenInDatenGebiet), context));
      result.addAll(teileSummenAufSeitenAuf(
          documentCreator.createSummenDocumentFuerDatenArtBriefwahl(baseElectContext, ergebnisart, stimmzettelgebietInformation, summenGebiet, datenGebiet, zeilenInDatenGebiet), context));
      result.addAll(teileSummenAufSeitenAuf(
          documentCreator.createSummenDocumentFuerDatenArtGesamt(baseElectContext, ergebnisart, stimmzettelgebietInformation, summenGebiet, datenGebiet, zeilenInDatenGebiet), context));
    } else {
      result.addAll(teileSummenAufSeitenAuf(
          documentCreator.createSummenDocumentAlleDatenArten(baseElectContext, ergebnisart, stimmzettelgebietInformation, summenGebiet, datenGebiet, zeilenInDatenGebiet), context));
    }

    return result;
  }

  private List<ErgebnisGesamtergebnisTabellarischDocument> teileAufSeitenUndTeile(ErgebnisGesamtergebnisTabellarischDocument document,
      ErgebnisGesamtergebnisTabellarischDocumentExportContext context) {
    return documentAufteiler.createDocumentCopiesHavingConfiguredAnzahlZeilenAndAnzahlSpalten(document, context.getAnzahlZeilenProSeite(), context);
  }

  // Tabellen für "Summen" enthalten nur eine Summenzeile (und ggf. Urnen-/Briefwahl-Zwischensummen als Inhaltszeilen bei der kompakten Darstellung), daher nur Aufteilung nach Seiten!
  private List<ErgebnisGesamtergebnisTabellarischDocument> teileSummenAufSeitenAuf(ErgebnisGesamtergebnisTabellarischDocument document,
      ErgebnisGesamtergebnisTabellarischDocumentExportContext context) {
    List<ErgebnisGesamtergebnisTabellarischDocument> result = documentAufteiler.createDocumentCopiesHavingConfiguredAnzahlSpalten(document, context);

    for (ErgebnisGesamtergebnisTabellarischDocument teil : result) {
      teil.zeilen.removeIf(zeile -> !zeile.isDatenArtZwischensumme()); // ELECTKOM-416
    }

    return result;
  }
}
