
package de.ivu.elect.sitzberechnung.verteilung;

import static java.util.stream.Collectors.toList;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

import de.ivu.elect.sitzberechnung.Gebiet;
import de.ivu.elect.sitzberechnung.Kandidatur;
import de.ivu.elect.sitzberechnung.control.Sitzberechnungsdaten;
import de.ivu.elect.sitzberechnung.entity.DefaultSitzberechnungsergebnis;
import de.ivu.elect.sitzberechnung.konflikt.ErgebnisKonfliktKandidaturenSortieren;

public class SortierungErgebnisKonfliktFactory extends AbstractErgebnisKonfliktFactory {

  public SortierungErgebnisKonfliktFactory(Sitzberechnungsdaten sitzberechnungsdaten, DefaultSitzberechnungsergebnis sitzberechnungsergebnis) {
    super(sitzberechnungsdaten, sitzberechnungsergebnis);
  }

  public <E extends AbstractReihenfolgeErgebnis> SortierungErgebnisKonfliktLosentscheid loseKandidaturen(E ergebnis, Gebiet wahlgebiet, Gebiet wahlvorschlaggebiet, Enum< ? > typ,
      List< ? extends KandidaturTeilnehmer> teilnehmerInSortierung) {
    List<KandidaturInVerteilung> sortierbar = filterTeilnehmerImLosverfahren(ergebnis, teilnehmerInSortierung);

    List<Kandidatur> kandidaturenSortierbar = sortierbar.stream().map(KandidaturInVerteilung::getKandidatur).filter(Objects::nonNull).collect(toList());

    ErgebnisKonfliktKandidaturenSortieren ergebnisKonflikt = createOrFindErgebnisKonflikt(
        ErgebnisKonfliktKandidaturenSortieren.newInstance(wahlgebiet, wahlvorschlaggebiet, typ, kandidaturenSortierbar));

    List<KandidaturInVerteilung> sortiert = new ArrayList<>();
    sortiert.addAll(ergebnisKonflikt.filterKandidaturenSortiert(sortierbar));

    updateTeilnehmerImLosverfahren(ergebnis, teilnehmerInSortierung, sortiert);

    return new SortierungErgebnisKonfliktLosentscheid(ergebnisKonflikt, sortierbar, sortiert);
  }

  public <O, T extends AbstractTeilnehmer<O>> List<O> filterTeilnehmerImLosverfahren(AbstractReihenfolgeErgebnis ergebnis, List<T> teilnehmerInSortierung) {
    return filterTeilnehmerImLosverfahren(ergebnis::getTeilnehmerErgebnis, teilnehmerInSortierung);
  }

  public <O, T extends AbstractTeilnehmer<O>> void updateTeilnehmerImLosverfahren(AbstractReihenfolgeErgebnis ergebnis, List<T> teilnehmerInSortierung, List<O> sortiert) {
    if (sortiert.isEmpty()) {
      return; // Nichts zu tun...
    }

    List<AbstractTeilnehmer< ? >> teilnehmerSortiert = new ArrayList<>();

    for (AbstractTeilnehmer< ? > teilnehmer : teilnehmerInSortierung) {
      TeilnehmerErgebnis teilnehmerErgebnis = ergebnis.getTeilnehmerErgebnis(teilnehmer);

      if (teilnehmerErgebnis != null && isImLosverfahren(teilnehmerErgebnis) && sortiert.contains(teilnehmer.getObject())) {
        teilnehmerSortiert.add(teilnehmer);
      }
    }

    teilnehmerSortiert.sort((one, another) -> Integer.compare(sortiert.indexOf(one.getObject()), sortiert.indexOf(another.getObject()))); // ELECT-15978

    ergebnis.entscheideLosverfahren(teilnehmerSortiert);
  }
}
