
package de.ivu.elect.exchange;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

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

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

import de.ivu.document.generator.template.OpenOfficeKonfiguration;
import de.ivu.document.generator.template.monitor.DocumentProgressMonitor;
import de.ivu.document.generator.template.openoffice.CheckOOConnection;
import de.ivu.document.generator.template.openoffice.CheckOOConnectionResult;
import de.ivu.document.generator.template.pdf.OdtKonvertierer;
import de.ivu.elect.business.datei.entity.FileType;
import de.ivu.elect.business.dokumente.control.LayoutVorlageFinder;
import de.ivu.elect.business.dokumente.control.OpenOfficeKonfigurationFactory;
import de.ivu.elect.business.dokumente.control.ProgressMonitorWrapper;
import de.ivu.elect.business.exchange.control.FileFormat;
import de.ivu.elect.business.exchange.control.FileKonfiguration;
import de.ivu.elect.business.exchange.control.MappingException;
import de.ivu.elect.business.wahl.entity.WahlOderWahltermin;
import de.ivu.elect.jee.exception.ElectCommonException;
import de.ivu.elect.presentation.dokumente.DokumentGenerierenProgressMonitor;
import de.ivu.elect.util.CopylessByteArrayOutputStream;
import de.ivu.elect.util.ThrowableUtil;


import static de.ivu.elect.exchange.ExchangeOdtKonvertierer.toDocumentGeneratorFileType;

public abstract class AbstractOpenOfficeKonvertierenderExportMapper<ExportObject, Context>
    implements
      ExportMapper<ExportObject, Context> {

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

  @Inject
  private transient OpenOfficeKonfigurationFactory openOfficeDokumentKonfiguration;

  @Inject
  private transient DokumentGenerierenProgressMonitor dokumentGenerierenProgressMonitor;

  @Inject
  private transient LayoutVorlageFinder layoutVorlageFinder;

  private final CheckOOConnection checkOOConnection;

  private final OdtKonvertierer odtKonvertierer;

  private DocumentProgressMonitor documentProgressMonitor;

  protected AbstractOpenOfficeKonvertierenderExportMapper() {
    this.odtKonvertierer = new OdtKonvertierer();
    this.checkOOConnection = new CheckOOConnection();
  }

  @Override
  public void serialize(ExportObject exportObject, OutputStream os, Context context, FileKonfiguration fileKonfiguration)
      throws MappingException {

    DocumentProgressMonitor progressMonitor = getDocumentProgressMonitor();
    OpenOfficeKonfiguration openOfficeKonfiguration = getOpenOfficeKonfiguration();

    try {
      // fail fast
      assertOpenOfficeConnectionIsAvailable(openOfficeKonfiguration, progressMonitor);

      InputStream konverterInputStream = serializeWithWrappedExportMapper(exportObject, context,
          fileKonfiguration);

      try (InputStream ottIs = createOttIs(layoutVorlageFinder.getVorlageContent(getWahlOderWahltermin(context)))) {
        odtKonvertierer.konvertiereDokument(konverterInputStream, toDocumentGeneratorFileType(getEingabeFileType()), ottIs,
          os, toDocumentGeneratorFileType(getAusgabeFileType()), openOfficeKonfiguration, progressMonitor);
      }


    } catch (IOException rethrownEx) {
      throw new MappingException("Fehler beim Konvertieren [ausgangsTyp = " + getEingabeFileType() + ", zielTyp = "
          + getAusgabeFileType() + "]", rethrownEx);
    }

  }

  @Nullable
  private InputStream createOttIs(byte[] vorlageContent) {
    if (vorlageContent == null) {
      return null;
    }
    return new ByteArrayInputStream(vorlageContent);
  }

  protected abstract WahlOderWahltermin getWahlOderWahltermin(Context context);

  protected void assertOpenOfficeConnectionIsAvailable(OpenOfficeKonfiguration openOfficeKonfiguration,
      DocumentProgressMonitor progressMonitor) {

    CheckOOConnectionResult result = checkOOConnection.isConnectionPossible(openOfficeKonfiguration, progressMonitor);

    if (!result.isPossible()) {
      LOG.warn("Keine Verbindung zu OpenOffice. [ExportMapper = " + getClass().getCanonicalName() + "]",
          result.getThrowable());
      String messageDerUrsache = "";
      if (result.getThrowable() != null) {
        messageDerUrsache = ThrowableUtil.getMessageDerUrsache(result.getThrowable());
      }
      throw new ElectCommonException("exchange.exception.keine.verbindung.zu.open.office.message",
        messageDerUrsache);
    }

  }

  protected InputStream serializeWithWrappedExportMapper(ExportObject exportObject, Context context,
      FileKonfiguration fileKonfiguration) throws MappingException, IOException {

    CopylessByteArrayOutputStream os = new CopylessByteArrayOutputStream();

    getExportMapper().serialize(exportObject, os, context, fileKonfiguration);

    return os.toInputStreamAndClose();
  }

  protected abstract FileType getAusgabeFileType();

  protected FileType getEingabeFileType() {
    return getExportMapper().getFileFormat().getFileType();
  }



  protected DocumentProgressMonitor getDocumentProgressMonitor() {
    if (documentProgressMonitor == null) {
      documentProgressMonitor = new ProgressMonitorWrapper(dokumentGenerierenProgressMonitor);
    }

    return documentProgressMonitor;
  }

  protected abstract ExportMapper<ExportObject, Context> getExportMapper();

  protected OpenOfficeKonfiguration getOpenOfficeKonfiguration() {
    return openOfficeDokumentKonfiguration.createConfig();
  }

  @Override
  public FileFormat getFileFormat() {
    return new FileFormat(getExportMapper().getFileFormat().getName(), getAusgabeFileType());
  }

  @Override
  public boolean isAcceptContext(Context context) {
    return getExportMapper().isAcceptContext(context) && (getAusgabeFileType().isOpenDocumentFile() || openOfficeDokumentKonfiguration.isOpenOfficeVerfuegbar());
  }
}
