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

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

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import javax.inject.Inject;

import de.ivu.elect.business.ergebnis.boundary.ErgebnisDAO;
import de.ivu.elect.business.ergebnis.boundary.GewaehltDAO;
import de.ivu.elect.business.ergebnis.boundary.SitzeDAO;
import de.ivu.elect.business.ergebnis.boundary.StimmenDAO;
import de.ivu.elect.business.ergebnis.entity.AbstractErgebnis;
import de.ivu.elect.business.ergebnis.entity.Ergebnis;
import de.ivu.elect.business.ergebnis.entity.Ergebnisart;
import de.ivu.elect.business.ergebnis.entity.Gewaehlt;
import de.ivu.elect.business.ergebnis.entity.Sitze;
import de.ivu.elect.business.ergebnis.entity.Stimmen;
import de.ivu.elect.business.fremdkandidatur.boundary.FremdkandidaturDAO;
import de.ivu.elect.business.gebietsbaum.boundary.GebietDAO;
import de.ivu.elect.business.gebietsbaum.boundary.GebietPolygonPointDAO;
import de.ivu.elect.business.gebietsbaum.boundary.GebietsgruppierungDAO;
import de.ivu.elect.business.gruppe.boundary.GruppeDAO;
import de.ivu.elect.business.gruppe.entity.SystemGruppeEigenschaften;
import de.ivu.elect.business.gruppeimgebiet.boundary.GruppeImGebietDAO;
import de.ivu.elect.business.gruppeimgebiet.control.GruppeImGebietReihenfolgeVorsortierer;
import de.ivu.elect.business.internet.boundary.InternetSeiteDAO;
import de.ivu.elect.business.kandidatenportal.boundary.WvpWarnungDAO;
import de.ivu.elect.business.kandidatur.boundary.KandidaturDAO;
import de.ivu.elect.business.kandidatur.control.StimmzettelFormatter;
import de.ivu.elect.business.person.boundary.PersonenVerknuepfungDAO;
import de.ivu.elect.business.pruefung.boundary.ZulassungPrueferDAOFactory;
import de.ivu.elect.business.staat.boundary.StaatenGruppeDAO;
import de.ivu.elect.business.staat.entity.StaatenGruppeTyp;
import de.ivu.elect.business.stimmeingabe.boundary.StimmeingabePlausiKonfigurationDAO;
import de.ivu.elect.business.stimmzettel.boundary.EntityAufStimmzettelDatenStimmzettelPositionBerechnung;
import de.ivu.elect.business.stimmzettel.boundary.StimmzettelDAO;
import de.ivu.elect.business.stimmzettel.boundary.StimmzettelKopfdatumDAO;
import de.ivu.elect.business.veranstaltung.boundary.VeranstaltungDAO;
import de.ivu.elect.business.wahl.control.Wahldaten;
import de.ivu.elect.business.wahl.entity.Wahl;
import de.ivu.elect.business.wahl.entity.WahlParameterLoader;
import de.ivu.elect.business.wahlausschuss.boundary.WahlausschussEntschaedigungDAO;
import de.ivu.elect.business.wahlausschuss.boundary.WahlausschussmitgliedDAO;
import de.ivu.elect.business.wahlbeteiligung.boundary.WahlbeteiligungVerlaufDAO;
import de.ivu.elect.business.wahlrechtspruefung.boundary.PruefkriteriumDAO;
import de.ivu.elect.business.wahlrechtspruefung.boundary.WahlrechtspruefungDAO;
import de.ivu.elect.business.wahlvorschlag.boundary.WahlvorschlagDAO;
import de.ivu.elect.business.wahlvorschlagsperson.boundary.WahlvorschlagsPersonDAO;

public class WahldatenCreator implements Serializable {

  private static final long serialVersionUID = 1L;

  @Inject
  private transient SystemGruppeEigenschaften systemGruppeEigenschaften;

  @Inject
  private transient WahlvorschlagsPersonDAO wahlvorschlagsPersonDAO;

  @Inject
  private transient WahlausschussmitgliedDAO wahlausschussmitgliedDAO;

  @Inject
  private transient StaatenGruppeDAO staatenGruppeDAO;

  @Inject
  private transient GebietsgruppierungDAO gebietsgruppierungDAO;

  @Inject
  private transient WahlausschussEntschaedigungDAO wahlausschussEntschaedigungDAO;

  @Inject
  private transient WahlParameterLoader wahlParameterLoader;

  @Inject
  private transient ZulassungPrueferDAOFactory zulassungPrueferDAOFactory;

  @Inject
  private transient StimmzettelFormatter stimmzettelFormatter;

  @Inject
  private transient EntityAufStimmzettelDatenStimmzettelPositionBerechnung entityAufStimmzettelDatenStimmzettelPositionBerechnung;

  @Inject
  private transient GruppeImGebietReihenfolgeVorsortierer gruppeImGebietReihenfolgeVorsortierer;

  @Inject
  private transient GebietPolygonPointDAO gebietPolygonPointDAO;

  @Inject
  private transient FremdkandidaturDAO fremdkandidaturDAO;

  @Inject
  private transient InternetSeiteDAO internetSeiteDAO;

  @Inject
  private transient WahlbeteiligungVerlaufDAO wahlbeteiligungVerlaufDAO;

  @Inject
  private transient StimmeingabePlausiKonfigurationDAO stimmeingabePlausiKonfigurationDAO;

  @Inject
  private transient VeranstaltungDAO veranstaltungDAO;

  @Inject
  private transient GebietDAO gebietDAO;

  @Inject
  private transient GruppeDAO gruppeDAO;

  @Inject
  private transient GruppeImGebietDAO gruppeImGebietDAO;

  @Inject
  private transient WahlvorschlagDAO wahlvorschlagDAO;

  @Inject
  private transient KandidaturDAO kandidaturDAO;

  @Inject
  private transient PersonenVerknuepfungDAO personenVerknuepfungDAO;

  @Inject
  private transient StimmenDAO stimmenDAO;

  @Inject
  private transient SitzeDAO sitzeDAO;

  @Inject
  private transient GewaehltDAO gewaehltDAO;

  @Inject
  private transient ErgebnisDAO ergebnisDAO;

  @Inject
  private transient StimmzettelDAO stimmzettelDAO;

  @Inject
  private transient StimmzettelKopfdatumDAO stimmzettelKopfdatumDAO;

  @Inject
  private transient PruefkriteriumDAO pruefkriteriumDAO;

  @Inject
  private transient WahlrechtspruefungDAO wahlrechtspruefungDAO;

  @Inject
  private transient WvpWarnungDAO wvpWarnungDAO;

  public WahldatenCreator() {
    super();
  }

  public Wahldaten createWahldaten(Wahl wahl) {
    return new Wahldaten<>(wahl, () -> gebietDAO.findByWahl(wahl), () -> gruppeDAO.findByWahl(wahl), () -> gruppeImGebietDAO.findByWahl(wahl), () -> wahlvorschlagDAO.findByWahl(wahl),
        () -> kandidaturDAO.findByWahl(wahl)/* ELECTKOM-117 */.stream().filter(k -> k.getWahlvorschlag() != null).collect(toList()), () -> personenVerknuepfungDAO.findByWahl(wahl),
        ergebnisart -> getStimmen(wahl, ergebnisart), () -> getGewaehltVorperiode(wahl), () -> getSitzeVorperiode(wahl), () -> wahlvorschlagsPersonDAO.findByWahlOhneUnterstuetzer(wahl),
        () -> wahlausschussmitgliedDAO.findByWahl(wahl), () -> wahlausschussEntschaedigungDAO.findByWahl(wahl),
        () -> staatenGruppeDAO.findByWahlAndStaatenGruppeTyp(wahl, asList(StaatenGruppeTyp.values())), () -> gebietsgruppierungDAO.findByWahl(wahl), systemGruppeEigenschaften, wahlParameterLoader,
        zulassungPrueferDAOFactory.createZulassungPruefer(), stimmzettelFormatter, entityAufStimmzettelDatenStimmzettelPositionBerechnung, gruppeImGebietReihenfolgeVorsortierer,
        () -> gebietPolygonPointDAO.findByWahl(wahl), () -> fremdkandidaturDAO.findByWahl(wahl), () -> internetSeiteDAO.findByWahl(wahl), () -> wahlbeteiligungVerlaufDAO.findByWahl(wahl),
        () -> stimmeingabePlausiKonfigurationDAO.findByWahl(wahl), () -> veranstaltungDAO.findByWahlOrderByBeginn(wahl), () -> stimmzettelDAO.findByWahl(wahl),
        () -> stimmzettelKopfdatumDAO.findByWahl(wahl), () -> pruefkriteriumDAO.findByWahlOrderByPosition(wahl), () -> wahlrechtspruefungDAO.findByWahl(wahl), () -> wvpWarnungDAO.findByWahl(wahl));
  }

  private Ergebnis getErgebnis(Wahl wahl, Ergebnisart ergebnisart) {
    return AbstractErgebnis.getErgebnisZurWahlNachErgebnisart(ergebnisart, wahl, ergebnisDAO);
  }

  private Ergebnis getErgebnisVorperiode(Wahl wahl) {
    return getErgebnis(wahl, Ergebnisart.VORPERIODE);
  }

  private List<Gewaehlt> getGewaehltVorperiode(Wahl wahl) {
    Ergebnis ergebnisVorperiode = getErgebnisVorperiode(wahl);

    if (ergebnisVorperiode == null) {
      return new ArrayList<>();
    } else {
      return gewaehltDAO.findByErgebnis(ergebnisVorperiode);
    }
  }

  private List<Sitze> getSitzeVorperiode(Wahl wahl) {
    Ergebnis ergebnisVorperiode = getErgebnisVorperiode(wahl);

    if (ergebnisVorperiode == null) {
      return new ArrayList<>();
    } else {
      return sitzeDAO.findByErgebnis(ergebnisVorperiode);
    }
  }

  private List<Stimmen> getStimmen(Wahl wahl, Ergebnisart ergebnisart) {
    Ergebnis ergebnis = getErgebnis(wahl, ergebnisart);

    if (ergebnis == null) {
      return Collections.emptyList();
    } else {
      return stimmenDAO.findByErgebnisNoCache(ergebnis);
    }
  }
}
