
package de.ivu.elect.dokumente.stimmzettel;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
import java.util.Map.Entry;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

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

import org.apache.commons.io.IOUtils;

import de.ivu.elect.business.datei.entity.Datei;
import de.ivu.elect.business.datei.entity.FileType;
import de.ivu.elect.business.dokumente.entity.DokumentVorlage;
import de.ivu.elect.business.dokumente.entity.DokumentVorlageArt;
import de.ivu.elect.business.exchange.control.MappingException;
import de.ivu.elect.business.gebietsbaum.boundary.WahlverwaltungGebietInformation;
import de.ivu.elect.business.gruppe.entity.Gruppe;
import de.ivu.elect.business.kandidatur.entity.Listenkandidatur;
import de.ivu.elect.business.stimmzettel.boundary.EntityAufStimmzettelDatenDAOFactory;
import de.ivu.elect.business.stimmzettel.control.StimmzettelExportContext;
import de.ivu.elect.business.stimmzettel.entity.EntityAufStimmzettelDaten;
import de.ivu.elect.business.stimmzettel.entity.StimmzettelDaten;
import de.ivu.elect.business.wahl.entity.Wahl;
import de.ivu.elect.business.wahlvorschlag.entity.Direktkandidat;
import de.ivu.elect.business.wahlvorschlag.entity.Liste;
import de.ivu.elect.business.wahlvorschlag.entity.ReferendumOption;
import de.ivu.elect.dokumente.konverter.AbstractDocumentMapper;
import de.ivu.elect.dokumente.konverter.DokumentGeneratorDocumentMapperDokumentVorlagen;
import de.ivu.elect.wus.exchange.stimmzettel.SimpleStimmzettelForZipExportMapper;

public abstract class AbstractStimmzettelDocumentExportMapper extends AbstractDocumentMapper<StimmzettelExportContext> implements SimpleStimmzettelForZipExportMapper {

  protected static final FileType FILE_TYPE_CONFIGURABLE = null;

  @Inject
  private transient EntityAufStimmzettelDatenDAOFactory entityAufStimmzettelDatenDAOFactory;

  protected AbstractStimmzettelDocumentExportMapper() {
    super();
  }

  @Override
  public boolean isAcceptContext(StimmzettelExportContext context) {
    Wahl wahl = context.getWahl();

    return wahl != null && isAcceptContext(wahl, getDokumentVorlagen(context).getDokumentVorlage());
  }

  protected abstract boolean isAcceptContext(@Nonnull Wahl wahl, @Nullable DokumentVorlage dokumentVorlage);

  protected abstract FileType getFileTypeSerialized(StimmzettelExportContext context);

  @Override
  public void serialize(OutputStream os, StimmzettelExportContext context, String fileFormatName, WahlverwaltungGebietInformation auswertungsgebietInformation,
      List<Entry<WahlverwaltungGebietInformation, StimmzettelDaten>> stimmzettelDatenPerStimmzettelgebiet, boolean closeOutputStream) throws MappingException {
    DokumentVorlage dokumentVorlage = getDokumentVorlagen(context).getDokumentVorlage(context.getDokumentVorlage());

    info("Erzeuge Dokument [" //
        + "wahl = " + context.getWahl().getName() + ", " //
        + "dokumentVorlage = " + (dokumentVorlage == null ? "ohne" : "[art = " + dokumentVorlage.getDokumentVorlageArt() + ", name = " + dokumentVorlage.getName() + "]") + ", " //
        + "dateityp serialisiert = " + getFileTypeSerialized(context) + ", " //
        + "zieldateityp = " + getTargetFileType(context) + ", " //
        + "dokument erzeugt = pro " + (context.isTargetCreatedPerWurzelgebiet() ? "wurzelgebiet" : "stimmzettelgebiet") + ", "//
        + "stimmzettelgebiete enthalten = " + stimmzettelDatenPerStimmzettelgebiet.size() //
        + "]");

    info("\tAuswertungsgebiet [art = " + auswertungsgebietInformation.getArt() + "/" + auswertungsgebietInformation.getArtIndex() + ", nummer = " + auswertungsgebietInformation.getNummer()
        + ", name = " + auswertungsgebietInformation.getName() + ", gebiet in gebietsbaum = " + auswertungsgebietInformation.getGebiet().isPresent() + "]");

    for (Entry<WahlverwaltungGebietInformation, StimmzettelDaten> entry : stimmzettelDatenPerStimmzettelgebiet) {
      WahlverwaltungGebietInformation stimmzettelgebietInformation = entry.getKey();

      info("\tStimmzettelgebiet [art = " + stimmzettelgebietInformation.getArt() + "/" + stimmzettelgebietInformation.getArtIndex() + ", nummer = " + stimmzettelgebietInformation.getNummer()
          + ", name = " + stimmzettelgebietInformation.getName() + ", gebiet in gebietsbaum = " + stimmzettelgebietInformation.getGebiet().isPresent() + "]");

      StimmzettelDaten stimmzettelDaten = entry.getValue();

      info("\t\tStimmen [anzahlStimmenProWaehler = " + stimmzettelDaten.getAnzahlStimmenProWaehlerMaximal() + ", anzahlStimmenProWaehlerProDirektkandidatur = "
          + stimmzettelDaten.getAnzahlStimmenProWaehlerProDirektkandidaturMaximal() + ", anzahlStimmenProWaehlerProListenkandidatur = "
          + stimmzettelDaten.getAnzahlStimmenProWaehlerProListenkandidaturMaximal() + "]");

      for (Direktkandidat direktkandidat : stimmzettelDaten.getDirektkandidatenAufgestellt()) {
        info("\t\tDirektkandidat [kennwort = " + direktkandidat.getKennwort() + ", kurzname = " + direktkandidat.getKurzname() + "]");
      }

      for (Liste liste : stimmzettelDaten.getListenAufgestellt()) {
        List<Listenkandidatur> listenkandidaturen = stimmzettelDaten.getListenkandidaturen(liste);

        info("\t\tListe [kennwort = " + liste.getKennwort() + ", kurzname = " + liste.getKurzname() + ", listenkandidaturen = " + listenkandidaturen.size() + "]");

        for (Listenkandidatur listenkandidatur : listenkandidaturen) {
          info("\t\t\tListenkandidatur [name = " + stimmzettelDaten.getListenkandidaturName(listenkandidatur) + "]");
        }
      }

      for (Gruppe referendumVorlage : stimmzettelDaten.getReferendumVorlagen()) {
        List<ReferendumOption> referendumOptionen = stimmzettelDaten.getReferendumOptionen(referendumVorlage);

        info("\t\tVorlage [schluessel = " + referendumVorlage.getSchluessel() + ", name = " + referendumVorlage.getName() + ", kurzname = " + referendumVorlage.getKurzname()
            + ", referendumOptionen = " + referendumOptionen.size() + "]");

        for (ReferendumOption referendumOption : referendumOptionen) {
          info("\t\t\tOption [kurzbezeichnung = " + referendumOption.getKurzbezeichnung() + ", label = " + referendumOption.getLabel() + "]");
        }
      }
    }

    long tic = System.currentTimeMillis();

    try {
      serialize(os, context, fileFormatName, auswertungsgebietInformation, stimmzettelDatenPerStimmzettelgebiet, dokumentVorlage, closeOutputStream);
    } catch (Exception ex) {
      throw new MappingException(ex.getMessage(), ex);
    }

    long durationInMs = System.currentTimeMillis() - tic;

    info("Dokument erzeugt [dauer = " + durationInMs / 1000 + "s (" + durationInMs + "ms)]");
  }

  protected abstract void serialize(OutputStream os, StimmzettelExportContext context, String fileFormatName, WahlverwaltungGebietInformation auswertungsgebietInformation,
      List<Entry<WahlverwaltungGebietInformation, StimmzettelDaten>> stimmzettelDatenPerStimmzettelgebiet, @Nullable DokumentVorlage dokumentVorlage, boolean closeOutputStream) throws Exception;

  public DokumentGeneratorDocumentMapperDokumentVorlagen getDokumentVorlagen(StimmzettelExportContext context) {
    return new DokumentGeneratorDocumentMapperDokumentVorlagen(context.getWahl(), getDokumentVorlageDAO(), getDokumentVorlageArt(), getDokumentVorlageFileType(context), getDokumentVorlageNameInfix());
  }

  protected abstract DokumentVorlageArt getDokumentVorlageArt();

  protected abstract FileType getDokumentVorlageFileType(StimmzettelExportContext context);

  protected abstract String getDokumentVorlageNameInfix();

  protected EntityAufStimmzettelDaten getEntityAufStimmzettelDaten() {
    return entityAufStimmzettelDatenDAOFactory.getEntityAufStimmzettelDaten();
  }

  @Override
  public void addResources(ZipOutputStream zos, StimmzettelExportContext context) throws IOException {
    if (Boolean.TRUE.equals(context.getDokumentVorlageResourcesIncluded())) {
      DokumentVorlage barcodeFontsArchive = getDokumentVorlagen(context).getDokumentVorlageResourcesArchive();

      if (barcodeFontsArchive != null) {
        zos.putNextEntry(new ZipEntry("barcode-fonts.zip"));

        Datei datei = barcodeFontsArchive.getDatei();

        try (InputStream is = createInputStream(datei)) {
          IOUtils.copy(is, zos);
        }

        zos.closeEntry();
      }
    }
  }
}
