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

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

import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
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.kandidatur.ErgebnisKandidaturenTabellarischDocumentExportContext;
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.Wahldurchfuehrungsgebiet;
import de.ivu.elect.business.gruppe.entity.PolitischeGruppe;
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.AbstractErgebnisTabellarischDocument;
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.kandidatur.ErgebnisKandidaturenExchange;
import de.ivu.elect.was.exchange.ergebnis.kandidatur.ErgebnisKandidaturenTabellarischDocumentExportMapper;
import de.ivu.elect.was.exchange.ergebnis.zusammenstellung.ErgebnisZusammenstellung.GemeindeGruppeTabellen;
import de.ivu.elect.was.exchange.ergebnis.zusammenstellung.ErgebnisZusammenstellung.GemeindeTabelle;
import de.ivu.elect.was.exchange.ergebnis.zusammenstellung.ErgebnisZusammenstellung.GruppeGemeindeTabellen;
import de.ivu.elect.was.exchange.ergebnis.zusammenstellung.ErgebnisZusammenstellung.GruppeStimmzettelgebietTabelle;
import de.ivu.elect.was.exchange.ergebnis.zusammenstellung.ErgebnisZusammenstellung.GruppeTabelle;
import de.ivu.elect.was.exchange.ergebnis.zusammenstellung.ErgebnisZusammenstellungDokumentFactory;
import de.ivu.elect.was.exchange.ergebnis.zusammenstellung.ErgebnisZusammenstellungGruppe;
import de.ivu.elect.was.exchange.ergebnis.zusammenstellung.ErgebnisZusammenstellungTabelle;

/**
 * Die Hinweise zur Benennung von TEILEN und SEITEN bei AbstractErgebnisGesamtergebnisTabellarischDocumentExportMapper gelten entsprechend.
 */
public abstract class AbstractErgebnisKandidaturenTabellarischDocumentExportMapper<E, R>
    extends
      AbstractErgebnisDocumentExportMapper<ErgebnisKandidaturenTabellarischDocumentExportContext, E, R, ErgebnisKandidaturenTabellarischDocument>
    implements
      ErgebnisKandidaturenTabellarischDocumentExportMapper,
      ErgebnisKandidaturenExchange {

  @Inject
  private transient ErgebnisZusammenstellungDokumentFactory ergebnisZusammenstellungDokumentFactory;

  @Inject
  private transient ErgebnisKandidaturenTabellarischDocumentCreator documentCreator;

  @Inject
  private transient ErgebnisKandidaturenTabellarischTabelleSortierung ergebnisKandidaturenTabellarischTabelleSortierung;

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

  @Override
  protected Function<ErgebnisKandidaturenTabellarischDocument, File> getTemplateFileByDocumentFinder(ErgebnisKandidaturenTabellarischDocumentExportContext context, ErgebnisDaten ergebnisDaten) {
    final File templateFileErsteSeite = getTemplateFile(context.getDokumentVorlageErsteSeite());
    final File templateFileWeitereSeiten = getTemplateFile(context.getDokumentVorlageWeitereSeiten());

    return document -> document.hasSeitenNummer(ErgebnisKandidaturenTabellarischDocument.SEITE_NUMMER_ERSTE_SEITE) ? templateFileErsteSeite : templateFileWeitereSeiten;
  }

  // ELECT-15337
  protected LinkedHashMap<WahlverwaltungGebietInformation, List<ErgebnisKandidaturenTabellarischDocument>> createDocumentsByErgebnisZusammenstellung(
      ErgebnisKandidaturenTabellarischDocumentExportContext context, ErgebnisDaten ergebnisDaten, List<Wahldurchfuehrungsgebiet> auswertungsgebiete) {
    LinkedHashMap<WahlverwaltungGebietInformation, List<ErgebnisKandidaturenTabellarischDocument>> result = new LinkedHashMap<>();

    ErgebnisKandidaturenTabellarisch ergebnisKandidaturenTabellarisch = new ErgebnisKandidaturenTabellarisch(
        ergebnisZusammenstellungDokumentFactory.createByAuswertungsgebiete(ergebnisDaten, auswertungsgebiete));

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

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

    boolean documentsCreatedPerGemeinde = context.isDocumentsCreatedPerGemeinde();

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

    for (WahlverwaltungGebietInformation stimmzettelgebietInformation : stimmzettelgebietInformationen) {
      SortedMap<WahlverwaltungGemeindeInformation, GemeindeGruppeTabellen> tabellenPerGemeinde = ergebnisKandidaturenTabellarisch.collectTabellenPerGemeinde(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<ErgebnisKandidaturenTabellarischDocument> stimmzettelgebietOdtSeiten = new ArrayList<>();

        if (documentsCreatedPerStimmzettelgebietKompakt) { // ELECT-16479
          SortedMap<ErgebnisZusammenstellungGruppe, GruppeStimmzettelgebietTabelle> tabellenPerGruppeStimmzettelgebiet = ergebnisKandidaturenTabellarisch
              .collectTabellenPerGruppeStimmzettelgebiet(stimmzettelgebietInformation);

          stimmzettelgebietOdtSeiten.addAll(createOdtSeitenPerGruppeStimmzettelgebiet(context, ergebnisDaten, stimmzettelgebietInformation, tabellenPerGruppeStimmzettelgebiet.values()));
        } else {
          SortedMap<ErgebnisZusammenstellungGruppe, GruppeGemeindeTabellen> tabellenPerGruppeGemeinden = ergebnisKandidaturenTabellarisch
              .collectTabellenPerGruppeGemeinden(stimmzettelgebietInformation);

          stimmzettelgebietOdtSeiten.addAll(createOdtSeitenPerGruppeGemeinden(context, ergebnisDaten, stimmzettelgebietInformation, tabellenPerGruppeGemeinden.values()));
        }

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

      if (documentsCreatedPerGemeinde) {
        for (Entry<WahlverwaltungGemeindeInformation, GemeindeGruppeTabellen> entryPerGemeinde : tabellenPerGemeinde.entrySet()) {
          WahlverwaltungGemeindeInformation gemeindeInformation = entryPerGemeinde.getKey();

          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<ErgebnisKandidaturenTabellarischDocument> gemeindeOdtSeiten = createOdtSeitenGemeinden(context, ergebnisDaten, stimmzettelgebietInformation, entryPerGemeinde.getValue());

            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<ErgebnisKandidaturenTabellarischDocument> auswertungsgebietOdtSeiten = new ArrayList<>();

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

          if (documentsCreatedPerStimmzettelgebietKompakt) { // ELECT-16479
            stimmzettelgebietOdtSeiten.addAll(createOdtSeitenPerGruppeStimmzettelgebiet(context, ergebnisDaten, stimmzettelgebietInformation,
                ergebnisKandidaturenTabellarisch.collectTabellenPerGruppeStimmzettelgebiet(stimmzettelgebietInformation).values()));
          } else {
            stimmzettelgebietOdtSeiten.addAll(createOdtSeitenPerGruppeGemeinden(context, ergebnisDaten, stimmzettelgebietInformation,
                ergebnisKandidaturenTabellarisch.collectTabellenPerGruppeGemeinden(stimmzettelgebietInformation).values()));
          }

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

        result.put(auswertungsgebietOdtKey, auswertungsgebietOdtSeiten);
      }
    }

    return result;
  }

  private List<ErgebnisKandidaturenTabellarischDocument> createOdtSeitenPerGruppeStimmzettelgebiet(ErgebnisKandidaturenTabellarischDocumentExportContext context, ErgebnisDaten ergebnisDaten,
      WahlverwaltungGebietInformation stimmzettelgebietInformation, Collection<GruppeStimmzettelgebietTabelle> values) {
    List<ErgebnisKandidaturenTabellarischDocument> result = new ArrayList<>();

    for (GruppeStimmzettelgebietTabelle value : values) {
      ErgebnisZusammenstellungGruppe gruppe = value.getGruppe();

      WahlverwaltungGebietInformation datenGebietInformation = stimmzettelgebietInformation;

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

      Map<WahlverwaltungGebietInformation, ErgebnisZusammenstellungTabelle> tabellePerDatenGebietInformation = new TreeMap<>(ErgebnisTabellarischTabelle.GEBIET_INFORMATION_DEFAULT_COMPARATOR);
      tabellePerDatenGebietInformation.put(datenGebietInformation, value.getTabelle().getTabelle());

      result.addAll(createOdtSeiten(context, ergebnisDaten, stimmzettelgebietInformation, gruppe, tabellePerDatenGebietInformation));
    }

    return result;
  }

  private List<ErgebnisKandidaturenTabellarischDocument> createOdtSeitenPerGruppeGemeinden(ErgebnisKandidaturenTabellarischDocumentExportContext context, ErgebnisDaten ergebnisDaten,
      WahlverwaltungGebietInformation stimmzettelgebietInformation, Collection<GruppeGemeindeTabellen> values) {
    List<ErgebnisKandidaturenTabellarischDocument> result = new ArrayList<>();

    for (GruppeGemeindeTabellen value : values) {
      ErgebnisZusammenstellungGruppe gruppe = value.getGruppe();

      SortedMap<WahlverwaltungGebietInformation, ErgebnisZusammenstellungTabelle> tabellePerGemeindeInformation = new TreeMap<>(ErgebnisTabellarischTabelle.GEMEINDE_INFORMATION_DEFAULT_COMPARATOR);

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

      result.addAll(createOdtSeiten(context, ergebnisDaten, stimmzettelgebietInformation, gruppe, tabellePerGemeindeInformation));
    }

    return result;
  }

  private List<ErgebnisKandidaturenTabellarischDocument> createOdtSeitenGemeinden(ErgebnisKandidaturenTabellarischDocumentExportContext context, ErgebnisDaten ergebnisDaten,
      WahlverwaltungGebietInformation stimmzettelgebietInformation, GemeindeGruppeTabellen value) {
    List<ErgebnisKandidaturenTabellarischDocument> result = new ArrayList<>();

    for (GruppeTabelle gruppeTabelle : value.getTabellen()) {
      ErgebnisZusammenstellungGruppe gruppe = gruppeTabelle.getGruppe();

      Map<WahlverwaltungGebietInformation, ErgebnisZusammenstellungTabelle> tabellePerGemeindeInformation = new TreeMap<>(ErgebnisTabellarischTabelle.GEMEINDE_INFORMATION_DEFAULT_COMPARATOR);
      tabellePerGemeindeInformation.put(value.getGemeinde(), gruppeTabelle.getTabelle());

      result.addAll(createOdtSeiten(context, ergebnisDaten, stimmzettelgebietInformation, gruppe, tabellePerGemeindeInformation));
    }

    return result;
  }

  private List<ErgebnisKandidaturenTabellarischDocument> createOdtSeiten(ErgebnisKandidaturenTabellarischDocumentExportContext context, ErgebnisDaten ergebnisDaten,
      WahlverwaltungGebietInformation stimmzettelgebietInformation, ErgebnisZusammenstellungGruppe gruppe,
      Map<WahlverwaltungGebietInformation, ErgebnisZusammenstellungTabelle> tabellePerDatenGebietInformation) {
    ErgebnisTabellarischContext documentContext = new ErgebnisTabellarischContext( //
        getErfassungsgebietGebietsreferenzFormat(), //

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

        ergebnisKandidaturenTabellarischTabelleSortierung, //

        false, // Erste Listenkandidaturen bereits auf dem ersten Blatt...

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

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

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

  private List<ErgebnisKandidaturenTabellarischDocument> postProcessOdtSeiten(ErgebnisKandidaturenTabellarischDocumentExportContext context, List<ErgebnisKandidaturenTabellarischDocument> odtSeiten) {
    return createErgebnisTabellarischDocument(context).postProcessOdtSeiten(odtSeiten, (one, another) -> {
      int compareByWahlvorschlag = AbstractErgebnisTabellarischDocument.compareNummerFormatted(one.parteiWahlkreisvorschlagNummer, another.parteiWahlkreisvorschlagNummer);

      if (compareByWahlvorschlag == 0) {
        return one.compareBySeitenNummerThenTeilNummer(another); // ELECT-16542
      }

      return compareByWahlvorschlag;
    });
  }

  private ErgebnisTabellarischDocument<ErgebnisKandidaturenTabellarischDocument, ErgebnisKandidaturenTabellarischDocumentZeile> createErgebnisTabellarischDocument(
      ErgebnisKandidaturenTabellarischDocumentExportContext context) {
    Wahldurchfuehrung wahldurchfuehrung = context.getWahl();
    boolean wahlMitListenstimmen = wahldurchfuehrung.isWahlMitListengebieten();

    BiFunction<ErgebnisTabellarischContext, ErgebnisTabellarischTabelle, List<ErgebnisTabellarischAufteilungBlatt>> blaetterMapper = new ErgebnisTabellarischAufteilung()::collectBlaetterNurKopfzahlen;

    if (wahlMitListenstimmen) {
      blaetterMapper = new ErgebnisTabellarischAufteilung()::collectBlaetterNurListenkandidaturstimmen;
    }

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

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

  @Inject
  private transient ErgebnisKandidaturenTabellarischDocumentZeileCreator documentZeileCreator;

  @Inject
  private transient ErgebnisKandidaturenTabellarischDocumentAufteiler documentAufteiler;

  protected List<ErgebnisKandidaturenTabellarischDocumentZeile> createDocumentZeilenFuerAlleErfassungsgebiete(ErgebnisKandidaturenTabellarischDocumentExportContext context,
      ErgebnisDaten ergebnisDaten) {
    return documentZeileCreator.createZeilenFuerAlleErfassungsgebiete(context, ergebnisDaten, getErfassungsgebietGebietsreferenzFormat(), WahlverwaltungGebietDescriptor::getStimmzettelgebiet,
        WahlverwaltungGebietDescriptor::getGemeinde);
  }

  protected ErgebnisKandidaturenTabellarischDocumentZeile createDocumentZeile(ErgebnisKandidaturenTabellarischDocumentExportContext context, ErgebnisDaten ergebnisDaten,
      WahlverwaltungGebietInformation stimmzettelgebietInformation, WahlverwaltungGebietInformation gemeindeInformation, WahlverwaltungGebietDescriptor erfassungsgebietDescriptor) {
    ErgebnisKandidaturenTabellarischDocumentZeile 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 void sortiereBlaetterNachGruppen(List<ErgebnisKandidaturenTabellarischDocument> blaetter) {
    Collections.sort(blaetter, (o1, o2) -> Long.compare(Long.valueOf(o1.parteiWahlkreisvorschlagNummer), Long.valueOf(o2.parteiWahlkreisvorschlagNummer)));
  }

  /**
   * @deprecated
   * @see AbstractErgebnisKandidaturenTabellarischDocumentExportMapper#createTabellen
   */
  @Deprecated
  protected List<ErgebnisKandidaturenTabellarischDocument> createSeitenUndTeileGruppeInGemeinde(ErgebnisKandidaturenTabellarischDocumentExportContext context, PolitischeGruppe politischeGruppe,
      ErgebnisDaten ergebnisDaten, WahlverwaltungGebietInformation stimmzettelgebietInformation, WahlverwaltungGebietInformation gemeindeInformation,
      List<ErgebnisKandidaturenTabellarischDocumentZeile> zeilenInGemeinde) {
    return createTabellen(context, ergebnisDaten, true, politischeGruppe, stimmzettelgebietInformation, gemeindeInformation, zeilenInGemeinde);
  }

  protected List<ErgebnisKandidaturenTabellarischDocument> createTabellen(ErgebnisKandidaturenTabellarischDocumentExportContext context, ErgebnisDaten ergebnisDaten,
      boolean tabellenCreatedPerDatenArt, PolitischeGruppe politischeGruppe, WahlverwaltungGebietInformation stimmzettelgebietInformation, WahlverwaltungGebietInformation datenGebiet,
      List<ErgebnisKandidaturenTabellarischDocumentZeile> zeilenInDatenGebiet) {
    List<ErgebnisKandidaturenTabellarischDocument> result = new ArrayList<>();

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

    List<ErgebnisKandidaturenTabellarischDocumentZeile> zeilenFuerGruppeInDatenGebiet = documentZeileCreator.createZeilenFuerGruppe(zeilenInDatenGebiet, ergebnisDaten, politischeGruppe);

    if (tabellenCreatedPerDatenArt) {
      result.addAll(teileAufSeitenUndTeile(context,
          documentCreator.createDocumentFuerDatenArtUrnenwahl(baseElectContext, ergebnisart, stimmzettelgebietInformation, datenGebiet, zeilenFuerGruppeInDatenGebiet)));
      result.addAll(teileAufSeitenUndTeile(context,
          documentCreator.createDocumentFuerDatenArtBriefwahl(baseElectContext, ergebnisart, stimmzettelgebietInformation, datenGebiet, zeilenFuerGruppeInDatenGebiet)));
      result.addAll(
          teileSummenAufSeitenAuf(context, documentCreator.createDocumentFuerDatenArtGesamt(baseElectContext, ergebnisart, stimmzettelgebietInformation, datenGebiet, zeilenFuerGruppeInDatenGebiet)));
    } else {
      // TODO result.addAll(teileAufSeitenUndTeile(context, documentCreator.createDocumentAlleDatenArten(baseElectContext, ergebnisart, datenGebiet, zeilenInDatenGebiet)));
      // TODO AbstractErgebnisKandidaturenTabellarischDocumentExportMapper#teileSummenAufSeitenAuf analog zu AbstractErgebnisGesamtergebnisTabellarischDocumentExportMapper.teileSummenAufSeitenAuf anpassen!!!
      throw new UnsupportedOperationException("Tabellenart \"kompakt\" NICHT implementiert!!!");
    }

    return result;
  }

  /**
   * @deprecated
   * @see AbstractErgebnisKandidaturenTabellarischDocumentExportMapper#createSummenTabellen
   */
  @Deprecated
  protected List<ErgebnisKandidaturenTabellarischDocument> createSeitenUndTeileFuerGruppeInStimmzettelgebiet(ErgebnisKandidaturenTabellarischDocumentExportContext context,
      PolitischeGruppe politischeGruppe, ErgebnisDaten ergebnisDaten, WahlverwaltungGebietInformation summenGebiet, WahlverwaltungGebietInformation stimmzettelgebietInformation,
      List<ErgebnisKandidaturenTabellarischDocumentZeile> zeilenInStimmzettelgebiet) {
    return createSummenTabellen(context, ergebnisDaten, true, politischeGruppe, stimmzettelgebietInformation, summenGebiet, stimmzettelgebietInformation, zeilenInStimmzettelgebiet);
  }

  protected List<ErgebnisKandidaturenTabellarischDocument> createSummenTabellen(ErgebnisKandidaturenTabellarischDocumentExportContext context, ErgebnisDaten ergebnisDaten,
      boolean tabellenCreatedPerDatenArt, PolitischeGruppe politischeGruppe, WahlverwaltungGebietInformation stimmzettelgebietInformation, WahlverwaltungGebietInformation summenGebiet,
      WahlverwaltungGebietInformation datenGebiet, List<ErgebnisKandidaturenTabellarischDocumentZeile> zeilenInDatenGebiet) {
    List<ErgebnisKandidaturenTabellarischDocument> result = new ArrayList<>();

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

    List<ErgebnisKandidaturenTabellarischDocumentZeile> zeilenFuerGruppeInDatenGebiet = documentZeileCreator.createZeilenFuerGruppe(zeilenInDatenGebiet, ergebnisDaten, politischeGruppe);

    if (tabellenCreatedPerDatenArt) {
      result.addAll(teileSummenAufSeitenAuf(context,
          documentCreator.createSummenDocumentFuerDatenArtUrnenwahl(baseElectContext, ergebnisart, stimmzettelgebietInformation, summenGebiet, datenGebiet, zeilenFuerGruppeInDatenGebiet)));
      result.addAll(teileSummenAufSeitenAuf(context,
          documentCreator.createSummenDocumentFuerDatenArtBriefwahl(baseElectContext, ergebnisart, stimmzettelgebietInformation, summenGebiet, datenGebiet, zeilenFuerGruppeInDatenGebiet)));
      result.addAll(teileSummenAufSeitenAuf(context,
          documentCreator.createSummenDocumentFuerDatenArtGesamt(baseElectContext, ergebnisart, stimmzettelgebietInformation, summenGebiet, datenGebiet, zeilenFuerGruppeInDatenGebiet)));
    } else {
      // TODO result.addAll(teileSummenAufSeitenAuf(context, documentCreator.createSummenDocumentAlleDatenArten(baseElectContext, ergebnisart, summenGebiet, datenGebiet, zeilenInDatenGebietFuerGruppe)));
      // TODO AbstractErgebnisKandidaturenTabellarischDocumentExportMapper#teileSummenAufSeitenAuf analog zu AbstractErgebnisGesamtergebnisTabellarischDocumentExportMapper.teileSummenAufSeitenAuf anpassen!!!
      throw new UnsupportedOperationException("Tabellenart \"kompakt\" NICHT implementiert!!!");
    }

    return result;
  }

  private List<ErgebnisKandidaturenTabellarischDocument> teileAufSeitenUndTeile(ErgebnisKandidaturenTabellarischDocumentExportContext context, ErgebnisKandidaturenTabellarischDocument document) {
    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<ErgebnisKandidaturenTabellarischDocument> teileSummenAufSeitenAuf(ErgebnisKandidaturenTabellarischDocumentExportContext context, ErgebnisKandidaturenTabellarischDocument document) {
    List<ErgebnisKandidaturenTabellarischDocument> result = documentAufteiler.createDocumentCopiesHavingConfiguredAnzahlSpalten(document, context);

    for (ErgebnisKandidaturenTabellarischDocument teil : result) {
      teil.ergebnisSummen.setSummen(teil.zeilen, teil);
      teil.zeilen.clear(); // alle Zeilen löschen --> es bleiben nur die Summen übrig
    }

    return result;
  }

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