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

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

import java.util.List;
import java.util.function.Predicate;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.EnumConverter;

import org.slf4j.LoggerFactory;

import de.ivu.elect.util.ClassUtil;

public abstract class AbstractAutoCompleteEnumOptionsProvider<T extends Enum<T>> extends AbstractAutoCompleteOptionsProvider<T> {

  private static final long serialVersionUID = 1L;

  private final Class<T> enumClass;

  private final List<T> enumValues;

  private Converter enumConverter;

  public AbstractAutoCompleteEnumOptionsProvider(List<T> enumValuesSorted) {
    super();

    requireNonEmpty(enumValuesSorted);

    this.enumClass = (Class<T>) ClassUtil.getEnumClass(enumValuesSorted.get(0)); // ELECT-11003
    this.enumValues = enumValuesSorted;
  }

  protected boolean isEmptyOption(Enum<T> enumValue) {
    return getEmptyOption().equals(enumValue);
  }

  protected abstract T getEmptyOption();

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

    if (optionByQuery != null) {
      return optionByQuery;
    }

    return getEmptyOption();
  }

  @Override
  protected List<T> getOptions() {
    return enumValues;
  }

  @Override
  protected Predicate<T> matchesQuery(String query) {
    return enumValue -> startsWithIgnoreCase(getMessage(enumValue), query);
  }

  @Override
  public Converter getConverter() {
    if (enumConverter == null) {
      enumConverter = new EnumConverterExtended(getEnumClass(), getEmptyOption());
    }

    return enumConverter;
  }

  protected Class<T> getEnumClass() {
    return enumClass;
  }

  private static void requireNonEmpty(List< ? extends Enum< ? >> enumValues) {
    if (enumValues.isEmpty()) {
      throw new IllegalArgumentException("Enum muss mindestens einen Wert enthalten!");
    }
  }

  private static final class EnumConverterExtended extends EnumConverter {

    private final Enum< ? > defaultEnumValue;

    private EnumConverterExtended(Class< ? > enumClass, Enum< ? > defaultEnumValue) {
      super(enumClass);

      this.defaultEnumValue = defaultEnumValue;
    }

    public Enum< ? > getDefaultEnumValue() {
      return defaultEnumValue;
    }

    @Override
    public Object getAsObject(FacesContext context, UIComponent component, String value) {
      try {
        return super.getAsObject(context, component, value);
      } catch (Exception logged) {
        LoggerFactory.getLogger(getClass()).warn("Fehler beim Konvertieren zu einem Enum-Wert [value = {}, defaultValue = {}, ex = {}]", value, defaultEnumValue, logged);
      }

      return getDefaultEnumValue();
    }
  }
}
