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

import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;

import javax.annotation.Nullable;
import javax.enterprise.event.Observes;
import javax.enterprise.event.Reception;
import javax.inject.Inject;
import javax.inject.Named;

import org.apache.deltaspike.core.api.scope.ViewAccessScoped;

import de.ivu.elect.business.gebietsbaum.boundary.GebietExtendedDAO;
import de.ivu.elect.business.gebietsbaum.boundary.GebietGeometrieEdit;
import de.ivu.elect.business.gebietsbaum.boundary.GebietSelectChangeEvent;
import de.ivu.elect.business.gebietsbaum.boundary.GebietsartExtendedDAO;
import de.ivu.elect.business.gebietsbaum.control.GebietsgeometrieToSVGConverter;
import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
import de.ivu.elect.business.gebietsbaum.entity.Gebietsart;
import de.ivu.elect.business.gebietsbaum.entity.Svg;
import de.ivu.elect.business.gebietsbaum.entity.SvgGebietDarstellung;
import de.ivu.elect.business.gebietsbaum.entity.SvgGebietProperties;
import de.ivu.elect.business.gebietsbaum.entity.SvgGebietPropertiesMitLegende;
import de.ivu.elect.business.gebietsbaum.entity.SvgKonfiguration;
import de.ivu.elect.business.gebietsbaum.entity.SvgLegendeKonfiguration;
import de.ivu.elect.business.security.UseCasePermission;
import de.ivu.elect.jee.presentation.dynamicform.FormBuilder;
import de.ivu.elect.jee.presentation.dynamicform.MessageProperty;
import de.ivu.elect.jee.presentation.redirect.Redirect;
import de.ivu.elect.presentation.View;
import de.ivu.elect.presentation.hilfe.OnlineHilfeId;


import static java.util.Arrays.asList;
import static java.util.Comparator.comparing;
import static java.util.stream.Collectors.toList;

@OnlineHilfeId("685")
@ViewAccessScoped
@Named(GebietGeometrieSVGView.COMPONENT_NAME)
@View(viewId = GebietGeometrieSVGView.VIEW_ID, useCase = UseCasePermission.GEBIET_GEOMETRIE_ANZEIGEN)
public class GebietGeometrieSVGView extends AbstractGebietGeometrieView {

  private static final long serialVersionUID = 1L;

  public static final String COMPONENT_NAME = "gebietGeometrieSVGView";

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

  private static final long MAX_TARGET_WIDTH = 800;
  private static final long MAX_TARGET_HEIGHT = 600;

  private SvgGebietDarstellung svgGebietDarstellung = SvgGebietDarstellung.GEBIETSDATEN_ALLER_UNTERGEBIETE;

  private Gebietsart gebietsart;

  @Inject
  private transient GebietsgeometrieToSVGConverter gebietsgeometrieToSVGConverter;

  @Inject
  private transient GebietExtendedDAO gebietExtendedDAO;

  @Inject
  private transient GebietGeometrieEdit gebietGeometrieEdit;

  @Inject
  private transient GebietsartExtendedDAO gebietsartExtendedDAO;

  private Svg svg;

  public GebietGeometrieSVGView() {
    super();
  }

  @Nullable
  @Override
  public String reset() {
    resetSvg();
    return super.reset();
  }

  protected void resetSvg() {
    svg = null;
  }

  @Override
  protected boolean isGebietBearbeitenRechtPruefen() {
    return false;
  }

  @Override
  protected boolean isSavePermissionVorhanden() {
    return false;
  }

  @Override
  public String save() {
    return null;
  }

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

  @Override
  public boolean isBtnCancelRendered() {
    return true;
  }

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

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

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

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

  public Svg getSvg() {
    if (svg == null) {
      svg = createSvg();
    }

    return svg;
  }

  @Override
  public void addProperties(FormBuilder formBuilder, Gebiet gebiet) {
    super.addProperties(formBuilder, gebiet);
    formBuilder.addProperty(new MessageProperty(getMessage("gebiets.geometrie.hinweis")));
  }

  private Svg createSvg() {

    SvgKonfiguration svgKonfiguration = new SvgKonfiguration(MAX_TARGET_WIDTH, MAX_TARGET_HEIGHT,
        new SvgGebietPropertiesMitLegende(
            g -> new SvgGebietProperties(java.util.Objects.equals(g, getGebiet()) ? FARBE_POLYGON_GEBIET : FARBE_POLYGON_UNTERGEORDNET,
                FARBE_STROKE, 1, 0.6d, 0.5d)),
        SvgLegendeKonfiguration.KEINE_LEGENDE);

    return gebietsgeometrieToSVGConverter.createSvg(
      getSvgGebietDarstellung().bestimmeZuBetrachtendeGebiet(getGebiet(), gebietExtendedDAO::findByUebergeordnetesGebiet).stream()
        .filter(this::isGebietsartPassend).collect(toList()),
        gebietGeometrieEdit::getGebietPolygone, svgKonfiguration);
  }

  private boolean isGebietsartPassend(Gebiet g) {
    Gebietsart gebietsart = getGebietsart();
    if (gebietsart == null) {
      return true;
    }
    return gebietsart.equals(g.getGebietsart());
  }

  public void onGebietSelectChangeEvent(@Observes(notifyObserver = Reception.IF_EXISTS) GebietSelectChangeEvent event) {
    reset();
  }

  public SvgGebietDarstellung getSvgGebietDarstellung() {
    return svgGebietDarstellung;
  }

  public void setSvgGebietDarstellung(SvgGebietDarstellung svgGebietDarstellung) {
    if (!svgGebietDarstellung.equals(this.svgGebietDarstellung)) {
      resetSvg();
    }
    this.svgGebietDarstellung = svgGebietDarstellung;
  }

  public List<SvgGebietDarstellung> getGebietsdarstellungen() {
    return asList(SvgGebietDarstellung.values());
  }

  public Gebietsart getGebietsart() {
    return gebietsartExtendedDAO.attach(gebietsart);
  }

  public void setGebietsart(Gebietsart gebietsart) {
    if (!Objects.equals(gebietsart, this.gebietsart)) {
      resetSvg();
    }
    this.gebietsart = gebietsart;
  }

  public List<Gebietsart> getGebietsarten() {
    Gebiet gebiet = getGebiet();

    if (gebiet == null) {
      return Collections.emptyList();
    }

    Set<Gebietsart> gebietsarten = new HashSet<>();

    addGebietsart(gebiet, gebietsarten);

    return gebietsarten.stream().sorted(comparing(Gebietsart::getPosition)).collect(toList());
  }

  private void addGebietsart(Gebiet gebiet, Set<Gebietsart> gebietsarten) {
    gebietsarten.add(gebiet.getGebietsart());
    for (Gebiet untergebiet : gebietExtendedDAO.findByUebergeordnetesGebiet(gebiet)) {
      addGebietsart(untergebiet, gebietsarten);
    }
  }

}
