
package de.ivu.elect.presentation.gebietsbaum.polygon;

import static de.ivu.elect.business.gebietsbaum.entity.AbstractGebietPolygonPoint.berechneMitteLatLng;
import static java.util.Arrays.asList;
import static org.apache.commons.lang3.StringUtils.isNotBlank;

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

import javax.inject.Inject;
import javax.inject.Named;

import org.apache.deltaspike.core.api.scope.ViewAccessScoped;
import org.primefaces.event.FileUploadEvent;
import org.primefaces.event.map.MarkerDragEvent;
import org.primefaces.event.map.OverlaySelectEvent;
import org.primefaces.event.map.PointSelectEvent;
import org.primefaces.model.file.UploadedFile;
import org.primefaces.model.map.LatLng;
import org.primefaces.model.map.MapModel;
import org.primefaces.model.map.Marker;
import org.primefaces.model.map.Overlay;
import org.primefaces.model.map.Polygon;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import de.ivu.elect.business.datei.entity.FileType;
import de.ivu.elect.business.datei.entity.FileTypeResolver;
import de.ivu.elect.business.gebietsbaum.control.ReadShpFile;
import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
import de.ivu.elect.business.gebietsbaum.entity.GebietPolygon;
import de.ivu.elect.business.gebietsbaum.entity.GebietPolygonPoint;
import de.ivu.elect.business.gebietsbaum.entity.GebietPolygone;
import de.ivu.elect.business.security.UseCasePermission;
import de.ivu.elect.jee.faces.FacesContextUtil;
import de.ivu.elect.jee.faces.FacesMessageUtil;
import de.ivu.elect.jee.faces.upload.UploadedFileWithCanonicalFilename;
import de.ivu.elect.jee.presentation.redirect.Redirect;
import de.ivu.elect.presentation.View;
import de.ivu.elect.presentation.ViewHavingFileUpload;
import de.ivu.elect.presentation.gebietsbaum.shp.ShpFileConfig;
import de.ivu.elect.presentation.hilfe.KeineOnlineHilfe;
import de.ivu.elect.presentation.hilfe.OnlineHilfeId;

@OnlineHilfeId(KeineOnlineHilfe.KEINE_ONLINE_HILFE)
@ViewAccessScoped
@Named(GebietGeometrieZuweisenView.COMPONENT_NAME)
@View(viewId = GebietGeometrieZuweisenView.VIEW_ID, useCase = UseCasePermission.GEBIET_GEOMETRIE_BEARBEITEN)
// Deaktiviert
public class GebietGeometrieZuweisenView extends AbstractGebietGeometrieMapEditView implements ViewHavingFileUpload {

  private static final long serialVersionUID = 1L;

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

  public static final String COMPONENT_NAME = "gebietGeometrieZuweisenView";

  public static final String VIEW_ID = "/gebietsbaum/gebiet-geometrie-zuweisen.xhtml";

  public static final String FARBE_POLYGON_AUS_DATEI = "#F4FA58";

  @Inject
  private transient ReadShpFile readShpFile;

  @Inject
  private transient FileTypeResolver fileTypeResolver;

  private UploadedFile uploadedFile;

  private Map<Polygon, List<Polygon>> verbundenePolygoneAusDatei = new HashMap<>();

  private final Map<Marker, List<Polygon>> polygoneDesMarker = new HashMap<>();

  public GebietGeometrieZuweisenView() {
    super();
  }

  @Override
  public String reset() {
    verbundenePolygoneAusDatei = new HashMap<>();

    return super.reset();
  }

  @Override
  protected MapModel createModel() {
    MapModel geometrieModel = super.createModel();

    if (getGebiet().hasUntergeordneteGebiete()) {

      // Hmm?

    } else {
      List<GebietPolygonPoint> polygoneDesGebietes = new ArrayList<>();

      GebietPolygone gebietPolygone = gebietGeometrieEdit.getGebietPolygone(getGebiet());

      for (GebietPolygon gebietPolygon : gebietPolygone.getPolygonList()) {
        Polygon polygon = addPolygonDesAktuellenGebietes();

        polygoneDesGebietes.addAll(gebietPolygon.getGebietPolygonPointListReadOnly());

        for (GebietPolygonPoint gebietPolygonPoint : gebietPolygon.getGebietPolygonPointListReadOnly()) {
          polygon.getPaths().add(new LatLng(gebietPolygonPoint.getLatitude(), gebietPolygonPoint.getLongitude()));
        }
      }

      for (GebietPolygone polygone : ladeAusDatei()) {
        List<GebietPolygonPoint> shapesAusDatei = new ArrayList<>();
        List<Polygon> polygoneProGebietAusDatei = new ArrayList<>();

        for (GebietPolygon gebietPolygon : polygone.getPolygonList()) {
          if (gebietPolygon.getAnzahlPunkte() > 2) {
            shapesAusDatei.addAll(gebietPolygon.getGebietPolygonPointListReadOnly());

            Polygon polygon = createPolygon("", FARBE_POLYGON_AUS_DATEI);
            geometrieModel.addOverlay(polygon);
            polygoneProGebietAusDatei.add(polygon);
            verbundenePolygoneAusDatei.put(polygon, polygoneProGebietAusDatei);
            for (GebietPolygonPoint latLng : gebietPolygon.getGebietPolygonPointListReadOnly()) {
              LatLng latLng1 = new LatLng(latLng.getLatitude(), latLng.getLongitude());
              polygon.getPaths().add(latLng1);
            }
          }
        }

        if (isNotBlank(polygone.getName()) && !shapesAusDatei.isEmpty()) {
          Marker mittelPunktMarker = new Marker(berechneMitteLatLng(shapesAusDatei), polygone.getName(), "", "http://maps.google.com/mapfiles/ms/micons/blue-dot.png");
          mittelPunktMarker.setDraggable(false);

          getMapModel().addOverlay(mittelPunktMarker);

          polygoneDesMarker.put(mittelPunktMarker, polygoneProGebietAusDatei);
        }
      }

      if (isMindestensEinPolgonVorhanden(polygoneDesGebietes, new ArrayList<List<GebietPolygonPoint>>())) {
        setCenter(berechneMitte(polygoneDesGebietes, new ArrayList<List<GebietPolygonPoint>>()));
      }
    }

    return geometrieModel;
  }

  private List<GebietPolygone> cachedAusDatei;

  private List<GebietPolygone> ladeAusDatei() {
    List<GebietPolygone> result = new ArrayList<>();

    LOG.info("ladeAusDatei, uploadedFile=" + uploadedFile + ", cachedAusDatei=" + cachedAusDatei);

    if (uploadedFile != null) {
      if (cachedAusDatei == null) {
        try (InputStream is = uploadedFile.getInputStream()) {
          result.addAll(readShpFile.readFile(is, ShpFileConfig.NO_CONFIG));
        } catch (Exception e) {
          FacesMessageUtil.addErrorMessage("gebiet.geometrie.file.error", e.getMessage());
        }
      } else {
        result = cachedAusDatei;
      }
    }

    cachedAusDatei = result;

    return result;
  }

  @Override
  protected void onPointSelectInternal(PointSelectEvent event) {
    // nichts zu tun...
  }

  @Override
  protected void onMarkerDragInternal(MarkerDragEvent event) {
    // nichts zu tun...
  }

  @Override
  @Redirect
  public String save() {
    return VIEW_ID;
  }

  @Override
  @Redirect
  public String delete() {
    throw new UnsupportedOperationException("This is NOT implemented");
  }

  @Override
  public void handleFileUpload(FileUploadEvent event) {
    setUploadedFile(new UploadedFileWithCanonicalFilename(event.getFile()));
  }

  @Override
  public String getFileUploadLabel() {
    return getMessage("datei.import.edit.action.upload.choose", fileTypeResolver.formatFileTypes(getFileUploadFileTypes()));
  }

  @Override
  public String getFileUploadAllowTypes() {
    return fileTypeResolver.getFileTypesHtmlRegEx(getFileUploadFileTypes());
  }

  @Override
  public String getFileUploadAccept() {
    return fileTypeResolver.getFileTypesHtmlAccept(getFileUploadFileTypes());
  }

  @Override
  public List<FileType> getFileUploadFileTypes() {
    return asList(FileType.ZIP);
  }

  public UploadedFile getUploadedFile() {
    return uploadedFile;
  }

  public void setUploadedFile(UploadedFile uploadedFile) {
    this.uploadedFile = uploadedFile;

    cachedAusDatei = null;

    reset();

    FacesContextUtil.redirectToView(VIEW_ID);
  }

  @Override
  protected void save(Gebiet entity) {
    // nichts zu tun...
  }

  @Override
  protected String getViewId() {
    return VIEW_ID;
  }

  public void onPolygonSelect(OverlaySelectEvent event) {
    if (isRechtZumBearbeitenAufGebietVorhanden(getEntity())) {
      Overlay overlay = event.getOverlay();

      List<Polygon> polygons = null;

      if (verbundenePolygoneAusDatei.containsKey(overlay)) {
        polygons = verbundenePolygoneAusDatei.get(overlay);
      } else if (polygoneDesMarker.containsKey(overlay)) {
        polygons = polygoneDesMarker.get(overlay);
      }

      if (polygons != null) {
        gebietGeometrieEdit.save(getGebiet(), convertPolygonPath(polygons));

        reset();

        FacesMessageUtil.addInfoMessage("gebiet.geoemetrie.edit.save.gebiet", "gebiet.geoemetrie.edit.save.gebiet", getGebietName());
        FacesContextUtil.redirectToView(getViewId());
      }
    }
  }

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

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

  @Override
  public boolean isBtnEditRendered() {
    return hasPermission(UseCasePermission.GEBIET_GEOMETRIE_BEARBEITEN);
  }

  @Override
  public boolean isBtnSVGRendered() {
    return hasPermission(UseCasePermission.GEBIET_GEOMETRIE_ANZEIGEN);
  }
}
