
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.Collections;
import java.util.List;
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.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.gruppe.entity.PolitischeGruppe;
import de.ivu.elect.dokumente.BaseElectContext;
import de.ivu.elect.dokumente.ergebnis.AbstractErgebnisDocumentExportMapper;
import de.ivu.elect.was.exchange.ergebnis.kandidatur.ErgebnisKandidaturenExchange;
import de.ivu.elect.was.exchange.ergebnis.kandidatur.ErgebnisKandidaturenTabellarischDocumentExportMapper;

/**
 * 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 {

  private static final String ERSTE_SEITE_NUMMER = "1";

  @Inject
  private transient ErgebnisKandidaturenTabellarischDocumentCreator documentCreator;

  @Inject
  private transient ErgebnisKandidaturenTabellarischDocumentZeileCreator documentZeileCreator;

  @Inject
  private transient ErgebnisKandidaturenTabellarischDocumentAufteiler documentAufteiler;

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

  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;
  }

  @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(ERSTE_SEITE_NUMMER) ? templateFileErsteSeite : templateFileWeitereSeiten;
  }

  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 gemeindeInformation, List<ErgebnisKandidaturenTabellarischDocumentZeile> zeilenInGemeinde) {
    return createTabellen(context, ergebnisDaten, true, politischeGruppe, gemeindeInformation, zeilenInGemeinde);
  }

  protected List<ErgebnisKandidaturenTabellarischDocument> createTabellen(ErgebnisKandidaturenTabellarischDocumentExportContext context, ErgebnisDaten ergebnisDaten,
      boolean tabellenCreatedPerDatenArt, PolitischeGruppe politischeGruppe, 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,
        getListenkandidaturstimmeLabelGenerator());

    if (tabellenCreatedPerDatenArt) {
      result.addAll(teileAufSeitenUndTeile(context, documentCreator.createDocumentFuerDatenArtUrnenwahl(baseElectContext, ergebnisart, datenGebiet, zeilenFuerGruppeInDatenGebiet)));
      result.addAll(teileAufSeitenUndTeile(context, documentCreator.createDocumentFuerDatenArtBriefwahl(baseElectContext, ergebnisart, datenGebiet, zeilenFuerGruppeInDatenGebiet)));
      result.addAll(teileSummenAufSeitenAuf(context, documentCreator.createDocumentFuerDatenArtGesamt(baseElectContext, ergebnisart, 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, summenGebiet, stimmzettelgebietInformation, zeilenInStimmzettelgebiet);
  }

  protected List<ErgebnisKandidaturenTabellarischDocument> createSummenTabellen(ErgebnisKandidaturenTabellarischDocumentExportContext context, ErgebnisDaten ergebnisDaten,
      boolean tabellenCreatedPerDatenArt, PolitischeGruppe politischeGruppe, 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,
        getListenkandidaturstimmeLabelGenerator());

    if (tabellenCreatedPerDatenArt) {
      result
          .addAll(teileSummenAufSeitenAuf(context, documentCreator.createSummenDocumentFuerDatenArtUrnenwahl(baseElectContext, ergebnisart, summenGebiet, datenGebiet, zeilenFuerGruppeInDatenGebiet)));
      result
          .addAll(teileSummenAufSeitenAuf(context, documentCreator.createSummenDocumentFuerDatenArtBriefwahl(baseElectContext, ergebnisart, summenGebiet, datenGebiet, zeilenFuerGruppeInDatenGebiet)));
      result.addAll(teileSummenAufSeitenAuf(context, documentCreator.createSummenDocumentFuerDatenArtGesamt(baseElectContext, ergebnisart, 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);
  }
}
