package de.ivu.elect.presentation.gebietsbaum;

import java.io.Serializable;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import javax.enterprise.event.Observes;
import javax.enterprise.event.Reception;
import javax.inject.Inject;

import org.apache.commons.lang3.StringUtils;
import org.apache.deltaspike.core.api.scope.ViewAccessScoped;

import de.ivu.elect.business.gebietsbaum.control.GebietByFormattedNummerComparator;
import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
import de.ivu.elect.business.gebietsbaum.entity.GebietPK;
import de.ivu.elect.business.wahl.entity.Wahl;
import de.ivu.elect.business.wahl.entity.Wahltermin;
import de.ivu.elect.presentation.gebietsbaum.tree.GebietsbaumAutoCompleteGebietsCacheLoader;
import de.ivu.elect.presentation.gebietsbaum.tree.GebietsbaumAutoCompleteGebietsCacheStoreData;
import de.ivu.elect.presentation.security.GebietSecurity;

@ViewAccessScoped
public class GebietsbaumAutoCompleteGebietsCacheStore implements Serializable {

  private static final long serialVersionUID = 1L;

  @Inject
  private transient GebietSecurity gebietSecurity;

  @Inject
  private transient GebietsbaumAutoCompleteGebietsCacheLoader gebietsbaumAutoCompleteGebietsCacheLoader;

  @Inject
  protected transient GebietByFormattedNummerComparator gebietByNummerComparator;

  private GebietsbaumAutoCompleteGebietsCacheStoreData gebietsbaumAutoCompleteGebietsCacheStoreData;

  public GebietsbaumAutoCompleteGebietsCacheStoreData getGebietsbaumAutoCompleteGebietsCacheStoreData(Wahltermin wahltermin, Wahl wahl) {
    // TOOD global cachen?
    if (gebietsbaumAutoCompleteGebietsCacheStoreData == null) {
      gebietsbaumAutoCompleteGebietsCacheStoreData = init(wahltermin, wahl);
    }

    return gebietsbaumAutoCompleteGebietsCacheStoreData;
  }

  private GebietsbaumAutoCompleteGebietsCacheStoreData init(Wahltermin wahltermin, Wahl wahl) {

    List<Gebiet> gebiete = filterErlaubteGebiete(gebietsbaumAutoCompleteGebietsCacheLoader.ladeAlleAktuellenGebiete(wahltermin, wahl));
    gebiete.sort(gebietByNummerComparator);
    Map<String, Set<GebietPK>> gebieteNachNamen = new HashMap<>(gebiete.size() * 2 + 1);
    Map<String, Set<GebietPK>> gebieteNachNummern = new HashMap<>(gebiete.size() * 2 + 1);
    for (Gebiet gebiet : gebiete) {
      putLowerIfNotNullEmpty(gebiet.getName(), gebiet, gebieteNachNamen);
      putLowerIfNotNullEmpty(gebiet.getDarstellung(), gebiet, gebieteNachNamen);
      putLowerIfNotNullEmpty(gebiet.getNummer(), gebiet, gebieteNachNummern);
    }

    Map<GebietPK, Long> reihenfolge = IntStream.range(0, gebiete.size()).boxed()
        .collect(Collectors.toMap(i -> gebiete.get(i).getPk(), i -> (long) i, (a, b) -> b, () -> new HashMap<>(gebiete.size() * 2 + 1)));

    return new GebietsbaumAutoCompleteGebietsCacheStoreData(gebieteNachNamen, gebieteNachNummern, reihenfolge);
  }

  private List<Gebiet> filterErlaubteGebiete(List<Gebiet> gebiets) {
    return gebietSecurity.getEinsehenGebietSecurity().filterGebiete(gebiets);
  }

  private void putLowerIfNotNullEmpty(String string, Gebiet gebiet, Map<String, Set<GebietPK>> gebieteNachString) {
    if (StringUtils.isBlank(string)) {
      return;
    }
    gebieteNachString.computeIfAbsent(string.toLowerCase(), x -> new HashSet<>()).add(gebiet.getPk());
  }

  public void onWahlSelectChangeEvent(@Observes(notifyObserver = Reception.IF_EXISTS) de.ivu.elect.business.wahl.boundary.WahlSelectChangeEvent event) {
    reset();
  }

  public void onWahlterminSelectChangeEvent(
      @Observes(notifyObserver = Reception.IF_EXISTS) de.ivu.elect.business.wahl.boundary.WahlterminSelectChangeEvent event) {
    reset();
  }

//  public void onGebietChangeEvent(
//      @Observes(notifyObserver = Reception.IF_EXISTS) GebietChangeEvent event) {
//    reset();
//  }

  private void reset() {
    gebietsbaumAutoCompleteGebietsCacheStoreData = null;
  }

}
