package de.ivu.elect.was.exchange.wahl.wes;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

import javax.annotation.Nullable;
import javax.inject.Inject;
import javax.xml.bind.JAXBException;

import de.ivu.elect.business.datei.entity.FileType;
import de.ivu.elect.business.dokumente.boundary.DokumentVorlageDAO;
import de.ivu.elect.business.exchange.control.FileFormat;
import de.ivu.elect.business.exchange.control.ImportExportStreamCreator;
import de.ivu.elect.business.stimmzettel.boundary.StimmzettelErfassungSupport;
import de.ivu.elect.business.wahl.boundary.WesTemplateFinder;
import de.ivu.elect.business.wahl.control.WahlExportObject;
import de.ivu.elect.business.wahl.control.WahlWesExportContext;
import de.ivu.elect.business.wahl.entity.Wahl;
import de.ivu.elect.util.FileUtil;
import de.ivu.elect.wus.exchange.wahl.WasWahlXmlZipExportMapper;

public abstract class AbstractWahlFuerWesExportMapper implements WahlFuerWesExportMapper {

  public static final String FORMAT_NAME = "file.format.wahl.export.wes";

  public static final String WAHLEN_ZIP = "wahlen.zip";
  public static final String WAHL_DIR = "wahl";
  public static final String CONFIG_WAHL_BAT = "config-wahl.bat";
  public static final String CONFIG_WAHL_SH = "config-wahl.sh";


  private static final int BUFFER_SIZE = 100 * 1024;

  @Inject
  private transient StimmzettelErfassungSupport stimmzettelErfassungSupport;

  @Inject
  private transient WasWahlXmlZipExportMapper wasWahlZipExportMapper;

  @Inject
  private transient DokumentVorlageDAO dokumentVorlageDAO;

  @Inject
  private transient ImportExportStreamCreator streamCreator;

  @Inject
  private transient WesTemplateFinder wesTemplateFinder;

  @Override
  public FileFormat getFileFormat() {
    return new FileFormat(FORMAT_NAME, getFileType());
  }

  protected abstract FileType getFileType();

  @Override
  public boolean isAcceptContext(WahlWesExportContext context) {
    return stimmzettelErfassungSupport.isStimmzettelErfassungVerfuegbar(context.getWahl()) && wasWahlZipExportMapper.isAcceptContext(context);
  }

  protected Map<String, byte[]> mappeWahlen(List<WahlExportObject> wahlExportObjects, WahlWesExportContext context) {
    if (wahlExportObjects.isEmpty()) {
      return Collections.emptyMap();
    }
    Map<String, byte[]> result = new HashMap<>();
    int count = 1;
    for (WahlExportObject wahlExportObject : wahlExportObjects) {
      String name = "wahl" + count++ + "_" + FileUtil.normalizeZipEntryName(wahlExportObject.getWahldaten().getWahl().getName()) + ".xml";
      try (ByteArrayOutputStream bos = new ByteArrayOutputStream(BUFFER_SIZE)) {
        wasWahlZipExportMapper.serializeWahlExportObject(wahlExportObject, bos);
        result.put(name, bos.toByteArray());
      } catch (IOException | JAXBException e) {
        throw new RuntimeException(e.getMessage(), e);
      }
    }
    return result;
  }

  @Nullable
  protected ZipInputStream createTemplateStream(WahlWesExportContext context) {

    File wesTemplateFile = wesTemplateFinder.getTemplateVorlage(context.getWesTemplateFile());
    if (wesTemplateFile == null || !wesTemplateFile.isFile()) {
      return null;
    }
    try {
      return new ZipInputStream(new FileInputStream(wesTemplateFile));
    } catch (Exception e) {
      throw new RuntimeException("file:" + wesTemplateFile + ", " + e.getMessage(), e);
    }
  }

  protected byte[] writeWahlen(Map<String, byte[]> map) throws IOException {
    try (ByteArrayOutputStream bos = new ByteArrayOutputStream(BUFFER_SIZE)) {
      try (ZipOutputStream zosWahlen = new ZipOutputStream(bos)) {
        for (Map.Entry<String, byte[]> entry : map.entrySet()) {
          ZipEntry wahlEntry = new ZipEntry(entry.getKey());
          zosWahlen.putNextEntry(wahlEntry);
          zosWahlen.write(entry.getValue());
          zosWahlen.closeEntry();
        }
        zosWahlen.finish();
      }
      return bos.toByteArray();
    }
  }


  protected byte[] createConfigWahlBat(String wahlId) {
    StringBuilder bat = new StringBuilder();
    bat.append("@ECHO OFF\r\n");
    bat.append("SET WAHL_FILE=" + WAHLEN_ZIP + "\r\n");
    bat.append("SET WAHL_ID=" + wahlId + "\r\n");
    return bat.toString().getBytes(StandardCharsets.UTF_8);
  }

  protected byte[] createConfigWahlSh(String wahlId) {
    StringBuilder sh = new StringBuilder();
    sh.append("WAHL_FILE=" + WAHLEN_ZIP + "\n");
    sh.append("WAHL_ID=" + wahlId + "\n");
    return sh.toString().getBytes(StandardCharsets.UTF_8);
  }

  protected String createWahlId(WahlWesExportContext context) {
    // ELECTKOM-1011
    if (context.isWahlIdMitZeitstempel()) {
      return new SimpleDateFormat("yyyyMMdd").format(context.getWahl().getWahltermin().getWahldatum()) + "_" + new SimpleDateFormat("yyyyMMddHHmmss").format(context.getNow());
    }
    StringBuilder id = new StringBuilder(new SimpleDateFormat("yyyyMMdd").format(context.getWahl().getWahltermin().getWahldatum()));
    id.append("_").append(FileUtil.normalizeZipEntryName(context.getWahl().getName()));
    for (Wahl wahl : context.getWeitereWahlen()) {
      id.append("_").append(FileUtil.normalizeZipEntryName(wahl.getName()));
    }
    return id.toString();
  }

}
