
package de.ivu.elect.was.exchange.stimmeingabe;

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

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

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

import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import de.ivu.elect.EmlExportMapper;
import de.ivu.elect.EmlImportMapper;
import de.ivu.elect.EmlImportMapperHelper;
import de.ivu.elect.business.datei.entity.Datei;
import de.ivu.elect.business.datei.entity.FileType;
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.stimmeingabe.control.StimmeingabeImportContext;
import de.ivu.elect.business.stimmeingabe.entity.Ergebniseingang;
import de.ivu.elect.business.wahl.entity.Wahldurchfuehrung;
import de.ivu.elect.jee.exchange.AbstractViolation;
import de.ivu.elect.jee.exchange.Violations;
import de.ivu.elect.jee.presentation.dynamicform.FormBuilder;
import de.ivu.elect.presentation.security.NlPrivatePublicKeyNeeded;

public class Eml510StimmeingabeZipImportMapper extends AbstractStimmeingabeImportMapper implements EmlImportMapper {

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

  private static final int BUFFER_SIZE = 64 * 1024;

  private static final long serialVersionUID = 1L;

  @Inject
  private transient NlPrivatePublicKeyNeeded nlPrivatePublicKeyNeeded;

  @Inject
  private transient Eml510StimmeingabeImportMapper eml510StimmeingabeImportMapper;

  @Inject
  private transient EmlImportMapperHelper emlImportMapperHelper;

  public Eml510StimmeingabeZipImportMapper() {
    super();
  }

  @Override
  public boolean isAcceptContext(StimmeingabeImportContext context) {
    return super.isAcceptContext(context) && eml510StimmeingabeImportMapper.isAcceptContext(context);
  }

  @Override
  protected boolean isAcceptWahl(Wahldurchfuehrung wahldurchfuehrung) {
    return nlPrivatePublicKeyNeeded.isPublicKeyNeeded(wahldurchfuehrung.getMandant());
  }

  @Override
  public FileFormat getFileFormat() {
    return new FileFormat(eml510StimmeingabeImportMapper.getFileFormat().getName(), FileType.ZIP);
  }

  @Override
  public List<Ergebniseingang> deserialize(InputStream is, StimmeingabeImportContext context, FileKonfiguration fileKonfiguration) throws MappingException {
    return eml510StimmeingabeImportMapper.deserialize(new ByteArrayInputStream(readContent(is).getEmlContent()), context, fileKonfiguration);
  }

  @Override
  public Violations validateInputFormat(InputStream is, StimmeingabeImportContext context, FileKonfiguration fileKonfiguration) {
    Violations violations = new Violations();

    EmlSignaturContent emlSignaturContent = readContent(is);

    if (emlSignaturContent.getEmlContent() == null) {
      violations.addError("eml.content.nicht.gefunden");
    } else {
      addSignatureToContext(context, emlSignaturContent);

      Violations violationsInner = eml510StimmeingabeImportMapper.validateInputFormat(new ByteArrayInputStream(emlSignaturContent.getEmlContent()), context, fileKonfiguration);

      for (AbstractViolation violation : violationsInner.getViolations()) {
        violations.add(violation);
      }
    }

    return violations;
  }

  private void addSignatureToContext(StimmeingabeImportContext context, EmlSignaturContent emlSignaturContent) {
    if (context.get(Eml510StimmeingabeImportMapper.EML_SIGNATUR_AS_BYTES, Object.class) == null) {
      byte[] signatureData = emlSignaturContent.getSignatur();

      if (signatureData != null) {
        try {
          context.put(Eml510StimmeingabeImportMapper.EML_SIGNATUR_AS_BYTES, signatureData);
        } catch (Exception ignored) {
        }
      }
    }
  }

  @Override
  public void addProperties(FormBuilder formBuilder, StimmeingabeImportContext context, @Nullable Datei datei) {
    if (datei != null) {
      try {
        try (InputStream is = emlImportMapperHelper.getInputStream(datei)) {
          EmlSignaturContent emlSignaturContent = readContent(is);
          addSignatureToContext(context, emlSignaturContent);
          eml510StimmeingabeImportMapper.addProperties(formBuilder, context, new ByteArrayInputStream(emlSignaturContent.getEmlContent()));
        }
      } catch (Exception e) {
        LOG.warn("Fehler beim Lesen der Datei: " + datei + ", " + e.getMessage());
      }
    }

  }

  private EmlSignaturContent readContent(InputStream is) {
    byte[] emlContent = null;
    byte[] signature = null;

    try (ZipInputStream zis = new ZipInputStream(is) {

      @Override
      public void close() throws IOException {
        // ignorieren
      }
    }) {
      ZipEntry zipEntry;

      while ((zipEntry = zis.getNextEntry()) != null) {
        try (ByteArrayOutputStream bos = new ByteArrayOutputStream(BUFFER_SIZE)) {
          IOUtils.copy(zis, bos);

          String zipEntryFileName = zipEntry.getName();

          if (endsWithIgnoreCase(zipEntryFileName, EmlExportMapper.EML_FILE_EXTENSION)) {
            emlContent = bos.toByteArray();
          }

          if (endsWithIgnoreCase(zipEntryFileName, EmlExportMapper.EML_SIGNATURE_FILE_EXTENSION)) {
            signature = bos.toByteArray();
          }
        }
      }

    } catch (IOException e) {
      throw new RuntimeException(e.getMessage(), e);
    }

    return new EmlSignaturContent(emlContent, signature);
  }
}
