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) {
      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();
  }
}
