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

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

import javax.faces.convert.Converter;
import javax.faces.convert.EnumConverter;

import de.ivu.elect.jee.faces.FacesMessageUtil;


import static java.util.Objects.isNull;

public class LambdaEnumSelectManyProperty<E extends Enum<E>> extends AbstractSelectManyProperty<E> {

  private final String defaultLabel;

  private final boolean defaultRequired;

  private final Consumer<Collection<E>> setter;

  private final Supplier<Collection<E>> getter;

  private final Class<E> enumClass;

  private final List<E> values;

  private final boolean showSeparateItems;

  private Function<E, String> itemLabeler = null;

  public LambdaEnumSelectManyProperty(PropertyType propertyType, String defaultLabel, boolean defaultRequired, Consumer<Collection<E>> setter, Supplier<Collection<E>> getter, Class<E> enumClass, List<E> values) {
    this(propertyType, defaultLabel, defaultRequired, setter, getter, enumClass, values, true, e -> true);
  }

  public LambdaEnumSelectManyProperty(PropertyType propertyType, String defaultLabel, boolean defaultRequired, Consumer<Collection<E>> setter, Supplier<Collection<E>> getter, Class<E> enumClass, List<E> values, boolean showSeparateItems) {
    this(propertyType, defaultLabel, defaultRequired, setter, getter, enumClass, values, showSeparateItems, e -> true);
  }

  public LambdaEnumSelectManyProperty(PropertyType propertyType, String defaultLabel, boolean defaultRequired, Consumer<Collection<E>> setter, Supplier<Collection<E>> getter, Class<E> enumClass, List<E> values, Predicate<E> valueFilter) {
    this(propertyType, defaultLabel, defaultRequired, setter, getter, enumClass, values, true, valueFilter);
  }

  public LambdaEnumSelectManyProperty(PropertyType propertyType, String defaultLabel, boolean defaultRequired, Consumer<Collection<E>> setter, Supplier<Collection<E>> getter, Class<E> enumClass, List<E> values, boolean showSeparateItems, Predicate<E> valueFilter) {
    super(propertyType, valueFilter);
    this.defaultLabel = defaultLabel;
    this.defaultRequired = defaultRequired;
    this.setter = setter;
    this.getter = getter;
    this.enumClass = enumClass;
    this.values = values;
    this.showSeparateItems = showSeparateItems;
  }

  @Override
  public Converter getDefaultConverter() {
    return new EnumConverter(getEnumClass());
  }

  @Override
  public String getItemLabel(E value) {
    if (itemLabeler==null) {
      return getMessage(getItemLabelMessageKey(value));
    }
    return itemLabeler.apply(value);
  }

  protected String getItemLabelMessageKey(E value) {
    if (isNull(value)) {
      return "";
    }

    return FacesMessageUtil.getEnumMessageKey(value);
  }

  @Override
  public List<E> getValues() {
    return values.stream().filter(getValueFilter()).collect(Collectors.toList());
  }

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

  @Override
  public String getDefaultLabel() {
    return defaultLabel;
  }

  @Override
  public boolean getDefaultRequired() {
    return defaultRequired;
  }

  @Override
  protected Collection<E> getValueAsCollection() {
    return getter.get();
  }

  @Override
  protected void setValueAsCollection(Collection<E> value) {
    setter.accept(value);
  }

  @Override
  public boolean isShowSeparateItems() {
    return showSeparateItems;
  }

  public void setItemLabeler(Function<E, String> itemLabeler) {
    this.itemLabeler = itemLabeler;
  }
}
