
package de.ivu.elect.presentation.stimmeingabe;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.enterprise.context.RequestScoped;
import javax.enterprise.event.Observes;
import javax.enterprise.event.Reception;
import javax.inject.Inject;
import javax.inject.Named;

import de.ivu.elect.business.gebietsbaum.boundary.GebietSelectChangeEvent;
import de.ivu.elect.business.gebietsbaum.boundary.WahldurchfuehrungsgebietSessionSelect;
import de.ivu.elect.business.gebietsbaum.entity.Wahldurchfuehrungsgebiet;
import de.ivu.elect.business.stimmeingabe.entity.Ergebniseingangsart;
import de.ivu.elect.business.wahl.boundary.WahlSessionSelect;
import de.ivu.elect.business.wahl.entity.Wahldurchfuehrung;
import de.ivu.elect.jee.faces.Messages;
import de.ivu.elect.presentation.View;
import de.ivu.elect.presentation.gebietsbaum.status.ErgebniseingangStatusEntry;
import de.ivu.elect.presentation.stimmeingabe.status.ErgebniseingangErfassungsstatusPieChartModelBuilder;
import de.ivu.elect.presentation.stimmeingabe.status.GebietErfassungsstatusInErgebniseingabestufe;

@RequestScoped
@Named(ErgebniseingangStatusViewHelper.COMPONENT_NAME)
public class ErgebniseingangStatusViewHelper implements Serializable {

  private static final long serialVersionUID = 1L;

  public static final String COMPONENT_NAME = "ergebniseingangStatusViewHelper";

  public static final int ENTRIES_SCHRITTWEISE_LADEN_SCHRITTGROESSE = 200; // Wert erhöht, da Seite 'deferred' geladen wird

  @Inject
  private transient Messages messages;

  @View
  @Inject
  private transient ErgebniseingangStatusView ergebniseingangStatusView;

  @Inject
  private transient ErgebniseingangStatusViewCollapseState ergebniseingangStatusViewCollapseState;

  @Inject
  private transient ErgebniseingangErfassungsstatusPieChartModelBuilder ergebniseingangErfassungsstatusPieChartModelBuilder;

  @Inject
  private transient WahldurchfuehrungsgebietSessionSelect wahldurchfuehrungsgebietSessionSelect;

  @Inject
  private transient StimmeingabeErfassenViewErgebniseingangsartStore stimmeingabeErfassenViewErgebniseingangsartStore;

  @Inject
  private transient WahlSessionSelect wahlSessionSelect;

  private Map<GebietErfassungsstatusInErgebniseingabestufe, List<ErgebniseingangStatusEntry>> statusNachGebietErfassungsstatus;

  private List<GebietErfassungsstatusInErgebniseingabestufe> vorhandeneGebietErfassungsstatus;

  public ErgebniseingangStatusViewHelper() {
    super();
  }

  public List<GebietErfassungsstatusInErgebniseingabestufe> getVorhandeneGebietErfassungsstatus() {
    if (vorhandeneGebietErfassungsstatus == null) {
      vorhandeneGebietErfassungsstatus = new ArrayList<>();

      if (wahldurchfuehrungsgebietSessionSelect.isSelected()) {
        Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet = wahldurchfuehrungsgebietSessionSelect.getEntity();
        Wahldurchfuehrung wahldurchfuehrung = wahldurchfuehrungsgebiet.getWahldurchfuehrung();

        Map<GebietErfassungsstatusInErgebniseingabestufe, Long> anzahlErfassungsgebieteInLetztenErgebniseingaengenAktuell = ergebniseingangErfassungsstatusPieChartModelBuilder
            .countAnzahlErfassungsgebieteInLetztenErgebniseingaengenAktuell(wahldurchfuehrung, wahldurchfuehrungsgebiet, getErgebniseingangsart());

        List<GebietErfassungsstatusInErgebniseingabestufe> gebietErfassungsstatusInErgebniseingabestufeAuswaehlbar = ergebniseingangStatusViewCollapseState.getEingangsStatusViewGebietAnzeige()
            .getGebietErfassungsstatusList(ergebniseingangErfassungsstatusPieChartModelBuilder.collectErgebniseingabestufen(wahldurchfuehrung));

        for (Entry<GebietErfassungsstatusInErgebniseingabestufe, Long> entry : anzahlErfassungsgebieteInLetztenErgebniseingaengenAktuell.entrySet()) {
          GebietErfassungsstatusInErgebniseingabestufe gebietErfassungsstatusInErgebniseingabestufe = entry.getKey();

          if (gebietErfassungsstatusInErgebniseingabestufeAuswaehlbar.contains(gebietErfassungsstatusInErgebniseingabestufe)) {
            Long anzahl = entry.getValue();

            if (anzahl != null && anzahl > 0L) {
              vorhandeneGebietErfassungsstatus.add(gebietErfassungsstatusInErgebniseingabestufe);
            }
          }
        }

        vorhandeneGebietErfassungsstatus.sort(GebietErfassungsstatusInErgebniseingabestufe.DEFAULT_COMPARATOR);
      }
    }
    return vorhandeneGebietErfassungsstatus;
  }

  public List<ErgebniseingangStatusEntry> getStatusEntriesFuer(GebietErfassungsstatusInErgebniseingabestufe gebietErfassungsstatus) {
    return getStatusNachGebietErfassungsstatus().get(gebietErfassungsstatus);
  }

  public CollapsedProperty getCollapsed(GebietErfassungsstatusInErgebniseingabestufe gebietErfassungsstatus) {
    return getCollapsed(gebietErfassungsstatus, getStatusNachGebietErfassungsstatus());
  }

  private CollapsedProperty getCollapsed(GebietErfassungsstatusInErgebniseingabestufe gebietErfassungsstatus,
      Map<GebietErfassungsstatusInErgebniseingabestufe, List<ErgebniseingangStatusEntry>> statusEntriesNachErfassungsstatus) {
    return ergebniseingangStatusViewCollapseState.getCollapsedPropertyForGebietErfassungsstatus(gebietErfassungsstatus,
        bestimmeDefaultCollapsedState(gebietErfassungsstatus, statusEntriesNachErfassungsstatus));
  }

  protected boolean bestimmeDefaultCollapsedState(GebietErfassungsstatusInErgebniseingabestufe gebietErfassungsstatus,
      Map<GebietErfassungsstatusInErgebniseingabestufe, List<ErgebniseingangStatusEntry>> statusEntriesNachErfassungsstatus) {
    return bestimmeAnzahlGebiete(statusEntriesNachErfassungsstatus.get(gebietErfassungsstatus)) >= ENTRIES_SCHRITTWEISE_LADEN_SCHRITTGROESSE;
  }

  private int bestimmeAnzahlGebiete(List<ErgebniseingangStatusEntry> ergebniseingangStatusEntries) {
    if (ergebniseingangStatusEntries == null) {
      return 0;
    }

    return (int) ergebniseingangStatusEntries.stream().filter(ergebniseingangStatusEntry -> !isSeparator(ergebniseingangStatusEntry)).count();
  }

  private boolean isSeparator(ErgebniseingangStatusEntry ergebniseingangStatusEntry) {
    return ergebniseingangStatusEntry.isSeparator();
  }

  public Map<GebietErfassungsstatusInErgebniseingabestufe, List<ErgebniseingangStatusEntry>> getStatusNachGebietErfassungsstatus() {
    if (statusNachGebietErfassungsstatus == null) {
      statusNachGebietErfassungsstatus = new HashMap<>();

      for (ErgebniseingangStatusEntry ergebniseingangStatusEntry : ergebniseingangStatusView.getStatusEntries()) {
        if (isSeparator(ergebniseingangStatusEntry)) {
          for (List<ErgebniseingangStatusEntry> gebietsbaumStatusEntries : statusNachGebietErfassungsstatus.values()) {
            if (!gebietsbaumStatusEntries.isEmpty() && !isLetzterEintragSeparator(gebietsbaumStatusEntries)) {
              gebietsbaumStatusEntries.add(ergebniseingangStatusEntry);
            }
          }
        } else {
          statusNachGebietErfassungsstatus.computeIfAbsent(ergebniseingangStatusEntry.getGebietErfassungsstatus(), egal -> new ArrayList<>()).add(ergebniseingangStatusEntry);
        }
      }
    }

    return statusNachGebietErfassungsstatus;
  }

  public String lokalisiere(GebietErfassungsstatusInErgebniseingabestufe gebietErfassungsstatus) {
    return formatMitAnzahlVorkommen(gebietErfassungsstatus, getStatusNachGebietErfassungsstatus());
  }

  private String formatMitAnzahlVorkommen(GebietErfassungsstatusInErgebniseingabestufe gebietErfassungsstatus,
      Map<GebietErfassungsstatusInErgebniseingabestufe, List<ErgebniseingangStatusEntry>> statusEntriesNachErfassungsstatus) {
    int anzahl = bestimmeAnzahlGebiete(statusEntriesNachErfassungsstatus.get(gebietErfassungsstatus));

    return gebietErfassungsstatus.getMessage(messages, "ergebnis.eingang.status") + " (" + anzahl + ")";
  }

  private boolean isLetzterEintragSeparator(List<ErgebniseingangStatusEntry> ergebniseingangStatusEntries) {
    if (ergebniseingangStatusEntries.isEmpty()) {
      return false;
    }

    return isSeparator(ergebniseingangStatusEntries.get(ergebniseingangStatusEntries.size() - 1));
  }

  public EingangsStatusViewGebietAnzeige getEingangsStatusViewGebietAnzeige() {
    return ergebniseingangStatusViewCollapseState.getEingangsStatusViewGebietAnzeige();
  }

  public boolean isButtonZurAuswahlEingangsStatusAnzeigen() {
    return ergebniseingangStatusViewCollapseState.isButtonZurAuswahlEingangsStatusAnzeigen();
  }

  public void setEingangsStatusViewGebietAnzeige(EingangsStatusViewGebietAnzeige eingangsStatusViewGebietAnzeige) {
    ergebniseingangStatusViewCollapseState.setEingangsStatusViewGebietAnzeige(eingangsStatusViewGebietAnzeige);
  }

  public List<EingangsStatusViewGebietAnzeige> getEingangsStatusViewGebietAnzeigen() {
    return EingangsStatusViewGebietAnzeige.getAuswahl(ergebniseingangErfassungsstatusPieChartModelBuilder.isGebietErfassungsstatusUnvollstaendigAnzeigen());
  }

  public Ergebniseingangsart getErgebniseingangsart() {
    return getStimmeingabeErfassenViewErgebniseingangsartStore().getErgebniseingangsart(wahlSessionSelect.getEntity());
  }

  private StimmeingabeErfassenViewErgebniseingangsartStore getStimmeingabeErfassenViewErgebniseingangsartStore() {
    return stimmeingabeErfassenViewErgebniseingangsartStore;
  }

  public void onGebietSelectChangeEvent(@Observes(notifyObserver = Reception.IF_EXISTS) GebietSelectChangeEvent event) {
    reset();
  }

  public void reset() {
    statusNachGebietErfassungsstatus = null;
    vorhandeneGebietErfassungsstatus = null;
  }
}
