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

import static de.ivu.elect.jee.entity.EntityUtil.castTo;

import java.util.Collection;
import java.util.function.Function;

import javax.annotation.Nullable;

import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
import de.ivu.elect.business.gebietsbaum.entity.Wahldurchfuehrungsgebiet;
import de.ivu.elect.business.sitzberechnung.entity.SitzberechnungsalgorithmusKonfiguration;
import de.ivu.elect.business.wahl.entity.AbstractWahlParameterLoader;
import de.ivu.elect.business.wahl.entity.Wahl;
import de.ivu.elect.business.wahl.entity.WahlartErgebnisart;
import de.ivu.elect.business.wahlvorschlag.entity.Liste;
import de.ivu.elect.business.wahlvorschlag.entity.Wahlvorschlag;
import de.ivu.elect.jee.faces.Messages;
import de.ivu.elect.presentation.kandidatur.table.KandidaturGewaehltSitzberechnungsalgorithmusColumnBuilder;

public interface Sitzberechnungsalgorithmus<K extends SitzberechnungsalgorithmusKonfiguration> extends SitzberechnungsalgorithmusMarker, Sitzberechnungsfunktion<K> {

  Wahldurchfuehrungsgebiet NICHT_UNTERSTUETZT = null;

  default boolean isDefaultFuerWahlart(Wahl wahl) {
    return wahl != null && wahl.getWahlart() != null && getSitzberechnungsalgorithmusEnum().equals(wahl.getWahlart().getSitzberechnungsalgorithmus());
  }

  /**
   * Standardkonfiguration, des Algorithmus, wichtig ist die Instanz der Klasse, um diese zum Beispiel über die GUI zu aktualisieren
   */
  K getDefaultKonfiguration();

  // NOTE, dass {@link CustomData} der {@link Gewaehlt} Entität Algorithmus-spezifisch ist!
  KandidaturGewaehltSitzberechnungsalgorithmusColumnBuilder getKandidaturGewaehltColumnBuilder(Messages messages);

  /**
   * Berechnet der Algorithmus überhaupt etwas, oder ist er nur eine Art Platzhalter?
   */
  boolean isBerechnetErgebnis();

  /**
   * Berechnet der Algorithmus etwas, falls in der Wahl vom Wähler benannte Wahlvorschläge erfasst werden können?
   */
  boolean isBerechnetErgebnisFallsWaehlerBenannteWahlvorschlaegeSonstigeErfasst();

  /**
   * Gibt an, ob bei der Sitzberechnung {@link Gewaehlt}e erzeugt werden. Dadurch wird z.B. die entsprechende Ansicht aktiviert.
   */
  boolean isMarkiertGewaehlte(AbstractWahlParameterLoader wahlParameterLoader, Wahl wahl, WahlartErgebnisart ergebnisart);

  /**
   * Werden durch den Algorithmus Sitze vergeben, oder nur Gewählte markiert (Bürgermeister...)...
   */
  boolean isVergibtSitze();

  /**
   * Werden durch den Algorithmus D'Hondt-Quotienten für eine Gruppe angelegt?
   * <p />
   * Relevant bspw. in DD bei der SRW, da die Sitze in der ersten Verteilung anhand der Quotienten für die Gruppe im Wahlgebiet (Dresden) und erst anschließend anhand der Quotienten für die Listen auf
   * den Listenwahlgebieten (Wahlkreise, cf. Sitzberechnungsalgorithmus#isErstelltDHondtQuotientListenInGruppe) verteilt werden.
   */
  boolean isErstelltDHondtQuotientPolitischeGruppe();

  /**
   * Werden durch den Algorithmus D'Hondt-Quotienten für die Listen einer Gruppe angelegt?
   * <p />
   * Relevant bspw. in DD bei der SRW, da die Sitze in der ersten Verteilung anhand der Quotienten für die Gruppe im Wahlgebiet (Dresden, cf.
   * Sitzberechnungsalgorithmus#isErstelltDHondtQuotientPolitischeGruppe) und erst anschließend anhand der Quotienten für die Listen auf den Listenwahlgebieten (Wahlkreise) verteilt werden.
   */
  boolean isErstelltDHondtQuotientListenInGruppe();

  /**
   * Werden durch den Algorithmus D'Hondt-Quotienten angelegt?
   * <p />
   * Relevant bspw. in DD bei der ORW, da dort die Wahlgebiete den Listenwahlgebieten (Ortschaften) entsprechen, so dass die Sitze bereits in der ersten Verteilung anhand der Quotienten für die Listen
   * auf den Listenwahlgebieten verteilt werden.
   */
  boolean isErstelltDHondtQuotientListenImGebiet();

  /**
   * Werden durch den Algorithmus D'Hondt-Quotienten für die Listen einer Listenverbindung angelegt?
   */
  boolean isErstelltDHondtQuotientListenInListenverbindung();

  boolean isWaehlerBenannteWahlvorschlaegeErfassbar(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, Collection< ? extends Wahlvorschlag> wahlvorschlaege);

  /**
   * Sollte die konkrete Wahl es erlauben, dass ein Wähler des gegebenen Erfassungsgebiets beliebigen Personen seine Stimme gibt: das Gebiet, auf dem diese Personen persistiert werden.
   * <p/>
   * Sonst: <code>null</code>.
   */
  @Nullable
  default Wahldurchfuehrungsgebiet getWaehlerBenannteListeListenwahlgebietFuerErfassungsgebiet(Wahldurchfuehrungsgebiet erfassungsgebiet,
      Function<Wahldurchfuehrungsgebiet, Collection<Liste>> listenInGebietshierarchieFinder) {
    if (erfassungsgebiet != null && erfassungsgebiet.isErfassungsgebiet()) {
      Gebiet listenwahlgebiet = erfassungsgebiet.getUebergeordnetesListenwahlgebiet();

      if (listenwahlgebiet != null) { // ELECT-13955
        Wahldurchfuehrungsgebiet result = castTo(listenwahlgebiet, Wahldurchfuehrungsgebiet.class);
        Collection<Liste> listen = listenInGebietshierarchieFinder.apply(result);

        if (listen != null && isWaehlerBenannteWahlvorschlaegeErfassbar(erfassungsgebiet, listen)) {
          return result;
        }
      }
    }

    return NICHT_UNTERSTUETZT;
  }
}
