
package de.ivu.elect.business.kandidatur.boundary;

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

import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.Set;

import javax.inject.Inject;

import de.ivu.elect.business.ergebnis.control.AbstractKumulierteStimmenSearch;
import de.ivu.elect.business.ergebnis.control.StimmenPool;
import de.ivu.elect.business.ergebnis.entity.Ergebnis;
import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
import de.ivu.elect.business.gruppe.boundary.PolitischeGruppeExtendedDAO;
import de.ivu.elect.business.gruppe.entity.PolitischeGruppe;
import de.ivu.elect.business.kandidatur.control.KandidaturKumulierteStimmenBuilder;
import de.ivu.elect.business.kandidatur.control.KandidaturKumulierteStimmenByStimmzettelPositionComparator;
import de.ivu.elect.business.kandidatur.entity.Direktkandidatur;
import de.ivu.elect.business.kandidatur.entity.Kandidatur;
import de.ivu.elect.business.kandidatur.entity.KandidaturKumulierteStimmen;
import de.ivu.elect.business.kandidatur.entity.KandidaturStatus;
import de.ivu.elect.business.kandidatur.entity.Listenkandidatur;
import de.ivu.elect.business.wahl.entity.Wahl;

public class KandidaturNachErfassungsgebietStimmenSearch extends AbstractKumulierteStimmenSearch<KandidaturNachErfassungsgebietStimmenSearchParameters, KandidaturKumulierteStimmen> {

  @Inject
  private transient PolitischeGruppeExtendedDAO politischeGruppeExtendedDAO;

  @Inject
  private transient KandidaturExtendedDAO kandidaturExtendedDAO;

  @Inject
  private transient ListenkandidaturExtendedDAO listenkandidaturExtendedDAO;

  @Inject
  private transient DirektkandidaturExtendedDAO direktkandidaturExtendedDAO;

  @Inject
  private transient KandidaturKumulierteStimmenBuilder kandidaturKumulierteStimmenBuilder;

  public KandidaturNachErfassungsgebietStimmenSearch() {
    super();
  }

  @Override
  protected List<KandidaturKumulierteStimmen> suche(Wahl wahl, Gebiet gebietSelected, Ergebnis ergebnisSelected, KandidaturNachErfassungsgebietStimmenSearchParameters searchParameters) {
    Gebiet gebiet = gebietExtendedDAO.attach(gebietSelected);
    PolitischeGruppe politischeGruppe = politischeGruppeExtendedDAO.attach(searchParameters.getGruppe());

    if (gebiet == null || politischeGruppe == null) {
      return new ArrayList<>();
    }

    List<KandidaturKumulierteStimmen> result = new ArrayList<>();

    Kandidatur kandidatur = null;

    if (searchParameters.getKandidatur() != null) {
      kandidatur = kandidaturExtendedDAO.attach(searchParameters.getKandidatur());
    }

    boolean gruppeListenstimmenSummenErzeugen = isGruppeListenstimmenSeparatErfassbar(gebiet) && kandidatur == null; // ELECTKOM-2832, Summen nur, falls alle Listenkandidaturen angezeigt werden...

    StimmenPool stimmenPool = StimmenPool.createStimmenPoolFuerGruppe(stimmenExtendedDAO, ergebnisSelected, politischeGruppe);

    for (Gebiet gebietErmittelt : ermittleGebiete(gebiet, searchParameters, collectErfassteGebiete(ergebnisSelected))) {
      result.addAll(kandidaturKumulierteStimmenBuilder.erzeugeKandidaturKumulierteStimmenFuerGruppeAufGebiet(gebietErmittelt, politischeGruppe, gruppeListenstimmenSummenErzeugen,
          filterListenkandidaturen(listenkandidaturExtendedDAO.findByGruppeAndUngleichStatus(politischeGruppe, KandidaturStatus.NICHT_ZUGELASSEN), gebietErmittelt, kandidatur),
          filterDirektkandidaturen(direktkandidaturExtendedDAO.findByGruppeAndUngleichStatus(politischeGruppe, KandidaturStatus.NICHT_ZUGELASSEN), gebietErmittelt, kandidatur), getEntityAufStimmzettelDaten(),
          stimmenPool));
    }

    return result;
  }

  private Collection< ? extends Gebiet> ermittleGebiete(Gebiet gebietSelected, KandidaturNachErfassungsgebietStimmenSearchParameters searchParameters, Set<Gebiet> erfassteGebiete) {
    return searchParameters.getStimmabgabeart().filterGebieteWennErfassungsgebiet(searchParameters.getGebietsauswahl().getGebiete(gebietSelected), () -> erfassteGebiete).stream()
        .filter(gebiet -> !gebiet.isOberhalbVomWahlgebietUndSelbstKeinWahlgebiet()).collect(toList());
  }

  private List<Listenkandidatur> filterListenkandidaturen(List<Listenkandidatur> kandidaturen, Gebiet gebiet, Kandidatur kandidatur) {
    final Listenkandidatur listenkandidaturAusgewaehlt;
    if (kandidatur != null && kandidatur.isInstanceOf(Listenkandidatur.class)) {
      listenkandidaturAusgewaehlt = kandidatur.castTo(Listenkandidatur.class).getEntity();
    } else {
      listenkandidaturAusgewaehlt = null;
    }
    return kandidaturen.stream().filter(listenkandidatur -> //
    (kandidatur == null || listenkandidaturAusgewaehlt != null && listenkandidaturAusgewaehlt.equals(listenkandidatur))
        && (gebiet.isInGebietshierarchieUntergeordnetesGebietVonSchnelleVersionFuerMassenoperationen(listenkandidatur.getGebiet())
            || gebiet.isInGebietshierarchieUebergeordnetesGebietVon(listenkandidatur.getGebiet()))) //
        .collect(toList());
  }

  private List<Direktkandidatur> filterDirektkandidaturen(List<Direktkandidatur> kandidaturen, Gebiet gebiet, Kandidatur kandidatur) {
    final Direktkandidatur direktkandidaturAusgewaehlt;
    if (kandidatur != null && kandidatur.isInstanceOf(Direktkandidatur.class)) {
      direktkandidaturAusgewaehlt = kandidatur.castTo(Direktkandidatur.class).getEntity();
    } else {
      direktkandidaturAusgewaehlt = null;
    }
    return kandidaturen.stream().filter(direktkandidatur -> //
    (kandidatur == null || direktkandidaturAusgewaehlt != null && direktkandidaturAusgewaehlt.equals(direktkandidatur)) //
        && (gebiet.isInGebietshierarchieUntergeordnetesGebietVonSchnelleVersionFuerMassenoperationen(direktkandidatur.getGebiet()) //
            || gebiet.isInGebietshierarchieUebergeordnetesGebietVon(direktkandidatur.getGebiet()))) //
        .collect(toList());
  }

  @Override
  protected Comparator< ? super KandidaturKumulierteStimmen> getDefaultComparator() {
    return new KandidaturKumulierteStimmenByStimmzettelPositionComparator();
  }
}
