package de.ivu.elect.core.exchange.gebietsbaum;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import de.ivu.elect.business.exchange.control.FileKonfiguration;
import de.ivu.elect.business.exchange.control.MappingException;
import de.ivu.elect.business.gebietsbaum.control.ReadShpFile;
import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
import de.ivu.elect.business.gebietsbaum.entity.GebietGeometrieImportContext;
import de.ivu.elect.business.gebietsbaum.entity.GebietPolygon;
import de.ivu.elect.business.gebietsbaum.entity.GebietPolygonPoint;
import de.ivu.elect.business.gebietsbaum.entity.GebietPolygonPointArt;
import de.ivu.elect.business.gebietsbaum.entity.GebietPolygone;
import de.ivu.elect.business.gebietsbaum.entity.GebietTK;
import de.ivu.elect.jee.exchange.Violations;
import de.ivu.elect.presentation.gebietsbaum.shp.ShpFileConfig;
import de.ivu.elect.was.exchange.gebietsbaum.GebietGeometrieImportMapper;

/**
 *  on 22.01.2016.
 */
@RequestScoped
public abstract class AbstractEsriGebietGeometrieImportMapper implements GebietGeometrieImportMapper {

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

  @Inject
  private transient ReadShpFile readShpFile;

  @Override
  public List<GebietPolygonPoint> deserialize(InputStream is, GebietGeometrieImportContext gebietGeometrieImportContext, FileKonfiguration fileKonfiguration) throws MappingException {
    try {
      return mappe(readShpFile.readFile(is, createConfig(gebietGeometrieImportContext)));
    } catch (Exception e) {
      throw new MappingException("Fehler beim Lesen der Shape-Files: " + e.getMessage(), e);
    }
  }

  @Override
  public Violations validateInputFormat(InputStream is, GebietGeometrieImportContext gebietGeometrieImportContext, FileKonfiguration fileKonfiguration) {
    Violations violations = new Violations();
    validate(is, gebietGeometrieImportContext, violations);

    return violations;
  }

  protected void validate(InputStream is, GebietGeometrieImportContext gebietGeometrieImportContext, Violations violations) {
    try {
      tryRun(is, createConfig(gebietGeometrieImportContext));
    } catch (Exception e) {
      violations.addError("gebiet.geometrie.import.fehler", e.getMessage());
      LOG.info("Fehler beim Lesen der Shape-Files", e);
    }
  }

  protected void tryRun(InputStream is, ShpFileConfig config) throws Exception {
    readShpFile.readFile(is, config);
  }

  protected abstract ShpFileConfig createConfig(GebietGeometrieImportContext gebietGeometrieImportContext);

  protected List<GebietPolygonPoint> mappe(List<GebietPolygone> gebietPolygone) {
    List<GebietPolygonPoint> result = new ArrayList<>();

    Map<GebietTK, Long> positionenNachGebieten = new HashMap<>(); // Bei Shapefiles mit mehreren

    for (GebietPolygone gebietPolygon : gebietPolygone) {
      if (!gebietPolygon.getGebietPolygonPoints().isEmpty() /*ELECT-13028*/) {
        List<GebietPolygonPoint> resultFuerPolygon = new ArrayList<>();
        Gebiet gebiet = gebietPolygon.getGebiet();

        long position = positionenNachGebieten.computeIfAbsent(gebiet.getGebietTK(), x -> 1L); // 0 ist der Mittelpunkt
        for (GebietPolygon polygon : gebietPolygon.getPolygonList()) {
          GebietPolygonPointArt art = GebietPolygonPointArt.BEGINN;
          for (GebietPolygonPoint gebietPolygonPoint : polygon.getGebietPolygonPointListReadOnly()) {
            gebietPolygonPoint.setPosition(position++);
            gebietPolygonPoint.setGebietPolygonPointArt(art);
            gebietPolygonPoint.setGebiet(gebiet);
            resultFuerPolygon.add(gebietPolygonPoint);
            if (GebietPolygonPointArt.BEGINN.equals(art)) {
              art = GebietPolygonPointArt.PUNKT;
            }
          }

          positionenNachGebieten.put(gebiet.getGebietTK(), position + 1L);
        }

        GebietPolygonPoint mittelpunkt = gebietPolygon.getMittelpunkt();
        if (gebietPolygon.getMittelpunkt() == null) {
          mittelpunkt = new GebietPolygonPoint();
          mittelpunkt.setLatLng(GebietPolygonPoint.berechneMitteLatLng(resultFuerPolygon));

        }
        mittelpunkt.setPosition(0L);
        mittelpunkt.setGebiet(gebiet);
        mittelpunkt.setGebietPolygonPointArt(GebietPolygonPointArt.MITTELPUNKT);
        resultFuerPolygon.add(mittelpunkt);

        result.addAll(resultFuerPolygon);
      }
    }

    return result;
  }


  @Override
  public boolean isAcceptContext(GebietGeometrieImportContext gebietGeometrieImportContext) {
    return readShpFile.isSupported() && gebietGeometrieImportContext.hasWahltermin();
  }
}
