
package de.ivu.elect.exchange.zip;

import java.io.IOException;
import java.io.InputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

import org.apache.commons.io.input.CloseShieldInputStream;

import de.ivu.elect.business.datei.entity.FileType;
import de.ivu.elect.business.exchange.control.DefaultFileKonfiguration;
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.exchange.ImportMapper;
import de.ivu.elect.jee.exchange.Violations;

public class DelegateZipImportMapper {

  public DelegateZipImportMapper() {
    super();
  }

  public FileFormat getFileFormat(ImportMapper< ? , ? > nonZipImportMapper) {
    FileFormat fileFormatArchived = nonZipImportMapper.getFileFormat();

    return new FileFormat(fileFormatArchived.getName(), FileType.ZIP, fileFormatArchived.getFileType(), nonZipImportMapper.getPriority());
  }

  public FileFormat getFileFormat(String formatName) {
    return new FileFormat(formatName, FileType.ZIP);
  }

  public <T, C> T deserialize(ImportMapper<T, C> nonZipImportMapper, InputStream is, C context, FileKonfiguration fileKonfiguration) throws MappingException {
    try {
      InputStream isEntry = getInputStreamForOnlyZipEntry(is);

      return nonZipImportMapper.deserialize(isEntry, context, createFileKonfiguration(nonZipImportMapper));
    } catch (IOException ex) {
      throw new MappingException("ZIP-Datei konnte nicht eingelesen werden [is=" + is + ", context=" + context + ", fileKonfiguration=" + fileKonfiguration + "]", ex);
    }
  }

  public <C> Violations validateInputFormat(ImportMapper< ? , C> nonZipImportMapper, InputStream is, C context, FileKonfiguration fileKonfiguration) {
    Violations result = new Violations();

    try {
      ZipInputStream zip = new ZipInputStream(is);

      ZipEntry firstZipEntry = zip.getNextEntry();

      if (validateEntryPresent(result, firstZipEntry)) {
        result.add(nonZipImportMapper.validateInputFormat(uncloseable(zip), context, createFileKonfiguration(nonZipImportMapper)));
        validateNoMoreZipEntries(result, zip, firstZipEntry);
      }
    } catch (Exception ex) {
      throw new RuntimeException("ZIP-Datei konnte nicht validiert werden [is=" + is + ", context=" + context + ", fileKonfiguration=" + fileKonfiguration + "]", ex);
    }

    return result;
  }

  protected FileKonfiguration createFileKonfiguration(ImportMapper< ? , ? > nonZipImportMapper) {
    return new DefaultFileKonfiguration(nonZipImportMapper.getFileFormat());
  }

  private boolean validateEntryPresent(Violations violations, ZipEntry zipEntry) {
    if (zipEntry == null) {
      violations.addError("validation.zip.empty");

      return false;
    }

    return true;
  }

  private void validateNoMoreZipEntries(Violations violations, ZipInputStream is, ZipEntry firstZipEntry) throws IOException {
    if (is.getNextEntry() != null) {
      violations.addWarning("validation.zip.too.many.entries", firstZipEntry.getName());
    }
  }

  private ZipInputStream getInputStreamForOnlyZipEntry(InputStream is) throws IOException {
    ZipInputStream zip = new ZipInputStream(is);

    ZipEntry entry = zip.getNextEntry();

    if (entry == null) {
      throw new IOException("ZIP-Datei enthält keine Dateien");
    }

    return zip;
  }

  private static InputStream uncloseable(InputStream is) {
    return new CloseShieldInputStream(is);
  }
}
