
package de.ivu.elect.business.gebietsbaum.control;

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

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

import javax.annotation.Nullable;

import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
import de.ivu.elect.business.gebietsbaum.entity.GebietPK;
import de.ivu.elect.jee.presentation.search.AbstractSearchParameters;

public abstract class AbstractFilterByGebiet<E> {

  public AbstractFilterByGebiet() {
    super();
  }

  public List<E> filterByGebiet(List<E> entitiesMitGebiet, FilterByGebietParameter filterByGebietParameter) {
    if (filterByGebietParameter.isIncludeUntergebiete()) {
      return filterByGebietIncludeUntergebiete(entitiesMitGebiet, filterByGebietParameter.getAttachedGebiet(),
          filterByGebietParameter.getUntergebietNameNummer());
    } else {
      return filterByGebietWithoutUntergebiete(entitiesMitGebiet, filterByGebietParameter.getAttachedGebiet());
    }
  }

  private List<E> filterByGebietWithoutUntergebiete(List<E> entitiesMitGebiet, Gebiet attachedGebiet) {
    if (attachedGebiet == null) {
      return entitiesMitGebiet;
    }

    List<E> result = new ArrayList<>(entitiesMitGebiet.size());

    for (E entity : entitiesMitGebiet) {
      if (isGleichesGebiet(attachedGebiet, entity)) {
        result.add(entity);
      }
    }

    return result;
  }

  private List<E> filterByGebietIncludeUntergebiete(List<E> entitiesMitGebiet, Gebiet attachedGebiet,
      @Nullable String untergebietNameNummer) {
    if (attachedGebiet == null) {
      return entitiesMitGebiet;
    }

    Set<GebietPK> erlaubteGebiet = getGebietsHierarchieInklusiveAttachedGebiet(attachedGebiet);

    List<E> result = new ArrayList<>(entitiesMitGebiet.size());

    for (E entity : entitiesMitGebiet) {
      if (isNameNummerPassend(entity, untergebietNameNummer) && isGebietInGebietshierarchie(erlaubteGebiet, entity)) {
        result.add(entity);
      }
    }

    return result;
  }

  protected abstract boolean isNameNummerPassend(E entity, @Nullable String untergebietNameNummer);

  protected boolean isNameNummerPassend(Gebiet gebiet, String untergebietNameNummer) {
    if (gebiet == null) {
      return false;
    }

    if (isBlank(untergebietNameNummer)) {
      return true;
    }

    String untergebietNameNummerTrimmed = untergebietNameNummer.trim().toLowerCase();

    if (untergebietNameNummerTrimmed.endsWith(AbstractSearchParameters.WILDCARD_CHARACTER)) {
      untergebietNameNummerTrimmed = untergebietNameNummerTrimmed.substring(0, untergebietNameNummerTrimmed.length() - 1);
    }

    String nummer = gebiet.getNummer();
    String name = gebiet.getName();

    if (nummer != null) {
      nummer = nummer.toLowerCase();
    }

    if (name != null) {
      name = name.toLowerCase();
    }

    return nummer != null && nummer.startsWith(untergebietNameNummerTrimmed)
        || name != null && name.startsWith(untergebietNameNummerTrimmed);
  }

  private Set<GebietPK> getGebietsHierarchieInklusiveAttachedGebiet(Gebiet gebiet) {
    Set<GebietPK> result = new HashSet<>();
    result.add(gebiet.getPk());

    for (Gebiet gebietAusBaum : gebiet.getInGebietshierarchieUntergeordneteGebiete()) {
      result.add(gebietAusBaum.getPk());
    }

    return result;
  }

  protected abstract boolean isGleichesGebiet(Gebiet gebiet, E entityMitGebiet);

  protected abstract boolean isGebietInGebietshierarchie(Set<GebietPK> erlaubteGebiete, E entityMitGebiet);
}
