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

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import com.google.common.math.BigIntegerMath;

import de.ivu.elect.business.ergebniskonflikt.control.util.PermutationList;
import de.ivu.elect.business.kandidatur.boundary.AbstractKandidaturDAO;
import de.ivu.elect.business.kandidatur.entity.Kandidatur;
import de.ivu.elect.business.person.entity.PersonendatenPK;

/**
 * CustomData für {@link ErgebnisKonflikt}e, bei denen die Reihenfolge von {@link Kandidatur}en per Los festgelegt
 * werden muss.
 */
public class KandidaturenReihenfolgeErgebnisKonfliktCustomData extends AbstractKandidaturenErgebnisKonfliktCustomData {

  private static final long serialVersionUID = -6857621902689333760L;

  public static final String KONFLIKT_LOESUNG_VIEW_ID = "/ergebniskonflikt/ergebnis-konflikt-reihenfolge-kandidatur-festlegen-edit.xhtml";

  // Attribut darf nicht umbenannt werden, damit CustomData-Objekte in der Datenbank weiterhin lesbar bleiben
  // TODO Workaround finden, sollte eigentlich beteiligteKandidaturenPKs heißen
  private List<PersonendatenPK> kandidaturenPKsZurAuswahl = new ArrayList<>();

  // Attribut darf nicht umbenannt werden, damit CustomData-Objekte in der Datenbank weiterhin lesbar bleiben
  // TODO Workaround finden, sollte eigentlich gelosteKandidaturenPKs heißen
  private List<PersonendatenPK> kandidaturenPKsGelosteReihenfolge = new ArrayList<>();

  @Override
  public String getErgebnisKonfliktLoesungViewId() {
    return KONFLIKT_LOESUNG_VIEW_ID;
  }

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

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

  @Override
  public List<PersonendatenPK> getGelosteKandidaturenPKs() {
    return kandidaturenPKsGelosteReihenfolge;
  }

  @Override
  public void setBeteiligteKandidaturenPKs(Collection< ? extends PersonendatenPK> beteiligteKandidaturenPKs) {
    this.kandidaturenPKsZurAuswahl = new ArrayList<>(beteiligteKandidaturenPKs);
    notifyEntity();
    setAnzahlZuLosen(beteiligteKandidaturenPKs.size());
  }

  /**
   * Methode für GUI - NICHT in Algorithmen verwenden!
   */
  public List<Kandidatur> getBeteiligteKandidaturen(AbstractKandidaturDAO abstractKandidaturDAO) {
    List<Kandidatur> result = new ArrayList<>(getBeteiligteKandidaturenPKs().size());
    for (PersonendatenPK personendatenPK : getBeteiligteKandidaturenPKs()) {
      result.add(abstractKandidaturDAO.findByPrimaryKey(personendatenPK));
    }
    return result;
  }

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

    notifyEntity();
  }

  @Override
  public int hashCode() {
    int result = 0;

    for (PersonendatenPK personendatenPK : getBeteiligteKandidaturenPKs()) {
      result += personendatenPK.hashCode();
    }

    return result;
  }

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

    // Gleichheit: Gleicher Typ und gleiche Alternativen

    KandidaturenReihenfolgeErgebnisKonfliktCustomData that = (KandidaturenReihenfolgeErgebnisKonfliktCustomData) o;

    // alle Alternativen müssen übereinstimmen
    return getBeteiligteKandidaturenPKs().containsAll(that.getBeteiligteKandidaturenPKs())
        && that.getBeteiligteKandidaturenPKs().containsAll(getBeteiligteKandidaturenPKs());
  }

  public void uebernimmLoesung(KandidaturenReihenfolgeErgebnisKonfliktCustomData other) {
    setGelosteKandidaturenPKs(other.getGelosteKandidaturenPKs());
  }

  @Override
  public BigInteger getAnzahlLoesungen() {
    return BigIntegerMath.factorial(getAnzahlZyklischerLoesungen());
  }

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

  @Override
  public void loeseKonfliktAutomatisch() {
    setGelosteKandidaturenPKs(asSortedList(getBeteiligteKandidaturenPKs()));
  }

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

  @Override
  public void loeseKonfliktAutomatischZyklisch(int indexDerLoesung) {
    setGelosteKandidaturenPKs(permutiereZyklisch(indexDerLoesung, asSortedList(getBeteiligteKandidaturenPKs())));
  }
}
