package de.ivu.elect.business.exchange.boundary;

import java.util.HashMap;
import java.util.Map;

import javax.faces.convert.ConverterException;


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

public class ExchangeEnumConverter<E extends Enum<E>> implements ExchangeTypeConverter {

  private final Class<E> enumType;

  private final Map<E, String> enumToAliasMap;

  private final Map<String, E> aliasToEnumMap;

  public ExchangeEnumConverter(Class<E> enumType, Map<E, String> enumToAliasMap, Map<String, E> aliasToEnumMap) {
    this.enumType = enumType;
    this.enumToAliasMap = enumToAliasMap;
    this.aliasToEnumMap = aliasToEnumMap;
  }

  public ExchangeEnumConverter (Class<E> enumType) {
    this(enumType, new HashMap<E, String>(), new HashMap<String, E>());
  }

  /**
   * copied from org.jsefa.common.converter
   * modified by WAA
   */
  public ExchangeEnumConverter(Class<E> enumType, Map<E, String> enumToAliasMap) {
    super();

    this.enumType = enumType;
    this.enumToAliasMap = enumToAliasMap;
    this.aliasToEnumMap = createAliasToEnumMapFromEnumToAliasMap(enumToAliasMap);
  }

  public ExchangeEnumConverter(Class<E> enumType, HashMap<String, E> aliasToEnumMap) {
    super();

    this.enumType = enumType;
    this.enumToAliasMap =  createEnumToAliasMapFromAliasToEnumMap(aliasToEnumMap);
    this.aliasToEnumMap = aliasToEnumMap;
  }

  private Map<String, E> createAliasToEnumMapFromEnumToAliasMap(Map<E, String> enumToAliasMap) {
    Map<String, E> aliasToEnumMap = new HashMap<String, E>();
    for (Map.Entry<E, String> entry : enumToAliasMap.entrySet()) {
      aliasToEnumMap.put(entry.getValue(), entry.getKey());
    }
    return aliasToEnumMap;
  }

  private Map<E, String> createEnumToAliasMapFromAliasToEnumMap(Map<String, E> aliasToEnumMap) {
    Map<E, String> enumToAliasMap = new HashMap<E, String>();
    for (Map.Entry<String, E> entry : aliasToEnumMap.entrySet()) {
      enumToAliasMap.put(entry.getValue(), entry.getKey());
    }
    return enumToAliasMap;
  }

  /**
   * copied from org.jsefa.common.converter
   * modified by WAA
   */
  @Override
  public E fromString(String value) {
    if (isBlank(value)) {
      return null;
    }

    String valueNormalized = normalizeSpace(value);

    E asEnum = findInAliasToEnumMapCaseInsensitive(valueNormalized);
    if (asEnum != null) {
      return asEnum;
    }

    for (E each: this.enumType.getEnumConstants()) {
      if (each.name().compareToIgnoreCase(valueNormalized) == 0) {
        return each;
      }
    }

    throw new ConverterException("Unknown enum value: " + valueNormalized);
  }

  private E findInAliasToEnumMapCaseInsensitive(String value) {
    return this.aliasToEnumMap.entrySet().stream()
        .filter(entry -> entry.getKey().equalsIgnoreCase(value))
        .findFirst()
        .map(Map.Entry::getValue)
        .orElse(null);

  }

  /**
   * copied from org.jsefa.common.converter
   * modified by WAA
   */
  @Override
  @SuppressWarnings("unchecked")
  public String toString(Object value) {
    if (value == null) {
      return null;
    }

    E asEnum = (E) value;

    String alias = this.enumToAliasMap.get(asEnum);
    if (alias != null) {
      return alias;
    }

    return asEnum.name();
  }

}
