
package de.ivu.elect.exchange.gebietsbaum;

import static de.ivu.elect.business.security.UseCasePermission.GEBIET_GEOMETRIE_IMPORTIEREN_GENERIC_GROOVY;

import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;

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

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

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.gebietsbaum.entity.GebietGeometrieImportContext;
import de.ivu.elect.business.gebietsbaum.entity.GebietPolygonPoint;
import de.ivu.elect.business.security.control.ElectCommonPrincipal;
import de.ivu.elect.business.system.control.SkriptRunner;
import de.ivu.elect.core.exchange.gebietsbaum.AbstractEsriGebietGeometrieImportMapper;
import de.ivu.elect.jee.exchange.Violations;
import de.ivu.elect.jee.faces.Messages;
import de.ivu.elect.jee.presentation.dynamicform.FormBuilder;
import de.ivu.elect.jee.presentation.dynamicform.LambdaBooleanProperty;
import de.ivu.elect.jee.presentation.dynamicform.LambdaStringProperty;
import de.ivu.elect.jee.presentation.dynamicform.PropertyType;
import de.ivu.elect.jee.presentation.dynamicform.ReadOnlyStringProperty;
import de.ivu.elect.presentation.gebietsbaum.shp.ShpFileConfig;

public class GroovyGenericGebietGeometrieImportMapper extends AbstractEsriGebietGeometrieImportMapper {

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

  public static final String DISABLE_LOGGING_KEY = "AbstractEsriGebietGeometrieImportMapper.DISABLE_LOGGING_KEY";

  public static final String GEBIETSART_SKRIPT_KEY = "AbstractEsriGebietGeometrieImportMapper.GEBIETSART_SKRIPT_KEY";

  public static final String GEBIETSNUMMER_SKRIPT_KEY = "AbstractEsriGebietGeometrieImportMapper.GEBIETSNUMMER_SKRIPT_KEY";

  public static final String LOGGER_KEY = "AbstractEsriGebietGeometrieImportMapper.LOGGER";

  public static final String GEBIET_GEOMETRIE_IMPORT_FILE_FORMAT = "gebiet.geometrie.import.file.format.generic.groovy";

  @Inject
  private transient ElectCommonPrincipal electCommonPrincipal;

  @Inject
  private transient SkriptRunner skriptRunner;

  @Inject
  private transient Messages messages;

  @Override
  protected ShpFileConfig createConfig(GebietGeometrieImportContext gebietGeometrieImportContext) {
    Consumer<String> consumer = gebietGeometrieImportContext.get(LOGGER_KEY, Consumer.class);

    String skriptGebietsart = gebietGeometrieImportContext.get(GEBIETSART_SKRIPT_KEY, String.class);
    String skriptGebietsnummer = gebietGeometrieImportContext.get(GEBIETSNUMMER_SKRIPT_KEY, String.class);

    LOG.info(electCommonPrincipal.getNameFuerProtokollierung() + " - Skript: " + skriptGebietsart);
    LOG.info(electCommonPrincipal.getNameFuerProtokollierung() + " - Skript: " + skriptGebietsnummer);

    return new ShpFileConfig() {

      @Nullable
      @Override
      public String getGebietsartFuerShpUndDBFEintrag(String shpId, String filename, Map<String, String> entriesNachSpaltenname, Map<Integer, String> entriesNachSpaltenidex) {
        return executeSkript(skriptGebietsart, shpId, filename, entriesNachSpaltenname, entriesNachSpaltenidex, consumer);
      }

      @Nullable
      @Override
      public String getNummerFuerDBFEintrag(String shpId, String filename, Map<String, String> entriesNachSpaltenname, Map<Integer, String> entriesNachSpaltenindex) {
        return executeSkript(skriptGebietsnummer, shpId, filename, entriesNachSpaltenname, entriesNachSpaltenindex, consumer);
      }

      @Override
      public boolean isReturnGebieteOhneNummer() {
        return false;
      }

      @Override
      public boolean isReturnGebieteOhneGebietsart() {
        return false;
      }

      @Override
      public void debug(String info) {
        if (consumer != null) {
          consumer.accept(info);
        }
      }
    };
  }

  private String executeSkript(String skript, String shpId, String filename, Map<String, String> entriesNachSpaltenname, Map<Integer, String> entriesNachSpaltenindex, Consumer<String> consumer) {
    if (StringUtils.isBlank(skript)) {
      return null;
    }
    Map<String, Object> bindings = new HashMap<>();

    Consumer<String> debug = s -> {
      if (consumer != null) {
        consumer.accept(s);
      }
    };

    bindings.put("shpId", shpId);
    bindings.put("filename", filename);
    bindings.put("entriesNachSpaltenname", entriesNachSpaltenname);
    bindings.put("entriesNachSpaltenindex", entriesNachSpaltenindex);
    bindings.put("debug", debug);

    Object result = skriptRunner.run(skript, bindings);
    if (result == null) {
      return null;
    }
    return result.toString();
  }

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

  @Override
  public List<GebietPolygonPoint> deserialize(InputStream is, GebietGeometrieImportContext gebietGeometrieImportContext, FileKonfiguration fileKonfiguration) throws MappingException {
    setLogger(gebietGeometrieImportContext, null);
    return super.deserialize(is, gebietGeometrieImportContext, fileKonfiguration);
  }

  @Override
  protected void validate(InputStream is, GebietGeometrieImportContext gebietGeometrieImportContext, Violations violations) {
    Consumer<String> logger = null;
    if (!Boolean.TRUE.equals(gebietGeometrieImportContext.get(DISABLE_LOGGING_KEY, Boolean.class))) {
      logger = s -> violations.addInfo("gebiet.geometrie.generic.logger", s);
    }
    setLogger(gebietGeometrieImportContext, logger);
    try {
      super.validate(is, gebietGeometrieImportContext, violations);
    } finally {
      setLogger(gebietGeometrieImportContext, null);
    }

  }

  private void setLogger(GebietGeometrieImportContext gebietGeometrieImportContext, Consumer<String> logger) {
    gebietGeometrieImportContext.put(LOGGER_KEY, logger);
  }

  @Override
  public boolean isAcceptContext(GebietGeometrieImportContext context) {
    try {
      return super.isAcceptContext(context) && context.hasWahltermin() && skriptRunner.isSkriptEngineVerfuegbar()
          && electCommonPrincipal.hasUseCasePermission(GEBIET_GEOMETRIE_IMPORTIEREN_GENERIC_GROOVY);
    } catch (Exception e) {
      return false; // Hintergrungprozesse und Principal...
    }
  }

  @Override
  public void addProperties(FormBuilder formBuilder, GebietGeometrieImportContext context, @Nullable Datei datei) {
    formBuilder.addProperty(new LambdaBooleanProperty(messages.getMessageForMessageKey("gebiet.geometrie.generic.disable.debug"), true, b -> context.put(DISABLE_LOGGING_KEY, b),
        () -> context.get(DISABLE_LOGGING_KEY, Boolean.class)));

    if (!context.containsKey(GEBIETSART_SKRIPT_KEY)) {
      context.put(GEBIETSART_SKRIPT_KEY, messages.getMessageForMessageKey("gebiet.geometrie.generic.skript.gebietsart.default"));
    }

    if (!context.containsKey(GEBIETSNUMMER_SKRIPT_KEY)) {
      context.put(GEBIETSNUMMER_SKRIPT_KEY, messages.getMessageForMessageKey("gebiet.geometrie.generic.skript.gebietsnummer.default"));
    }

    formBuilder.addProperty(new ReadOnlyStringProperty(messages.getMessageForMessageKey("gebiet.geometrie.generic.variables"),
        "String skript; String shpId; String filename; Map<String, String> entriesNachSpaltenname; Map<Integer, String> entriesNachSpaltenindex;"));

    formBuilder.addProperty(new ReadOnlyStringProperty(messages.getMessageForMessageKey("gebiet.geometrie.generic.debug"), "debug.accept(<String>)"));

    LambdaStringProperty gebietsartProperty = new LambdaStringProperty(messages.getMessageForMessageKey("gebiet.geometrie.generic.skript.gebietsart"), true, s -> context.put(GEBIETSART_SKRIPT_KEY, s),
        () -> context.get(GEBIETSART_SKRIPT_KEY, String.class), 0, Integer.MAX_VALUE);

    gebietsartProperty.setType(PropertyType.INPUT_TEXTAREA);

    formBuilder.addProperty(gebietsartProperty);
    LambdaStringProperty gebietsnummerProperty = new LambdaStringProperty(messages.getMessageForMessageKey("gebiet.geometrie.generic.skript.gebietsnummer"), true,
        s -> context.put(GEBIETSNUMMER_SKRIPT_KEY, s), () -> context.get(GEBIETSNUMMER_SKRIPT_KEY, String.class), 0, Integer.MAX_VALUE);
    gebietsnummerProperty.setType(PropertyType.INPUT_TEXTAREA);
    formBuilder.addProperty(gebietsnummerProperty);
  }
}
