
package de.ivu.elect.presentation.anschrift.form;

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

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
import java.util.regex.Pattern;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.inject.Inject;

import org.apache.deltaspike.core.api.scope.ViewAccessScoped;

import de.ivu.elect.business.geodata.boundary.GeodataPlzExtendedDAO;
import de.ivu.elect.jee.presentation.dynamicform.AbstractAutoCompleteOptionsProvider;

@ViewAccessScoped
public class AnschriftPlzAutoCompleteOptionsProvider extends AbstractAutoCompleteOptionsProvider<AnschriftPlzAutoCompleteOption> {

  private static final long serialVersionUID = 1L;

  @Inject
  private transient GeodataPlzExtendedDAO geodataPlzDao;

  private final List<AnschriftPlzAutoCompleteOption> options;

  private final Map<String, List<AnschriftPlzAutoCompleteOption>> optionsPerPlz;

  private boolean optionsInitialized;

  public AnschriftPlzAutoCompleteOptionsProvider() {
    super();

    this.options = new ArrayList<>();
    this.optionsPerPlz = new HashMap<>();
    this.optionsInitialized = false;
  }

  @Override
  public AnschriftPlzAutoCompleteOption tryGetOptionByQuery(String query) {
    if (optionsPerPlz.containsKey(query)) {
      List<AnschriftPlzAutoCompleteOption> options = optionsPerPlz.get(query);

      if (options.size() == 1) {
        return options.get(0);
      }
    }

    return new AnschriftPlzAutoCompleteOption(query, "", "");
  }

  @Override
  protected List<AnschriftPlzAutoCompleteOption> getOptions() {
    if (!optionsInitialized) {
      geodataPlzDao.findByAll().forEach(geodataPlz -> {
        List<AnschriftPlzAutoCompleteOption> values = new ArrayList<>();

        geodataPlz.getGeodataOrte().forEach(geodataOrt -> {
          values.add(new AnschriftPlzAutoCompleteOption(geodataPlz, geodataOrt));
        });

        optionsPerPlz.put(geodataPlz.getPlz(), values);
        options.addAll(values);
      });

      optionsInitialized = true;
    }

    return options;
  }

  public boolean isDisabled() {
    return getOptions().isEmpty();
  }

  @Override
  protected Predicate< ? super AnschriftPlzAutoCompleteOption> matchesQuery(String query) {
    return anschriftPlzAutoCompleteOption -> startsWithIgnoreCase(anschriftPlzAutoCompleteOption.getPlz(), query);
  }

  @Override
  public Converter getConverter() {
    return new PlzAutoCompleteOptionConverter();
  }

  private static final class PlzAutoCompleteOptionConverter implements Converter {

    private static final String ATTRIBUTE_SEPARATOR = "|";

    private static final String ATTRIBUTE_SEPARATOR_PATTERN = Pattern.quote(ATTRIBUTE_SEPARATOR);

    public PlzAutoCompleteOptionConverter() {
      super();
    }

    @Override
    public String getAsString(FacesContext context, UIComponent component, Object value) {
      if (value instanceof AnschriftPlzAutoCompleteOption) {
        AnschriftPlzAutoCompleteOption anschriftPlzAutoCompleteOption = (AnschriftPlzAutoCompleteOption) value;

        return anschriftPlzAutoCompleteOption.getPlz() + ATTRIBUTE_SEPARATOR + anschriftPlzAutoCompleteOption.getOrtsname() + ATTRIBUTE_SEPARATOR + anschriftPlzAutoCompleteOption.getOrtsteilname();
      }

      return "";
    }

    @Override
    public Object getAsObject(FacesContext context, UIComponent component, String value) {
      if (isNotBlank(value)) {
        String[] values = value.split(ATTRIBUTE_SEPARATOR_PATTERN);

        return new AnschriftPlzAutoCompleteOption(getOrDefault(values, 0, ""), getOrDefault(values, 1, ""), getOrDefault(values, 2, ""));
      }

      return null;
    }

    private String getOrDefault(String[] values, int index, String defaultValue) {
      if (values.length > index) {
        return values[index];
      }

      return defaultValue;
    }
  }
}
