
package de.ivu.elect.exchange.lokalisierung.properties;

import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
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.ZipOutputStream;

import javax.inject.Inject;

import de.ivu.elect.business.CurrentCustomer;
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.exchange.control.NoContext;
import de.ivu.elect.business.lokalisierung.control.LokalisierungBySchluesselComparator;
import de.ivu.elect.business.lokalisierung.entity.Lokalisierung;
import de.ivu.elect.business.lokalisierung.entity.LokalisierungLocale;
import de.ivu.elect.business.lokalisierung.entity.LokalisierungModul;
import de.ivu.elect.business.security.Customer;
import de.ivu.elect.exchange.lokalisierung.LokalisierungExportMapper;

public class LokalisierungPropertiesExportMapper implements LokalisierungExportMapper {

  @Inject
  private transient CurrentCustomer currentCustomer;

  public LokalisierungPropertiesExportMapper() {
    super();
  }

  @Override
  public boolean isAcceptContext(NoContext noContext) {
    return true;
  }

  @Override
  public FileFormat getFileFormat() {
    return new FileFormat("lokalisierung.properties.format.zip", FileType.ZIP);
  }

  @Override
  public void serialize(List<Lokalisierung> lokalisierungenInDb, OutputStream os, NoContext noContext, FileKonfiguration fileKonfiguration) throws MappingException {
    Map<LokalisierungModul, Map<LokalisierungLocale, List<Lokalisierung>>> lokalisierungenNachModulUndSprache = bestimmeLokalisierungenNachModulUndSprache(lokalisierungenInDb);

    try (ZipOutputStream zos = new ZipOutputStream(os)) {
      for (Map.Entry<LokalisierungModul, Map<LokalisierungLocale, List<Lokalisierung>>> lokalisierungModulMapEntry : lokalisierungenNachModulUndSprache.entrySet()) {
        LokalisierungModul modul = lokalisierungModulMapEntry.getKey();

        for (Map.Entry<LokalisierungLocale, List<Lokalisierung>> localeListEntry : lokalisierungModulMapEntry.getValue().entrySet()) {
          LokalisierungLocale lokalisierungLocale = localeListEntry.getKey();
          List<Lokalisierung> lokalisierungen = localeListEntry.getValue();
          Collections.sort(lokalisierungen, new LokalisierungBySchluesselComparator());

          if (lokalisierungLocale.isInIso_8859_1Enthalten()) {
            zos.putNextEntry(createEntry(currentCustomer.getCustomer(), modul, lokalisierungLocale, ".properties"));
            schreibeProperties(zos, lokalisierungen, StandardCharsets.ISO_8859_1, false);
            zos.closeEntry();
          } else {
            zos.putNextEntry(createEntry(currentCustomer.getCustomer(), modul, lokalisierungLocale, ".properties"));
            schreibeProperties(zos, lokalisierungen, StandardCharsets.ISO_8859_1, true);
            zos.closeEntry();
            zos.putNextEntry(createEntry(currentCustomer.getCustomer(), modul, lokalisierungLocale, ".utf8"));
            schreibeProperties(zos, lokalisierungen, StandardCharsets.UTF_8, false);
            zos.closeEntry();
          }
        }
      }

      zos.finish();
    } catch (IOException e) {
      throw new MappingException(e.getMessage(), e);
    }
  }

  private void schreibeProperties(ZipOutputStream zos, List<Lokalisierung> lokalisierungen, Charset charset, boolean benutzeUnicode) throws IOException {
    OutputStreamWriter writer = new OutputStreamWriter(zos, charset);
    BufferedWriter bw = new BufferedWriter(writer);

    for (Lokalisierung lokalisierung : lokalisierungen) {
      bw.write(konvertiere(lokalisierung.getSchluessel(), benutzeUnicode));
      bw.write("=");
      bw.write(konvertiere(lokalisierung.getWertGespeichert(), benutzeUnicode));
      bw.newLine();
    }

    bw.flush();
  }

  private String konvertiere(String string, boolean benutzeUnicode) {
    if (string == null) {
      return "";
    }

    if (benutzeUnicode) {
      return UTF8ToAscii.unicodeEscape(string);
    }

    return string;
  }

  private ZipEntry createEntry(Customer customer, LokalisierungModul modul, LokalisierungLocale lokalisierungLocale, String suffix) {
    return new ZipEntry(createPath(customer, modul) + "/" + modul.getResourceFile() + createLanguageSuffix(lokalisierungLocale) + suffix);
  }

  private String createPath(Customer customer, LokalisierungModul modul) {
    // ELECT-4004
    if (LokalisierungModul.ENTITIES.equals(modul)) {
      return "elect/base/jar/src/main/resources";
    }

    String result = "";
    String modulString = modul.name().toLowerCase();

    if (modulString.endsWith("_custom")) {
      result += extractCustomerName(customer) + "/";
      modulString = modulString.substring(0, modulString.lastIndexOf("_custom"));
    } else {
      result += "elect/";
    }

    result += modulString;
    result += "/jar/src/main/resources";

    return result;
  }

  private String extractCustomerName(Customer customer) {
    String name = customer.name().toLowerCase();

    if (name.contains("_")) {
      name = name.substring(0, name.indexOf("_"));
    }

    return name;
  }

  private String createLanguageSuffix(LokalisierungLocale lokalisierungLocale) {
    if (LokalisierungLocale.getFuehrendesLocale().equals(lokalisierungLocale)) {
      return "";
    }

    return "_" + lokalisierungLocale.getLocale().getLanguage();
  }

  private Map<LokalisierungModul, Map<LokalisierungLocale, List<Lokalisierung>>> bestimmeLokalisierungenNachModulUndSprache(List<Lokalisierung> lokalisierungenInDb) {
    Map<LokalisierungModul, Map<LokalisierungLocale, List<Lokalisierung>>> result = new HashMap<>();

    List<LokalisierungModul> module = LokalisierungModul.getModuleForCustomer(currentCustomer.getCustomer());

    for (LokalisierungModul lokalisierungModul : module) {
      Map<LokalisierungLocale, List<Lokalisierung>> localeMap = new HashMap<>();
      result.put(lokalisierungModul, localeMap);
      for (LokalisierungLocale lokalisierungLocale : currentCustomer.getAlleUnterstuetztenSprachen()) {
        List<Lokalisierung> lokalisierungen = new ArrayList<>();
        localeMap.put(lokalisierungLocale, lokalisierungen);
      }
    }

    for (Lokalisierung lokalisierung : lokalisierungenInDb) {
      result.computeIfAbsent(lokalisierung.getModul(), x->new HashMap<>()).get(lokalisierung.getLocale()).add(lokalisierung);
    }

    return result;
  }
}
