package de.ivu.elect.business.hochrechnung.entity;

import java.util.Collections;
import java.util.Set;
import java.util.function.BiFunction;

import de.ivu.elect.business.auffaelligkeit.entity.AbstandartZwischenGebieten;
import de.ivu.elect.jee.presentation.dynamicform.FormBuilder;
import de.ivu.elect.jee.presentation.dynamicform.LambdaEnumProperty;
import de.ivu.elect.jee.presentation.dynamicform.LambdaLongProperty;
import de.ivu.elect.jee.presentation.dynamicform.Property;

public interface AbstandGetter<N extends Number> {

  Double getAbstandWennDefiniert(GebietDaten<N> g1, GebietDaten<N> g2, KoordinatenInformationen koordinatenInformationen);

  default Double getAbstand(GebietDaten<N> g1, GebietDaten<N> g2, KoordinatenInformationen koordinatenInformationen) {
    if (isDefiniert()) {
      return getAbstandWennDefiniert(g1, g2, koordinatenInformationen);
    }
    setIsDefiniert(koordinatenInformationen);
    return isDefiniert()? getAbstandWennDefiniert(g1, g2, koordinatenInformationen) : null;
  }

  default Long getGewicht() {
    return 1L;
  }

  void setGewicht(Long gewicht);

  boolean isDefiniert();

  boolean setIsDefiniert(KoordinatenInformationen koordinatenInformationen);

  AbstandartZwischenGebieten getAbstandartZwischenGebieten();

  void addObservingProperties(FormBuilder formBuilder, LambdaEnumProperty observedProperty, BiFunction<String, Object[], String> messageGetter);

  default void addObservingGewichtProperty(FormBuilder formBuilder, LambdaEnumProperty observedProperty, BiFunction<String, Object[], String> messageGetter) {
    LambdaLongProperty lambdaPropertyGewicht = new LambdaLongProperty(
        messageGetter.apply("stimmeingabe.auffaelligkeit.abstandart.zwischen.gebieten.gewicht." + getAbstandartZwischenGebieten().toString(), new Object[0]),
        true, this::setGewicht, this::getGewicht);

    addAbstractObservingLambdaProperty(formBuilder, observedProperty, lambdaPropertyGewicht,
        Collections.singleton(AbstandartZwischenGebieten.MISCHUNG_MIT_BENUTZER_DEFINIERTE_GEWICHTUNG), false);
  }

  default void addAbstractObservingLambdaProperty(FormBuilder formBuilder, LambdaEnumProperty observedProperty, Property observerProperty, Set<Enum> valueWhenToShowObserver, boolean defaultRequired) {
    observerProperty.setVisibleOnPageLoad(false);
    observerProperty
        .setVisible(observedProperty.getValue() != null && observedProperty.isVisible() && valueWhenToShowObserver.contains(observedProperty.getValue()));
    observerProperty.appendObservedPropertyValueChangedConsumer(other -> {
          if (other.equals(observedProperty)) {
            observerProperty
                .setVisible(observedProperty.getValue() != null && observedProperty.isVisible() && valueWhenToShowObserver.contains(observedProperty.getValue()));
            if (observerProperty.isVisible()) {
              observerProperty.setRequired(defaultRequired);
            } else {
              observerProperty.setRequired(false);
            }
          }
        }
    );

    observedProperty.getObservedProperties().forEach(
        vonObservedObserved -> {
          observerProperty.appendObservedPropertyValueChangedConsumer(other -> {
                if (other.equals(vonObservedObserved)) {
                  observerProperty
                      .setVisible(
                          observedProperty.getValue() != null && observedProperty.isVisible() && valueWhenToShowObserver.contains(observedProperty.getValue()));
                  if (observerProperty.isVisible()) {
                    observerProperty.setRequired(defaultRequired);
                  } else {
                    observerProperty.setRequired(false);
                  }
                }
              }
          );

        });

    if (observerProperty.isVisible()) {
      observerProperty.setRequired(defaultRequired);
    } else {
      observerProperty.setRequired(false);
    }

    formBuilder.addProperty(observerProperty).withObservedProperty(observedProperty);
  }
}
