
package de.ivu.elect.presentation.ergebnis;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;

import javax.enterprise.event.Observes;
import javax.enterprise.event.Reception;
import javax.inject.Inject;

import org.primefaces.model.chart.PieChartModel;
import org.primefaces.model.charts.donut.DonutChartModel;

import de.ivu.elect.business.ergebnis.boundary.AbstractSitzeDAO;
import de.ivu.elect.business.ergebnis.boundary.SitzeExtendedDAO;
import de.ivu.elect.business.ergebnis.boundary.SitzeFactory;
import de.ivu.elect.business.ergebnis.boundary.SitzeSearchParameters;
import de.ivu.elect.business.ergebnis.boundary.SitzeZusammenfasser;
import de.ivu.elect.business.ergebnis.control.SitzeByAnzahlAbsteigendComparator;
import de.ivu.elect.business.ergebnis.entity.Ergebnis;
import de.ivu.elect.business.ergebnis.entity.Sitze;
import de.ivu.elect.business.ergebnis.entity.SitzeZusammengefasst;
import de.ivu.elect.business.gebietsbaum.boundary.GebietSelectChangeEvent;
import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
import de.ivu.elect.business.gruppe.boundary.GruppeFactory;
import de.ivu.elect.business.gruppe.entity.Gruppe;
import de.ivu.elect.business.wahl.entity.Wahl;
import de.ivu.elect.jee.faces.FacesContextUtil;
import de.ivu.elect.jee.presentation.charts.PieChartModelBuilder;
import de.ivu.elect.presentation.ergebnis.table.SitzeColumnBuilder;
import de.ivu.elect.presentation.gebietsbaum.tree.AbstractNoStoreGebietsbaumView;
import de.ivu.elect.presentation.security.AbstractGebietSecurityFilter;

public abstract class AbstractCoreSitzeSearchView extends AbstractSitzeSearchView {

  public static final String PSEUDO_GRUPPE_SCHLUESSEL_SUMME = "###SUMME###";

  private static final long serialVersionUID = 1L;

  @Inject
  private transient SitzeColumnBuilder sitzeColumnBuilder;

  @Inject
  private transient AbstractNoStoreGebietsbaumView abstractNoStoreGebietsbaumView;

  @Inject
  private transient SitzeExtendedDAO extendedDAO;

  @Inject
  private transient SitzeFactory sitzeFactory;

  @Inject
  private transient GruppeFactory gruppeFactory;

  @Inject
  private transient SitzeZusammenfasser sitzeZusammenfasser;

  protected AbstractCoreSitzeSearchView() {
    super();
  }

  @Override
  protected AbstractGebietSecurityFilter<Sitze> getGebietSecurityFilter() {
    return KEIN_GEBIET_SECURITY_FILTER;
  }

  @Override
  public boolean isSupportsLazyLoading() {
    return false; // Damit der Chart angezeigt wird
  }

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

    Gebiet neueEntity = event.getNeueEntity();

    if (neueEntity != null) { // ELECTDD-404 Abmeldung und nur, wenn der View für die aktuelle Wahl
      if (VIEW_ID.equals(FacesContextUtil.getCurrentViewId())) {
        abstractNoStoreGebietsbaumView.reset();

        FacesContextUtil.redirectToView(VIEW_ID); // Seite neu laden, da Lazy-Loading nicht unterstützt wird...
      }
    }
  }

  public boolean isPieChartAnzeigen() {
    return !isEmptySearchResultDisplayed() && isSitzeVorhanden();
  }

  /**
   * @deprecated Nicht mehr die Primefaces Charts verwenden, optisch keine Schönheit...
   */
  @Deprecated
  public PieChartModel getPieChartModel() {
    // Falls doch wieder verwendet, dann PieChartModelBuilder#getDefaultInstance() verwenden!
    PieChartModelBuilder pieChartModelBuilder = new PieChartModelBuilder();
    pieChartModelBuilder.setDiameter(200);

    List<Sitze> alleSitze = getSearchResultForChart();
    for (Sitze sitze : alleSitze) {
      addToPieChart(pieChartModelBuilder, sitze);
    }

    PieChartModel pieChartModel = pieChartModelBuilder.buildModel();
    pieChartModel.setLegendCols((int) Math.floor(alleSitze.size() / 10.0d) + 1);
    // pieChartModel.setLegendPlacement(LegendPlacement.OUTSIDE);
    pieChartModel.setLegendPosition("e");

    return pieChartModel;
  }

  private transient DonutChartModel pieChartJsModel;

  public DonutChartModel getPieChartJsModel() {
    if (pieChartJsModel == null) {
      return new SitzeDonutChartModelBuilder(sitzeColumnBuilder).getChartModel(getSearchResultForChart());// ELECTVAPP-741
    }

    return pieChartJsModel;
  }

  private void addToPieChart(PieChartModelBuilder pieChartModelBuilder, Sitze sitze) {
    Long anzahlSitze = sitze.getAnzahl();

    if (anzahlSitze > 0L) {
      String gruppeFarbe = sitze.getGruppe().getFarbe();

      if (gruppeFarbe == null) {
        gruppeFarbe = "FFFFFF";
      }

      pieChartModelBuilder.add(sitze.getGruppe().getName() + ": " + anzahlSitze, anzahlSitze, gruppeFarbe);
    }
  }

  private List<Sitze> getSearchResultForChart() {
    return filterUndSortiereFuerDonut(getSearchResult());
  }

  private List<Sitze> filterUndSortiereFuerDonut(List<Sitze> searchResult) {
    return searchResult.stream() //
        .filter(sitze -> sitze.getAnzahl() > 0L && !PSEUDO_GRUPPE_SCHLUESSEL_SUMME.equals(sitze.getGruppe().getSchluessel())) //
        .sorted(new SitzeByAnzahlAbsteigendComparator()) //
        .collect(Collectors.toCollection(() -> new ArrayList<>(searchResult.size())));
  }

  @Override
  public SitzeSearchParameters getSearchParameters() {
    SitzeSearchParameters searchParameters1 = super.getSearchParameters();
    searchParameters1.setErgebnis(getErgebnis());
    searchParameters1.setGebiet(getSelectedGebiet());
    return searchParameters1;
  }

  protected abstract Ergebnis getErgebnis();

  public String getGebietName() {
    return getSelectedGebietNameFormatted();
  }

  public String getErgebnisName() {
    Ergebnis ergebnis = getErgebnis();

    if (ergebnis == null) {
      return "";
    }

    return ergebnis.getName();
  }

  @Override
  public void presetSearchParameters() {
    getSearchParameters();
  }

  public boolean isSitzeVorhanden() {
    return getSearchResult().stream().anyMatch(sitze -> sitze.getAnzahl() > 0L || sitze.getAnzahlAnspruch() > 0L);
  }

  @Override
  public void resetSearchResult() {
    super.resetSearchResult();

    pieChartJsModel = null;
  }

  @Override
  protected List<Sitze> performSearch() {
    List<Sitze> result = super.performSearch();
    addSummeEntry(result);
    return result;
  }

  private void addSummeEntry(List<Sitze> result) {
    if (!result.isEmpty()) {
      Gruppe pseudogruppeSumme = createPseudogruppeFuerSumme();

      long anzahl = 0L;
      long anzahlAnspruch = 0L;

      long anzahlVorperiode = 0L;
      long anzahlAnspruchVorperiode = 0L;

      for (Sitze sitze : result) {
        anzahl += sitze.getAnzahl();
        anzahlAnspruch += sitze.getAnzahlAnspruch();

        if (sitze instanceof SitzeZusammengefasst) {
          SitzeZusammengefasst sitzeZusammengefasst = (SitzeZusammengefasst) sitze;
          Sitze sitzeVorperiode = sitzeZusammengefasst.getSitzeVorperiode(getSelectedWahl(), extendedDAO);

          anzahlVorperiode += sitzeVorperiode.getAnzahl();
          anzahlAnspruchVorperiode += sitzeVorperiode.getAnzahlAnspruch();
        }
      }

      long sitzeIdFuerDataTable = UUID.randomUUID().getMostSignificantBits();

      long finalAnzahlVorperiode = anzahlVorperiode;
      long finalAnzahlAnspruchVorperiode = anzahlAnspruchVorperiode;

      SitzeZusammengefasst summeZusammengefasst = new SitzeZusammengefasst(sitzeZusammenfasser, sitzeIdFuerDataTable, pseudogruppeSumme, getSelectedGebiet(), anzahl, anzahlAnspruch,
          sitzeZusammenfasser.fasseSitzeZusammen(1L, pseudogruppeSumme, null, result).getCustomData()) {

        private static final long serialVersionUID = 1L;

        @Override
        public Sitze getSitzeVorperiode(Wahl wahl, AbstractSitzeDAO sitzeDAO) {
          Sitze summeVorperiode = sitzeFactory.create();
          summeVorperiode.setGruppe(getGruppe());
          summeVorperiode.setGebiet(getGebiet());
          summeVorperiode.setAnzahl(finalAnzahlVorperiode);
          summeVorperiode.setAnzahlAnspruch(finalAnzahlAnspruchVorperiode);

          return summeVorperiode;
        }
      };

      result.add(0, summeZusammengefasst);
    }
  }

  private Gruppe createPseudogruppeFuerSumme() {
    Gruppe pseudogruppeSumme = gruppeFactory.create();
    pseudogruppeSumme.setSchluessel(PSEUDO_GRUPPE_SCHLUESSEL_SUMME);
    pseudogruppeSumme.setName(getMessage("sitze.row.summe.name"));
    pseudogruppeSumme.setKurzname(getMessage("sitze.row.summe.kurzname"));
    pseudogruppeSumme.setFarbe("");

    return pseudogruppeSumme;
  }
}
