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

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

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

import de.ivu.elect.business.datei.entity.DateiExport;
import de.ivu.elect.business.ergebnis.boundary.ErgebnisDAO;
import de.ivu.elect.business.ergebnis.boundary.StimmenDAO;
import de.ivu.elect.business.ergebnis.control.StimmenPool;
import de.ivu.elect.business.ergebnis.entity.Ergebnisart;
import de.ivu.elect.business.exchange.control.AbstractExporter;
import de.ivu.elect.business.exchange.control.ExportExchange;
import de.ivu.elect.business.exchange.entity.DateiExportArt;
import de.ivu.elect.business.kandidatur.control.ListenkandidaturByStimmzettelPositionComparator;
import de.ivu.elect.business.kandidatur.control.WahlvorschlagExportContext;
import de.ivu.elect.business.kandidatur.control.WahlvorschlagExportExchange;
import de.ivu.elect.business.kandidatur.entity.Kandidatur;
import de.ivu.elect.business.kandidatur.entity.Listenkandidatur;
import de.ivu.elect.business.kandidatur.entity.WahlvorschlagExportObject;
import de.ivu.elect.business.stimmzettel.boundary.EntityAufStimmzettelDatenDAOFactory;
import de.ivu.elect.business.stimmzettel.entity.EntityAufStimmzettelDaten;
import de.ivu.elect.business.wahl.boundary.WahlDAO;
import de.ivu.elect.business.wahl.entity.Wahl;
import de.ivu.elect.business.wahlvorschlag.entity.Wahlvorschlag;

@Stateless
public class WahlvorschlagExporter extends AbstractExporter<WahlvorschlagExportObject, WahlvorschlagExportContext> {

  @Inject
  private transient EntityAufStimmzettelDatenDAOFactory entityAufStimmzettelDatenDAOFactory;

  @Inject
  private transient WahlvorschlagExportExchange wahlvorschlagExportExchange;

  @Inject
  private transient WahlDAO wahlDAO;

  @Inject
  private transient StimmenDAO stimmenDAO;

  @Inject
  private transient ErgebnisDAO ergebnisDAO;

  public WahlvorschlagExporter() {
    super();
  }

  @Override
  protected DateiExportArt getDateiExportArt() {
    return DateiExportArt.WAHLVORSCHLAG_EXPORT;
  }

  @Override
  protected boolean isExportHeader() {
    return true;
  }

  @Override
  public ExportExchange<WahlvorschlagExportObject, WahlvorschlagExportContext> getExchange() {
    return wahlvorschlagExportExchange;
  }

  @Override
  protected void processContextAndDateiExportIfNecessary(WahlvorschlagExportContext context, DateiExport dateiExport) {
    super.processContextAndDateiExportIfNecessary(context, dateiExport);

    context.setNameDerExportDatei(dateiExport.getDatei().getDateiname());
  }

  @Override
  public WahlvorschlagExportObject getExportObject(WahlvorschlagExportContext context) {
    WahlvorschlagExportObject result = new WahlvorschlagExportObject();

    EntityAufStimmzettelDaten entityAufStimmzettelDaten = entityAufStimmzettelDatenDAOFactory.getEntityAufStimmzettelDaten();

    Wahl wahl = wahlDAO.attach(context.getWahl());
    List<Wahlvorschlag> wahlvorschlaegeInWahl = new ArrayList<>(entityAufStimmzettelDaten.collectWahlvorschlaegeInWahl(wahl));

    if (!context.isNichtZugelasseneExportieren()) {
      removeNichtZugelasseneWahlvorschlaege(wahlvorschlaegeInWahl, new ArrayList<>(wahlvorschlaegeInWahl),
          StimmenPool.createStimmenPool(stimmenDAO, ergebnisDAO.findByWahlAndErgebnisart(wahl, Ergebnisart.VORPERIODE).get(0)));
    }

    if (!context.isUngepruefteExportieren()) {
      removeUngepruefteWahlvorschlaege(wahlvorschlaegeInWahl);
    }

    result.getWahlvorschlaege().addAll(wahlvorschlaegeInWahl);

    for (Wahlvorschlag wahlvorschlag : wahlvorschlaegeInWahl) {
      List< ? extends Kandidatur> kandidaturen = new ArrayList<>(wahlvorschlag.extractAllKandidaturen());

      if (wahlvorschlag.isListe()) {
        sortiereListenkandidaturenNachStimmzettelPosition(kandidaturen, entityAufStimmzettelDaten);
      }

      if (!context.isNichtZugelasseneExportieren()) {
        removeNichtZugelasseneKandidaturen(kandidaturen);
      }

      if (!context.isUngepruefteExportieren()) {
        removeUngepruefteKandidaturen(kandidaturen);
      }

      result.getKandidaturenPerWahlvorschlag().putIfAbsent(wahlvorschlag, new ArrayList<>());
      result.getKandidaturenPerWahlvorschlag().get(wahlvorschlag).addAll(kandidaturen);
    }

    return result;
  }

  private void sortiereListenkandidaturenNachStimmzettelPosition(List< ? extends Kandidatur> kandidaturen, EntityAufStimmzettelDaten entityAufStimmzettelDaten) {
    ListenkandidaturByStimmzettelPositionComparator listenkandidaturComparator = new ListenkandidaturByStimmzettelPositionComparator(entityAufStimmzettelDaten);

    // ELECT-3864
    kandidaturen.sort((o1, o2) -> {
      if (o1.isListenkandidatur() && o2.isListenkandidatur()) {
        return listenkandidaturComparator.compare(o1.castTo(Listenkandidatur.class).getEntity(), o2.castTo(Listenkandidatur.class).getEntity());
      }

      if (o1.isListenkandidatur()) {
        return -1;
      }

      if (o2.isListenkandidatur()) {
        return 1;
      }

      return 0;
    });
  }

  private void removeNichtZugelasseneWahlvorschlaege(List<Wahlvorschlag> wahlvorschlaege, List<Wahlvorschlag> wahlvorschlaegeInWahl, StimmenPool vorperiodeStimmen) {
    List<Wahlvorschlag> toRemove = new ArrayList<>();
    for (Wahlvorschlag wahlvorschlag : wahlvorschlaege) {
      if (wahlvorschlag.isNichtZugelassenImSinneAbgelehnt() || wahlvorschlag.getGruppeImGebiet().isIrrelevant(wahlvorschlaegeInWahl, vorperiodeStimmen)) {
        toRemove.add(wahlvorschlag);
      }
    }
    wahlvorschlaege.removeAll(toRemove);
  }

  private void removeUngepruefteWahlvorschlaege(List<Wahlvorschlag> wahlvorschlaege) {
    List<Wahlvorschlag> toRemove = new ArrayList<>();
    for (Wahlvorschlag wahlvorschlag : wahlvorschlaege) {
      if (wahlvorschlag.isUngeprueft()) {
        toRemove.add(wahlvorschlag);
      }
    }
    wahlvorschlaege.removeAll(toRemove);
  }

  private void removeNichtZugelasseneKandidaturen(List< ? extends Kandidatur> kandidaturen) {
    List<Kandidatur> toRemove = new ArrayList<>();
    for (Kandidatur kandidatur : kandidaturen) {
      if (kandidatur.isNichtZugelassenImSinneAbgelehnt()) {
        toRemove.add(kandidatur);
      }
    }
    kandidaturen.removeAll(toRemove);
  }

  private void removeUngepruefteKandidaturen(List< ? extends Kandidatur> kandidaturen) {
    List<Kandidatur> toRemove = new ArrayList<>();
    for (Kandidatur kandidatur : kandidaturen) {
      if (kandidatur.isUngeprueft()) {
        toRemove.add(kandidatur);
      }
    }
    kandidaturen.removeAll(toRemove);
  }
}
