
package de.ivu.elect.jee.presentation.dynamicform;

import static java.util.Objects.isNull;
import static org.apache.commons.lang3.StringUtils.containsIgnoreCase;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import static org.apache.commons.lang3.StringUtils.trimToNull;

import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import javax.faces.convert.Converter;

import de.ivu.elect.jee.faces.StringEmptyToNullConverter;
import de.ivu.elect.util.CaseAndDiacriticIgnoringComparator;

public abstract class AbstractAutoCompleteStringOptionsProvider<T> extends AbstractAutoCompleteOptionsProvider<String> {

  private static final long serialVersionUID = 1L;

  private static final Converter CONVERTER = new StringEmptyToNullConverter();

  private static final Comparator<String> COMPARATOR = CaseAndDiacriticIgnoringComparator.DEFAULT_INSTANCE;

  private Collection<T> templates;

  private List<String> options;

  public AbstractAutoCompleteStringOptionsProvider() {
    super();
  }

  protected Collection<T> getTemplates() {
    if (isNull(templates)) {
      templates = getTemplatesInternal();
    }

    return templates;
  }

  protected abstract Collection<T> getTemplatesInternal();

  protected abstract Function<T, List<String>> templateToOptions();

  @Override
  public String tryGetOptionByQuery(String query) {
    String optionByQuery = super.tryGetOptionByQuery(query);

    if (isNotBlank(optionByQuery)) {
      return optionByQuery;
    }

    if (isQueryAsOptionEnabled()) {
      return trimToNull(query);
    }
    return null;
  }

  protected boolean isQueryAsOptionEnabled() {
    return true;
  }

  @Override
  protected List<String> getOptions() {
    if (isNull(options)) {
      options = getTemplates().stream().map(templateToOptions()).flatMap(Collection::stream).sorted(getOptionsComparator()).collect(Collectors.toList());
    }

    return options;
  }

  protected Comparator<String> getOptionsComparator() {
    return COMPARATOR;
  }

  protected void resetOptions() {
    templates = null;
    options = null;
  }

  @Override
  protected Predicate< ? super String> matchesQuery(String query) {
    return string -> containsIgnoreCase(string, query);
  }

  @Override
  public Converter getConverter() {
    return CONVERTER;
  }
}
