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

import static java.util.Collections.emptySet;

import java.util.ArrayList;
import java.util.Collection;
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.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 der Losentscheid eine Auswahl aus einer gegebenen Menge oder Liste von Kandidaturen trifft. Ob die Reihenfolge eine Rolle spielt hängt von der
 * konkreten Unterklasse ab. Duplikate sind in jedem Fall unzulässig.
 */
public abstract class AbstractKandidaturenErgebnisKonfliktCustomData extends AbstractErgebnisKonfliktLosoptionenCustomData {

  private static final long serialVersionUID = 1L;

  protected AbstractKandidaturenErgebnisKonfliktCustomData() {
    super();
  }

  @Override
  protected abstract Collection<PersonendatenPK> getBeteiligteKandidaturenPKs();

  protected abstract void setBeteiligteKandidaturenPKs(Collection< ? extends PersonendatenPK> beteiligteKandidaturenPKs);

  @Override
  protected abstract Collection<PersonendatenPK> getGelosteKandidaturenPKs();

  protected abstract void setGelosteKandidaturenPKs(Collection< ? extends PersonendatenPK> beteiligteKandidaturenPKs);

  public void setBeteiligteKandidaturen(Collection< ? extends Kandidatur> beteiligteKandidaturen) {
    Set<PersonendatenPK> pks = new LinkedHashSet<>();
    for (Kandidatur kandidatur : beteiligteKandidaturen) {
      pks.add(kandidatur.getPk());
    }
    setBeteiligteKandidaturenPKs(pks);
  }

  public void setGelosteKandidaturen(List< ? extends Kandidatur> gelosteKandidaturen) {
    Set<PersonendatenPK> pks = new LinkedHashSet<>();
    for (Kandidatur kandidatur : gelosteKandidaturen) {
      pks.add(kandidatur.getPk());
    }
    setGelosteKandidaturenPKs(pks);
  }

  @Override
  protected Collection<WahlvorschlagPK> getBeteiligteListenPKs() {
    return emptySet();
  }

  @Override
  protected Collection<WahlvorschlagPK> getGelosteListenPKs() {
    return emptySet();
  }

  @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();
  }

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

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

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

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

    setGelosteKandidaturenPKs(result);
  }

  //**************** Erzeugen einer Beschreibung des Konflikts bzw. der Lösung ***********************//

  @Override
  public String createLoesungBeschreibung(ErgebnisKonfliktCustomDataFormatter ergebnisKonfliktCustomDataFormatter, Collection<Wahlvorschlag> ignore1, Collection<Kandidatur> kandidaturen,
      Collection<Listenverbindung> ignore2, Collection<PolitischeGruppe> ignore3, Collection<Wahldurchfuehrungsgebiet> ignore4) {
    return createLoesungBeschreibung(ergebnisKonfliktCustomDataFormatter, kandidaturen, getGelosteKandidaturenPKs());
  }

  public String createLoesungBeschreibung(ErgebnisKonfliktCustomDataFormatter ergebnisKonfliktCustomDataFormatter, AbstractKandidaturDAO kandidaturDAO, Collection<PersonendatenPK> kandidaturPKs) {
    return createLoesungBeschreibung(ergebnisKonfliktCustomDataFormatter, getKandidaturen(kandidaturDAO, kandidaturPKs), kandidaturPKs);
  }

  public String createLoesungBeschreibung(ErgebnisKonfliktCustomDataFormatter ergebnisKonfliktCustomDataFormatter, Collection<Kandidatur> kandidaturen, Collection<PersonendatenPK> kandidaturPKs) {
    return createLoesungBeschreibung(ergebnisKonfliktCustomDataFormatter, mapEntityPerPK(kandidaturen), kandidaturPKs);
  }

  private String createLoesungBeschreibung(ErgebnisKonfliktCustomDataFormatter ergebnisKonfliktCustomDataFormatter, Map<PersonendatenPK, Kandidatur> kandidaturPerPK,
      Collection<PersonendatenPK> kandidaturPKs) {
    Collection<String> result = new ArrayList<>();

    Map<PersonendatenPK, String> displayNamePerKandidatur = ergebnisKonfliktCustomDataFormatter.collectDisplayNamePerKandidatur(kandidaturPerPK, kandidaturPKs);

    // NOTE, dass die Reihenfolge der PKs ggf. eine Rolle spielt!
    for (PersonendatenPK kandidaturPK : kandidaturPKs) {
      result.add(createKandidaturDisplayName(displayNamePerKandidatur, kandidaturPerPK.get(kandidaturPK)));
    }

    return joinErgebnisKonfliktLoesungItems(result);
  }

  private List<Kandidatur> getKandidaturen(AbstractKandidaturDAO kandidaturDAO, Collection<PersonendatenPK> kandidaturPKs) {
    List<Kandidatur> result = new ArrayList<>();

    for (PersonendatenPK pk : kandidaturPKs) {
      result.add(kandidaturDAO.findByPrimaryKey(pk));
    }

    return result;
  }

  public String createErgebnisKonfliktBeschreibung(ErgebnisKonfliktCustomDataFormatter ergebnisKonfliktCustomDataFormatter, String typBeschreibung, List< ? extends Kandidatur> kandidaturen) {
    return typBeschreibung + ": " + createErgebnisKonfliktBeschreibung(ergebnisKonfliktCustomDataFormatter, kandidaturen);
  }

  public String createErgebnisKonfliktBeschreibung(ErgebnisKonfliktCustomDataFormatter ergebnisKonfliktCustomDataFormatter, Collection< ? extends Kandidatur> kandidaturen) {
    Collection<String> result = new ArrayList<>();

    Map<PersonendatenPK, String> displayNamePerKandidatur = ergebnisKonfliktCustomDataFormatter.collectDisplayNamePerKandidatur(kandidaturen);

    // NOTE, dass die Reihenfolge der PKs ggf. eine Rolle spielt!
    for (Kandidatur kandidatur : kandidaturen) {
      result.add(createKandidaturDisplayName(displayNamePerKandidatur, kandidatur));
    }

    return joinErgebnisKonfliktBeschreibungItems(result);
  }
}
