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

import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.function.BiFunction;
import java.util.stream.Collectors;

import de.ivu.elect.business.auffaelligkeit.entity.AbstandartZwischenGebieten;
import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
import de.ivu.elect.business.hochrechnung.entity.AbstandGetter;
import de.ivu.elect.business.hochrechnung.entity.GebietDaten;
import de.ivu.elect.business.hochrechnung.entity.KoordinatenInformationen;
import de.ivu.elect.jee.presentation.dynamicform.FormBuilder;
import de.ivu.elect.jee.presentation.dynamicform.LambdaEnumProperty;

public class AbstandZwischenGebietenAnhandGebietsposition implements AbstandGetter<Double> {
  private Long gewicht = 1L;
  private final static long ABSCHAETZUNG_FUER_GROESST_MOEGLICHER_ABSTAND_WENN_NICHT_BERECHENBAR = 1000;
  private long abschaetzungFuerGroesstMoeglicherAbstand;
  private Boolean isDefiniert = null;

  @Override
  public boolean isDefiniert() {
    return this.isDefiniert != null && this.isDefiniert;
  }

  private Long getGroesstenMoeglichenAbstand(KoordinatenInformationen koordinatenInformationen) {
    List<Long> positions = koordinatenInformationen.getAlleGebiete().stream().map(Gebiet::getPosition).filter(Objects::nonNull).collect(Collectors.toList());
    if (positions.isEmpty()) {
      return null;
    }

    Long max = Collections.max(positions);
    Long min = Collections.min(positions);
    if (max != null && min != null && !max.equals(min)) {
      return max - min;
    }

    return null;
  }

  @Override
  public Double getAbstandWennDefiniert(GebietDaten<Double> g1, GebietDaten<Double> g2, KoordinatenInformationen koordinatenInformationen) {
    if (isDefiniert()) {
      Long resultUnnormiert = subtractGebietspositionen(g1.getGebiet(), g2.getGebiet());
      return getNormiertenWert(resultUnnormiert);
    }
    return null;
  }

  private Double getNormiertenWert(Long resultUnnormiert) {
    if (resultUnnormiert == null || abschaetzungFuerGroesstMoeglicherAbstand == 0) {
      return (double) abschaetzungFuerGroesstMoeglicherAbstand;
    }
    return 1.0 * resultUnnormiert / abschaetzungFuerGroesstMoeglicherAbstand;
  }

  @Override
  public Long getGewicht() {
    return gewicht;
  }
  @Override
  public void setGewicht(Long gewicht) {
    this.gewicht = gewicht;
  }

  @Override
  public boolean setIsDefiniert(KoordinatenInformationen koordinatenInformationen) {
    Long groessterMoeglicherAbstand = getGroesstenMoeglichenAbstand(koordinatenInformationen);
    if (groessterMoeglicherAbstand != null && groessterMoeglicherAbstand > 0) {
      this.abschaetzungFuerGroesstMoeglicherAbstand = groessterMoeglicherAbstand;
      this.isDefiniert = true;
    } else {
      this.abschaetzungFuerGroesstMoeglicherAbstand = ABSCHAETZUNG_FUER_GROESST_MOEGLICHER_ABSTAND_WENN_NICHT_BERECHENBAR;
      this.isDefiniert = false;
    }

    return this.isDefiniert;
  }

  @Override
  public AbstandartZwischenGebieten getAbstandartZwischenGebieten() {
    return AbstandartZwischenGebieten.ANHAND_POSITIONEN_DER_GEBIETE;
  }

  @Override
  public void addObservingProperties(FormBuilder formBuilder, LambdaEnumProperty observedProperty, BiFunction<String, Object[], String> messageGetter) {
    // do nothing
  }

  private Long subtractGebietspositionen(Gebiet g1, Gebiet g2) {
    if (g1 == null || g2 == null || g1.getPosition() == null || g2.getPosition() == null) {
      return abschaetzungFuerGroesstMoeglicherAbstand;
    }
    return Math.abs(g1.getPosition() - g2.getPosition());
  }
}
