
package de.ivu.elect.business.gruppeimgebiet.boundary;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import javax.ejb.Stateless;
import javax.inject.Inject;

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

import de.ivu.elect.business.gebietsbaum.boundary.AbstractGebietDAO;
import de.ivu.elect.business.gebietsbaum.boundary.GebietDAO;
import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
import de.ivu.elect.business.gruppe.entity.GruppeArt;
import de.ivu.elect.business.gruppeimgebiet.control.GruppeImGebietByRelativePositionComparator;
import de.ivu.elect.business.gruppeimgebiet.entity.GruppeImGebiet;
import de.ivu.elect.business.gruppeimgebiet.entity.ReihenfolgeBestaetigenOption;
import de.ivu.elect.business.gruppeimgebiet.entity.ReihenfolgeZuruecksetzenOption;

@Stateless
public class GruppeImGebietEdit extends AbstractGruppeImGebietEdit {

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

  @Inject
  private transient GebietDAO gebietDAO;

  @Inject
  private transient GruppeImGebietReihenfolgeBestaetiger gruppeImGebietReihenfolgeBestaetiger;

  @Inject
  private transient ReihenfolgeBestaetigenOptionFuerGebiet reihenfolgeBestaetigenOptionFuerGebiet;

  /**
   * liefert true, wenn die Relative Reihenfolgen konsistent mit den vorgegebenen Reihenfolgen sind...
   */
  public boolean positionenSetzenUndSpeichern(List<GruppeImGebiet> entities, boolean untergebieteAktualisieren) {

    boolean konsistent = true;
    Gebiet gebiet = null;
    Set<GruppeImGebiet> gruppeImGebietAttached = new HashSet<>();
    for (int i = 0; i < entities.size(); i++) {
      GruppeImGebiet gruppeImGebiet = dao.attach(entities.get(i));
      if (gebiet==null) {
        gebiet = gruppeImGebiet.getGebiet();
      }
      if (gruppeImGebiet.getGebiet().equals(gebiet)) {
        gruppeImGebietAttached.add(gruppeImGebiet);
      }
      gruppeImGebiet.setRelativePosition(i + 1L);
      //noinspection ConstantConditions getRelativePosition liefert keine NPE wird davor gesetzt
      konsistent &= gruppeImGebiet.getVorgegebenePosition() == null || gruppeImGebiet.getVorgegebenePosition() <= gruppeImGebiet.getRelativePosition();
    }
    if (gebiet!=null && gebiet.isWurzelgebiet()){
      aktualisiereGruppenReihenfolgen(gruppeImGebietAttached);
    }

    if (gebiet != null && untergebieteAktualisieren) {
      dao.flush();
      ReihenfolgeBestaetigenOption defaultOptionFuerGebiet = reihenfolgeBestaetigenOptionFuerGebiet.createDefaultOptionFuerGebiet(gebiet);
      LOG.info("Bestaetige für Untergebiete: " + defaultOptionFuerGebiet);
      gruppeImGebietReihenfolgeBestaetiger.bestaetigeVorsortierteReihenfolge(gebiet, defaultOptionFuerGebiet);
    }

    return konsistent;
  }

  private void aktualisiereGruppenReihenfolgen(Set<GruppeImGebiet> gruppeImGebietAttached) {
    List<Long> vorhandeneGruppenPositionen = gruppeImGebietAttached.stream()
        .map(gruppeImGebiet -> gruppeImGebiet.getGruppe().getRelativePosition())
        .sorted()
        .collect(Collectors.toList());

    List<GruppeImGebiet> gigSortiert = new ArrayList<>(gruppeImGebietAttached);
    gigSortiert.sort(new GruppeImGebietByRelativePositionComparator());
    for (GruppeImGebiet gruppeImGebiet : gigSortiert) {
      gruppeImGebiet.getGruppe().setRelativePosition(vorhandeneGruppenPositionen.remove(0));
    }

  }

  public void reihenfolgeFuerAusgewaehltesGebietZuruecksetzen(Gebiet gebietNotAttached, ReihenfolgeZuruecksetzenOption reihenfolgeZuruecksetzenOption) {
    if (gebietNotAttached == null) {
      return;
    }

    LOG.info("Setze Reihenfolgen zurück: gebiet=" + gebietNotAttached + ", optionen: " + reihenfolgeZuruecksetzenOption);

    setzePositionenZurueck(bestimmeGruppeImGebiet(gebietNotAttached, reihenfolgeZuruecksetzenOption.isUntergeordneteGebiete(), gebietDAO, dao), reihenfolgeZuruecksetzenOption);
  }

  public static List<GruppeImGebiet> bestimmeGruppeImGebiet(Gebiet gebietNotAttached, boolean untergeordneteGebiete, AbstractGebietDAO gebietDAO, AbstractGruppeImGebietDAO gruppeImGebietDAO) {
    final List<GruppeImGebiet> gruppenImGebiet;
    if (untergeordneteGebiete) {
      Gebiet gebiet = gebietDAO.attach(gebietNotAttached);
      //noinspection ConstantConditions wahl in WUS niemals null an gebiet
      gruppenImGebiet = gruppeImGebietDAO.findByWahl(gebiet.getWahl()).stream()
          .filter(gig -> !GruppeArt.SYSTEM_GRUPPE.equals(gig.getGruppe().getGruppeArt()))
          .filter(gig -> gebiet.isInGebietshierarchieUebergeordnetesGebietVon(gig.getGebiet()))
          .collect(Collectors.toList());
    } else {
      gruppenImGebiet = gruppeImGebietDAO.findByGebietAndUngleichGruppeArtOrderByRelativePosition(gebietNotAttached, GruppeArt.SYSTEM_GRUPPE);
    }
    return gruppenImGebiet;
  }

  private void setzePositionenZurueck(List<GruppeImGebiet> gruppenImGebiet, ReihenfolgeZuruecksetzenOption reihenfolgeZuruecksetzenOption) {
    for (GruppeImGebiet gruppeImGebiet : gruppenImGebiet) {
      if (reihenfolgeZuruecksetzenOption.isRelativePositionenLoeschen()) {
        gruppeImGebiet.setRelativePosition(null);
      }
      if (reihenfolgeZuruecksetzenOption.isVorgegebenePositionenLoeschen()) {
        gruppeImGebiet.setVorgegebenePosition(null);
      }
    }
  }

  public void verschiebePositionNachOben(GruppeImGebiet entity) {
    List<GruppeImGebiet> gruppenImGebiet = dao.findByGebietAndUngleichGruppeArtOrderByRelativePosition(entity.getGebiet(),
        GruppeArt.SYSTEM_GRUPPE);
    for (int i = 0; i < gruppenImGebiet.size(); i++) {
      GruppeImGebiet gruppeImGebiet = gruppenImGebiet.get(i);
      if (gruppeImGebiet.equals(entity) && gruppeImGebiet.getVorgegebenePosition() == null && i > 0
          && gruppenImGebiet.get(i - 1).getVorgegebenePosition() == null) {
        Long vorherigePosition = gruppenImGebiet.get(i - 1).getRelativePosition();
        gruppenImGebiet.get(i - 1).setRelativePosition(gruppeImGebiet.getRelativePosition());
        gruppeImGebiet.setRelativePosition(vorherigePosition);
      }
    }
  }

  public void verschiebePositionNachUnten(GruppeImGebiet entity) {
    List<GruppeImGebiet> gruppenImGebiet = dao.findByGebietAndUngleichGruppeArtOrderByRelativePosition(entity.getGebiet(),
        GruppeArt.SYSTEM_GRUPPE);
    for (int i = 0; i < gruppenImGebiet.size(); i++) {
      GruppeImGebiet gruppeImGebiet = gruppenImGebiet.get(i);
      if (gruppeImGebiet.equals(entity) && gruppeImGebiet.getVorgegebenePosition() == null && i < gruppenImGebiet.size() - 1) {
        Long nachfolgendePosition = gruppenImGebiet.get(i + 1).getRelativePosition();
        gruppenImGebiet.get(i + 1).setRelativePosition(gruppeImGebiet.getRelativePosition());
        gruppeImGebiet.setRelativePosition(nachfolgendePosition);
      }
    }
  }
}
