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

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

import javax.inject.Inject;

import de.ivu.elect.business.datei.entity.FileType;
import de.ivu.elect.business.datei.entity.FileTypeResolver;
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.interfaces.was.stimmeingabe.ErfassteStimmen;
import de.ivu.elect.jee.exchange.AbstractViolation;
import de.ivu.elect.jee.exchange.ViolationMitBezug;
import de.ivu.elect.jee.exchange.Violations;

public class WasStimmeingabeXMLZIPImportMapper extends AbstractWasStimmeingabeXMLImportMapper {

  private static final long serialVersionUID = 1L;

  @Inject
  private transient FileTypeResolver fileTypeResolver;

  public WasStimmeingabeXMLZIPImportMapper() {
    super();
  }

  @Override
  public FileFormat getFileFormat() {
    FileFormat fileFormatArchived = super.getFileFormat();

    return new FileFormat(fileFormatArchived.getName(), getFileType(), fileFormatArchived.getFileType());
  }

  @Override
  protected FileType getFileType() {
    return FileType.ZIP;
  }

  @Override
  public List<ErfassteStimmen> extractErfassteStimmen(InputStream is, StimmeingabeImportContext context, FileKonfiguration fileKonfiguration) throws MappingException {
    List<ErfassteStimmen> result = new ArrayList<>();

    ZipEntry entry = null;

    try (ZipInputStream zis = new ZipInputStream(is) {

      @Override
      public void close() throws IOException {
        // nicht schließen
      }
    }) {
      while ((entry = zis.getNextEntry()) != null) {
        if (!entry.isDirectory()) {

          if (isZipFile(entry)) {
            debug("Deserialisiere ZIP-Entry rekursiv: {}", entry);

            result.addAll(extractErfassteStimmen(zis, context, fileKonfiguration));
          } else {
            debug("Deserialisiere ZIP-Entry: {}", entry);

            result.addAll(super.extractErfassteStimmen(zis, context, fileKonfiguration));
          }
        }
      }

      return result;
    } catch (IOException ex) {
      throw new RuntimeException(entry.getName() + ": " + ex.getMessage(), ex);
    } catch (MappingException ex) {
      throw new MappingException(entry.getName() + ": " + ex.getMessage(), ex);
    }
  }

  @Override
  public List<Ergebniseingang> deserialize(InputStream is, StimmeingabeImportContext context, FileKonfiguration fileKonfiguration) throws MappingException {
    List<Ergebniseingang> result = new ArrayList<>();

    ZipEntry entry = null;

    try (ZipInputStream zis = new ZipInputStream(is) {

      @Override
      public void close() throws IOException {
        // nicht schließen
      }
    }) {
      while ((entry = zis.getNextEntry()) != null) {
        if (!entry.isDirectory()) {

          if (isZipFile(entry)) {
            debug("Deserialisiere ZIP-Entry rekursiv: {}", entry);

            result.addAll(deserialize(zis, context, fileKonfiguration));
          } else {
            debug("Deserialisiere ZIP-Entry: {}", entry);

            result.addAll(super.deserialize(zis, context, fileKonfiguration));
          }
        }
      }

      return result;
    } catch (IOException ex) {
      throw new RuntimeException(entry.getName() + ": " + ex.getMessage(), ex);
    } catch (MappingException ex) {
      throw new MappingException(entry.getName() + ": " + ex.getMessage(), ex);
    }
  }

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

    ZipEntry entry = null;

    try (ZipInputStream zis = new ZipInputStream(is) {

      @Override
      public void close() throws IOException {
        // nicht schließen
      }
    }) {

      while ((entry = zis.getNextEntry()) != null) {
        if (!entry.isDirectory()) {

          if (isZipFile(entry)) {
            debug("Validiere ZIP-Entry rekursiv: {}", entry);

            result.add(validateInputFormat(zis, context, fileKonfiguration));
          } else {
            debug("Validiere ZIP-Entry: {}", entry);

            result.add(setzeInBezug(entry, super.validateInputFormat(zis, context, fileKonfiguration)));
          }
        }
      }

      return result;
    } catch (IOException ex) {
      throw new RuntimeException(ex.getMessage(), ex);
    }
  }

  private Violations setzeInBezug(ZipEntry entry, Violations entryViolations) {
    Violations result = new Violations();

    for (AbstractViolation violation : entryViolations.getViolations()) {
      result.add(new ViolationMitBezug(entry.getName(), violation));
    }

    return result;
  }

  private Boolean isZipFile(ZipEntry entry) {
    return fileTypeResolver.getFileType(entry.getName()).map(FileType::isZipFile).orElse(false);
  }
}
