
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 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.WindowScoped;

import de.ivu.elect.business.gebietsbaum.boundary.GebietChangeEvent;
import de.ivu.elect.business.gebietsbaum.boundary.GebietExtendedDAO;
import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
import de.ivu.elect.business.wahl.boundary.WahlterminSessionSelect;
import de.ivu.elect.presentation.security.GebietSecurity;


import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNumeric;

@WindowScoped
public class GebietsbaumAutoCompleteGebietsCache implements Serializable {


  @Inject
  WahlterminSessionSelect wahlterminSessionSelect;

  @Inject
  protected GebietExtendedDAO gebietExtendedDAO;

  private final HashMap<String, Set<Gebiet>> gebieteNachNamen = new HashMap<>();

  private final HashMap<String, Set<Gebiet>> gebieteNachNummern =  new HashMap<>();

  private boolean initialized = false;

  @Inject
  GebietSecurity gebietSecurity;

  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() {
    gebieteNachNamen.clear();
    gebieteNachNummern.clear();
    initialized = false;
  }

  private void init() {
    if (initialized) {
      return;
    }
    if (!isSuchparameterOk()) {
      return;
    }
    for (Gebiet gebiet : filterErlaubteGebiete(ladeAlleAktuellenGebiete())) {
      putLowerIfNotNullEmpty(gebiet.getName(), gebiet, gebieteNachNamen);
      putLowerIfNotNullEmpty(gebiet.getDarstellung(), gebiet, gebieteNachNamen);
      putLowerIfNotNullEmpty(gebiet.getNummer(), gebiet, gebieteNachNummern);
    }
    initialized = true;
  }

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

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

  protected boolean isSuchparameterOk() {
    return wahlterminSessionSelect.isSelected();
  }

  protected List<Gebiet> ladeAlleAktuellenGebiete() {
    return gebietExtendedDAO.findByWahltermin(wahlterminSessionSelect.getEntity());
  }


  public Set<Gebiet> findeGebiet(String value) {
    if (isBlank(value)) {
      return new HashSet<>();
    }
    init();

    if (isNumeric(value)) {
      return findGebietByNummer(value);
    }

    return findGebietByName(value);
  }

  private Set<Gebiet> findGebietByName(String value) {
    return findeGebietByString(value, gebieteNachNamen);
  }

  protected Set<Gebiet> findGebietByNummer(String value) {
    return findeGebietByString(value, gebieteNachNummern);
  }

  private Set<Gebiet> findeGebietByString(String value, Map<String, Set<Gebiet>> gebieteNachString) {
    Set<Gebiet> result = new HashSet<>();
    String valueLowerCase = value.trim().toLowerCase();
    String[] valueSplitted = valueLowerCase.split(" ");
    for (String string : gebieteNachString.keySet()) {
      boolean contains = true;
      for (String valuePart : valueSplitted) {
        if (!string.contains(valuePart)) {
          contains = false;
          break;
        }
      }
      if (contains) {
        result.addAll(gebieteNachString.get(string));
      }
    }
    return result;
  }

}
