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

import static java.util.Collections.emptyList;
import static java.util.Objects.isNull;
import static java.util.Objects.nonNull;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

import javax.annotation.Nullable;

import org.primefaces.model.DualListModel;

public abstract class AbstractPickListProperty<E> extends AbstractProperty implements PropertyHavingValuesFilter {

  private DualListModel<E> model;

  public AbstractPickListProperty() {
    super();
  }

  @Override
  protected boolean mustBeAccessableOnPageLoadByValue() {
    if (super.mustBeAccessableOnPageLoadByValue()) {
      return true;
    }

    return notAllEqual(getDefaultValue(), getValue());
  }

  @Override
  public PropertyType getDefaultType() {
    return PropertyType.PICK_LIST;
  }

  @Override
  protected boolean hasDefaultValue() {
    return notNullOrEmpty(getDefaultValue());
  }

  public DualListModel<E> getDefaultValue() {
    return new DualListModel<>(emptyList(), emptyList());
  }

  @Override
  public boolean hasValue() {
    return notNullOrEmpty(getValue());
  }

  public DualListModel<E> getValue() {
    if (isNull(model)) {
      model = buildModel();
    }

    return model;
  }

  public void setValue(DualListModel<E> value) {
    model = value;

    if (model == null) {
      model = new DualListModel<>(emptyList(), emptyList());
    }

    setzeZugeordneteElemente(model.getTarget());
  }

  protected DualListModel<E> buildModel() {
    List<E> verfuegbareElemente = new ArrayList<>(getVerfuegbareElemente());
    List<E> zugeordneteElemente = getZugeordneteElemente();

    verfuegbareElemente.removeAll(zugeordneteElemente);

    return new DualListModel<>(verfuegbareElemente, zugeordneteElemente);
  }

  protected abstract List<E> getZugeordneteElemente();

  protected abstract List<E> getVerfuegbareElemente();

  protected abstract void setzeZugeordneteElemente(List<E> target);

  protected abstract String getSourceCaptionMessageKey();

  protected abstract String getTargetCaptionMessageKey();

  public String getSourceCaption() {
    return getMessage(getSourceCaptionMessageKey());
  }

  public String getTargetCaption() {
    return getMessage(getTargetCaptionMessageKey());
  }

  public int getItemsMaxSize() {
    int result = 0;

    if (nonNull(getZugeordneteElemente())) {
      result += getZugeordneteElemente().size();
    }

    if (nonNull(getVerfuegbareElemente())) {
      result += getVerfuegbareElemente().size();
    }

    return result;
  }

  public String getItemFilterLabel(E item) {
    return getItemLabel(item);
  }

  public abstract String getItemLabel(E item);

  public String getItemTooltip(E item) {
    return "";
  }

  @Override
  public boolean isShowFilter() {
    return true; // Primfaces Filter über den Elementen einblenden
  }

  @Override
  public String getFilterMatchMode() {
    return PropertyFilterMatchMode.CUSTOM.getFilterMatchModeText();
  }

  @Override
  public String getFilterFunction() {
    return PropertyFilterFunction.TEST_FILTER_VALUE_PATTERN_MATCHES_ITEM_FILTER_LABEL.getFunctionName();
  }

  // Primfaces Reihenfolgesortier-Buttons auf Target-Bereich?
  public boolean isShowTargetControls() {
    return false; // Reihenfolge spielt meistens keine Rolle
  }

  public boolean isColorPresent() {
    return false;
  }

  @Nullable
  public String getItemColor(E item) {
    return null;
  }

  private boolean notAllEqual(DualListModel<E> one, DualListModel<E> another) {
    return !allEqual(one, another);
  }

  private boolean allEqual(DualListModel<E> one, DualListModel<E> another) {
    return allNullOrEmpty(one, another) || Objects.equals(one, another);
  }

  private boolean notAllNullOrEmpty(DualListModel<E> one, DualListModel<E> another) {
    return !allNullOrEmpty(one, another);
  }

  private boolean allNullOrEmpty(DualListModel<E> one, DualListModel<E> another) {
    return isNullOrEmpty(one) && isNullOrEmpty(another);
  }

  private boolean notNullOrEmpty(DualListModel<E> value) {
    return !isNullOrEmpty(value);
  }

  private boolean isNullOrEmpty(DualListModel<E> value) {
    // Nur DualListModel#getTarget ausgewertet, da diese Collection die tatsächlich zugeordneten Werte enthält.
    return value == null || isNullOrEmpty(value.getTarget());
  }
}
