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

import java.math.BigInteger;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.Set;

import com.google.common.math.BigIntegerMath;

import de.ivu.elect.business.ergebniskonflikt.control.util.SublistList;
import de.ivu.elect.business.person.entity.PersonendatenPK;

/**
 * CustomData für einen ErgebnisKonflikt, bei dem der Losentscheid eine Auswahl aus einer gegebenen Menge von
 * Kandidaturen trifft. Die Größe der Auswahl ist vorgegeben (meist 1).
 * <p/>
 * Die Reihenfolge spielt dabei weder bei den Kandidaturen zur Auswahl noch bei den gelosten Kandidaturen eine zwingende
 * Rolle. Trotzdem wird mit Hilfe von LinkedHashMaps versucht, die Reihenfolge des Einfügens beizubehalten, um ein
 * deterministisches Verhalten zu ermöglichen.
 */
public class KandidaturenAuswaehlenErgebnisKonfliktCustomData extends AbstractKandidaturenErgebnisKonfliktCustomData {

  private static final long serialVersionUID = -852756885385963805L;

  /**
   * Menge der Kandidaturen, aus denen gelost wird
   */
  // Attribut darf nicht umbennannt werden, sollte eigentlich beteiligteKandidaturenPKs heißen.
  private Set<PersonendatenPK> kandidaturenPKsZurAuswahl = new LinkedHashSet<>();

  /**
   * Geloste Kandidaturen
   */
  // Attribut darf nicht umbennannt werden, sollte eigentlich gelosteKandidaturenPKs heißen.
  private Set<PersonendatenPK> kandidaturenPKsGelost = new LinkedHashSet<>();

  /**
   * Diese Methode muss von Subklassen überschrieben werden.
   * 
   * @see de.ivu.elect.business.ergebniskonflikt.entity.ErgebnisKonfliktCustomData#getClassName()
   */
  @Override
  public String getClassName() {
    return KandidaturenAuswaehlenErgebnisKonfliktCustomData.class.getName();
  }

  @Override
  public Set<PersonendatenPK> getBeteiligteKandidaturenPKs() {
    return kandidaturenPKsZurAuswahl;
  }

  @Override
  public Set<PersonendatenPK> getGelosteKandidaturenPKs() {
    return kandidaturenPKsGelost;
  }

  @Override
  public void setBeteiligteKandidaturenPKs(Collection< ? extends PersonendatenPK> beteiligteKandidaturenPKs) {
    this.kandidaturenPKsZurAuswahl = new LinkedHashSet<>(beteiligteKandidaturenPKs);

    notifyEntity();
  }

  @Override
  public void setGelosteKandidaturenPKs(Collection< ? extends PersonendatenPK> gelosteKandidaturenPKs) {
    this.kandidaturenPKsGelost = new LinkedHashSet<>(gelosteKandidaturenPKs);

    notifyEntity();
  }

  @Override
  public int hashCode() {
    return (int) (getAnzahlZuLosen() + getBeteiligteKandidaturenPKs().hashCode());
  }

  @Override
  public boolean equals(Object o) {
    if (this == o) {
      return true;
    }

    if (!(o instanceof KandidaturenAuswaehlenErgebnisKonfliktCustomData)) {
      return false;
    }

    // Gleichheit: Gleicher Typ und gleiche Alternativen

    KandidaturenAuswaehlenErgebnisKonfliktCustomData that = (KandidaturenAuswaehlenErgebnisKonfliktCustomData) o;
    if (this.getAnzahlZuLosen() != that.getAnzahlZuLosen()) {
      return false;
    }

    // alle Alternativen müssen übereinstimmen
    return this.getBeteiligteKandidaturenPKs().equals(that.getBeteiligteKandidaturenPKs());
  }

  /**
   * Sortiere die Kandidaten nach dem PK und nimm die ersten davon
   */
  @Override
  public void loeseKonfliktAutomatisch() {
    setGelosteKandidaturenPKs(waehleDieErstenAus(getAnzahlZuLosen(), getBeteiligteKandidaturenPKs()));
  }

  @Override
  public void loeseKonfliktAutomatisch(int indexDerLoesung) {
    setGelosteKandidaturenPKs(new SublistList<>(asSortedList(getBeteiligteKandidaturenPKs()), (int) getAnzahlZuLosen())
        .get(indexDerLoesung));
  }

  @Override
  public void loeseKonfliktAutomatischZyklisch(int indexDerLoesung) {
    setGelosteKandidaturenPKs(waehleZyklischAus(indexDerLoesung, getAnzahlZuLosen(), getBeteiligteKandidaturenPKs()));
  }

  @Override
  public BigInteger getAnzahlLoesungen() {
    return BigIntegerMath.binomial(getAnzahlZyklischerLoesungen(), (int) getAnzahlZuLosen());
  }

  @Override
  public int getAnzahlZyklischerLoesungen() {
    return getBeteiligteKandidaturenPKs().size();
  }
}
