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

import static java.util.Collections.emptyList;

import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

import javax.ejb.Stateless;
import javax.inject.Inject;

import de.ivu.elect.business.ergebnis.control.StimmenPool;
import de.ivu.elect.business.ergebnis.control.StimmenPoolExtendedDAOFactory;
import de.ivu.elect.business.gebietsbaum.boundary.GebietSessionSelect;
import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
import de.ivu.elect.business.gruppe.entity.GruppeArt;
import de.ivu.elect.business.gruppeimgebiet.boundary.GruppeImGebietSearchParameters.StatusOption;
import de.ivu.elect.business.gruppeimgebiet.control.GruppeImGebietByPositionFortlaufendComparator;
import de.ivu.elect.business.gruppeimgebiet.control.GruppeImGebietByRelativePositionComparator;
import de.ivu.elect.business.gruppeimgebiet.control.GruppeImGebietPositionFortlaufendNummerierer;
import de.ivu.elect.business.gruppeimgebiet.control.GruppeImGebietReihenfolgeVorsortierer;
import de.ivu.elect.business.gruppeimgebiet.control.GruppeImGebietUebrigeEinsortierer;
import de.ivu.elect.business.gruppeimgebiet.entity.GruppeImGebiet;
import de.ivu.elect.business.wahl.entity.Wahl;
import de.ivu.elect.business.wahlvorschlag.entity.Wahlvorschlag;
import de.ivu.elect.jee.entity.EagerLoadingCollector;
import de.ivu.elect.presentation.wahlvorschlag.WahlvorschlagRequestCache;

/**
 * Die ganze Klasse ist Mist, nicht als Vorbild nehmen...
 */
@Stateless
public class GruppeImGebietSearch extends AbstractGruppeImGebietSearch {

  private static final Comparator<GruppeImGebiet> REFERENDUM_VORLAGE_DEFAULT_COMPARATOR = new GruppeImGebietByRelativePositionComparator();

  @Inject
  protected transient GebietSessionSelect gebietSelect;

  @Inject
  private transient GruppeImGebietUebrigeEinsortierer gruppeImGebietUebrigeEinsortierer;

  @Inject
  private transient GruppeImGebietReihenfolgeVorsortierer gruppeImGebietReihenfolgeVorsortierer;

  @Inject
  private transient GruppeImGebietPositionFortlaufendNummerierer gruppeImGebietPositionFortlaufendNummerierer;

  @Inject
  private transient StimmenPoolExtendedDAOFactory stimmenPoolExtendedDAOFactory;

  @Inject
  private transient WahlvorschlagRequestCache wahlvorschlagRequestCache;

  public GruppeImGebietSearch() {
    super();
  }

  public List<GruppeImGebiet> getSearchResult(ReferendumVorlageImGebietSearchParameters searchParameters, EagerLoadingCollector<GruppeImGebiet> eagerLoadingCollector) {
    List<GruppeImGebiet> result = dao.findByGebietAndGruppeArt(searchParameters.getGebiet(), searchParameters.getGruppeArt());
    result.sort(REFERENDUM_VORLAGE_DEFAULT_COMPARATOR);

    return eagerLoadingCollector.eagerLoad(result);
  }

  public List<GruppeImGebiet> getSearchResultOhneUebrigeImGebiet(GruppeImGebietSearchParameters searchParameters) {
    return querySearchResult(searchParameters, false);
  }

  @Override
  protected List<GruppeImGebiet> querySearchResult(GruppeImGebietSearchParameters searchParameters) {
    return querySearchResult(searchParameters, true);
  }

  private List<GruppeImGebiet> querySearchResult(GruppeImGebietSearchParameters searchParameters, boolean isUebrigeInGebietEinsortieren) {
    Gebiet gebiet = bestimmeGebietDessenReihenfolgeAngezeigtWird();

    if (gebiet == null) {
      return emptyList();
    }

    List<GruppeImGebiet> result = dao.findByGebietAndUngleichGruppeArtOrderByRelativePosition(gebiet, GruppeArt.SYSTEM_GRUPPE);

    boolean einzelbewerberAnzeigen = searchParameters.isEinzelbewerberAnzeigen();

    if (!einzelbewerberAnzeigen) {
      entferneEinzelbewerber(result);
    }
    Wahl wahl = gebiet.getWahl();
    StimmenPool stimmenVorperiode = stimmenPoolExtendedDAOFactory.createStimmenPoolVorperiode(wahl);
    List<Wahlvorschlag> alleWahlvorschlaege = wahlvorschlagRequestCache.getAlleWahlvorschlaege(wahl);

    List<GruppeImGebiet> irrelevante = bestimmeIrrelevante(result, stimmenVorperiode, alleWahlvorschlaege);

    if (StatusOption.RELEVANTE.equals(searchParameters.getStatusOption())) {
      result.removeAll(irrelevante);
    } else if (StatusOption.IRRELEVANTE.equals(searchParameters.getStatusOption())) {
      result = irrelevante;
    }

    gruppeImGebietReihenfolgeVorsortierer.sortiere(wahl, result, alleWahlvorschlaege, stimmenPoolExtendedDAOFactory);

    if (isUebrigeInGebietEinsortieren && !StatusOption.IRRELEVANTE.equals(searchParameters.getStatusOption())) {

      if (einzelbewerberAnzeigen) {
        gruppeImGebietUebrigeEinsortierer.sortiereUebrigeImGebietEinMitEinzelbewerbern(result, gebiet, irrelevante, stimmenVorperiode);
      } else {
        gruppeImGebietUebrigeEinsortierer.sortiereUebrigeImGebietEinOhneEinzelbewerbern(result, gebiet, irrelevante, stimmenVorperiode);
      }
    }

    gruppeImGebietPositionFortlaufendNummerierer.nummerierePositionFortlaufend(result);

    result.sort(new GruppeImGebietByPositionFortlaufendComparator());

    return result;
  }

  protected List<GruppeImGebiet> bestimmeIrrelevante(List<GruppeImGebiet> gruppenImGebiet, StimmenPool stimmenVorperiode, List<Wahlvorschlag> alleWahlvorschlaege) {
    return gruppenImGebiet.stream().filter(gruppeImGebiet -> gruppeImGebiet.isIrrelevant(alleWahlvorschlaege, stimmenVorperiode)).collect(Collectors.toList());
  }

  public Gebiet bestimmeGebietDessenReihenfolgeAngezeigtWird() {
    Gebiet gebiet = gebietSelect.getEntity();
    while (gebiet != null && !gebiet.isWahlOderDirektwahlOderListenwahlgebietOderHatEntprechendesUntergebiet()) {
      gebiet = gebiet.getUebergeordnetesGebiet();
    }
    return gebiet;
  }

  private void entferneEinzelbewerber(List<GruppeImGebiet> gruppenImGebiet) {
    for (int i = gruppenImGebiet.size() - 1; i >= 0; i--) {
      // Rückwärts wegen remove
      GruppeImGebiet gruppeImGebiet = gruppenImGebiet.get(i);
      if (gruppeImGebiet.isEinzelbewerber()) {
        gruppenImGebiet.remove(gruppeImGebiet);
      }
    }
  }
}
