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

import static java.util.Collections.emptySet;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import de.ivu.elect.business.gebietsbaum.boundary.AbstractWahldurchfuehrungsgebietDAO;
import de.ivu.elect.business.gebietsbaum.entity.GebietPK;
import de.ivu.elect.business.gebietsbaum.entity.Wahldurchfuehrungsgebiet;
import de.ivu.elect.business.gruppe.boundary.AbstractPolitischeGruppeDAO;
import de.ivu.elect.business.gruppe.entity.GruppePK;
import de.ivu.elect.business.gruppe.entity.PolitischeGruppe;
import de.ivu.elect.business.kandidatur.boundary.AbstractKandidaturDAO;
import de.ivu.elect.business.kandidatur.entity.Kandidatur;
import de.ivu.elect.business.person.entity.PersonendatenPK;
import de.ivu.elect.business.wahlvorschlag.boundary.AbstractListenverbindungDAO;
import de.ivu.elect.business.wahlvorschlag.boundary.AbstractWahlvorschlagDAO;
import de.ivu.elect.business.wahlvorschlag.entity.Liste;
import de.ivu.elect.business.wahlvorschlag.entity.Listenverbindung;
import de.ivu.elect.business.wahlvorschlag.entity.ListenverbindungPK;
import de.ivu.elect.business.wahlvorschlag.entity.Wahlvorschlag;
import de.ivu.elect.business.wahlvorschlag.entity.WahlvorschlagPK;

/**
 * CustomData für {@link ErgebnisKonflikt}e, bei dem aus {@link Liste}n ausgelost wird. Ob die Reihenfolge eine Rolle spielt, hängt von der Unterklasse ab.
 */
public abstract class AbstractListenErgebnisKonfliktCustomData extends AbstractErgebnisKonfliktLosoptionenCustomData {

  private static final long serialVersionUID = 1L;

  private Set<WahlvorschlagPK> beteiligteListenPks = new LinkedHashSet<>();

  private Set<WahlvorschlagPK> gelosteListenPks = new LinkedHashSet<>();

  protected AbstractListenErgebnisKonfliktCustomData() {
    super();
  }

  @Override
  public Collection<PersonendatenPK> getBeteiligteKandidaturenPKs() {
    return emptySet();
  }

  @Override
  public Collection<PersonendatenPK> getGelosteKandidaturenPKs() {
    return emptySet();
  }

  @Override
  public Collection<WahlvorschlagPK> getBeteiligteListenPKs() {
    return beteiligteListenPks;
  }

  @Override
  public Collection<WahlvorschlagPK> getGelosteListenPKs() {
    return gelosteListenPks;
  }

  @Override
  protected Collection<ListenverbindungPK> getBeteiligteListenverbindungenPKs() {
    return emptySet();
  }

  @Override
  protected Collection<ListenverbindungPK> getGelosteListenverbindungenPKs() {
    return emptySet();
  }

  @Override
  protected Collection<GruppePK> getBeteiligtePolitischeGruppenPKs() {
    return emptySet();
  }

  @Override
  protected Collection<GruppePK> getGelostePolitischeGruppenPKs() {
    return emptySet();
  }

  @Override
  protected Collection<GebietPK> getBeteiligteGebietPKs() {
    return emptySet();
  }

  @Override
  protected Collection<GebietPK> getGelosteGebietPKs() {
    return emptySet();
  }

  public void setBeteiligteListenPKs(Collection<WahlvorschlagPK> beteiligteListenPKs) {
    this.beteiligteListenPks = new LinkedHashSet<>(beteiligteListenPKs);

    notifyEntity();
  }

  public void setBeteiligteListen(Collection<Liste> beteiligteListen) {
    Set<WahlvorschlagPK> pks = new LinkedHashSet<>();

    for (Liste liste : beteiligteListen) {
      pks.add(liste.getPk());
    }

    setBeteiligteListenPKs(pks);
  }

  public void setGelosteListenPKs(Collection<WahlvorschlagPK> gelosteListenPKs) {
    this.gelosteListenPks = new LinkedHashSet<>(gelosteListenPKs);

    notifyEntity();
  }

  @Override
  public List<ErgebnisKonfliktLosoption> getLosoptionenVerfuegbar(ErgebnisKonfliktCustomDataFormatter ergebnisKonfliktCustomDataFormatter, AbstractWahlvorschlagDAO wahlvorschlagDAO,
      AbstractKandidaturDAO kandidaturDAO, AbstractListenverbindungDAO listenverbindungDAO, AbstractPolitischeGruppeDAO politischeGruppeDAO,
      AbstractWahldurchfuehrungsgebietDAO wahldurchfuehrungsgebietDAO) {
    return createLosoptionen(ergebnisKonfliktCustomDataFormatter, wahlvorschlagDAO, getBeteiligteListenPKs());
  }

  @Override
  public List<ErgebnisKonfliktLosoption> getLosoptionenGelost(ErgebnisKonfliktCustomDataFormatter ergebnisKonfliktCustomDataFormatter, AbstractWahlvorschlagDAO wahlvorschlagDAO,
      AbstractKandidaturDAO kandidaturDAO, AbstractListenverbindungDAO listenverbindungDAO, AbstractPolitischeGruppeDAO politischeGruppeDAO,
      AbstractWahldurchfuehrungsgebietDAO wahldurchfuehrungsgebietDAO) {
    return createLosoptionen(ergebnisKonfliktCustomDataFormatter, wahlvorschlagDAO, getGelosteListenPKs());
  }

  @Override
  public void setLosoptionenGelost(List<ErgebnisKonfliktLosoption> losoptionenGelost) {
    Set<WahlvorschlagPK> result = new LinkedHashSet<>();

    for (ErgebnisKonfliktLosoption losoption : losoptionenGelost) {
      if (losoption.isInstanceOf(Liste.class)) {
        result.add((WahlvorschlagPK) losoption.getPk());
      }
    }

    setGelosteListenPKs(result);
  }

  /**
   * equals() bezieht sich nur auf die Attribute, die die zu entscheidene Situation beschreiben, aber nicht auf die Attribute, die die Lösung / Auswahl / Entscheidung betreffen.
   *
   * @see java.lang.Object#equals(java.lang.Object)
   */
  @Override
  public boolean equals(Object o) {
    if (this == o) {
      return true;
    }

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

    // Gleichheit: Gleicher Typ und gleiche Alternativen

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

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

  @Override
  public int hashCode() {
    int result = (int) (getAnzahlZuLosen() ^ getAnzahlZuLosen() >>> 32);
    return 31 * result + beteiligteListenPks.hashCode();
  }

  @Override
  public String createLoesungBeschreibung(ErgebnisKonfliktCustomDataFormatter ergebnisKonfliktCustomDataFormatter, Collection<Wahlvorschlag> wahlvorschlaege, Collection<Kandidatur> kandidaturen,
      Collection<Listenverbindung> listenverbindungen, Collection<PolitischeGruppe> politischeGruppen, Collection<Wahldurchfuehrungsgebiet> gebiete) {
    return createLoesungBeschreibungFuerListenUndListenverbindungen(ergebnisKonfliktCustomDataFormatter, wahlvorschlaege, getGelosteListenPKs(), listenverbindungen,
        Collections.<ListenverbindungPK> emptyList());
  }

  public String createErgebnisKonfliktBeschreibung(ErgebnisKonfliktCustomDataFormatter ergebnisKonfliktCustomDataFormatter, String typBeschreibung, List<Liste> listen) {
    return typBeschreibung + ": " + createErgebnisKonfliktBeschreibung(ergebnisKonfliktCustomDataFormatter, listen);
  }

  public String createErgebnisKonfliktBeschreibung(ErgebnisKonfliktCustomDataFormatter ergebnisKonfliktCustomDataFormatter, Collection<Liste> listen) {
    Collection<String> result = new ArrayList<>();

    Map<WahlvorschlagPK, String> displayNamePerWahlvorschlag = ergebnisKonfliktCustomDataFormatter.collectDisplayNamePerWahlvorschlag(listen);

    for (Liste liste : listen) {
      result.add(createWahlvorschlagDisplayName(displayNamePerWahlvorschlag, liste));
    }

    return joinErgebnisKonfliktBeschreibungItems(result);
  }
}
