
package de.ivu.elect.presentation.gruppe.table;

import static org.apache.commons.lang3.StringUtils.trimToEmpty;

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

import javax.annotation.Nonnull;
import javax.faces.application.FacesMessage;
import javax.faces.application.FacesMessage.Severity;
import javax.inject.Inject;

import de.ivu.elect.business.ergebnis.boundary.ErgebnisSessionSelect;
import de.ivu.elect.business.ergebnis.boundary.StimmenExtendedDAO;
import de.ivu.elect.business.ergebnis.entity.Ergebnis;
import de.ivu.elect.business.ergebnis.entity.Ergebnisart;
import de.ivu.elect.business.ergebnis.entity.Stimmenart;
import de.ivu.elect.business.gebietsbaum.boundary.WahldurchfuehrungsgebietSessionSelect;
import de.ivu.elect.business.gebietsbaum.entity.Wahldurchfuehrungsgebiet;
import de.ivu.elect.business.gruppe.entity.GruppeKumulierteStimmen;
import de.ivu.elect.business.stimmeingabe.boundary.StimmeingabeErfassenDescriptionResolver;
import de.ivu.elect.business.stimmeingabe.control.ListenwahlStimmeingabeKonfiguration;
import de.ivu.elect.business.stimmeingabe.control.StimmeingabeStimmenartLabelGenerator;
import de.ivu.elect.business.wahl.entity.Wahl;
import de.ivu.elect.business.wahl.entity.WahlParameterLoader;
import de.ivu.elect.jee.presentation.dynamictable.AbstractColumnGroupBuilder;
import de.ivu.elect.jee.presentation.dynamictable.ColumnGroup;

public abstract class AbstractGruppeStimmenColumnBuilder extends AbstractColumnGroupBuilder<GruppeKumulierteStimmen> {

  private static final long serialVersionUID = 1L;

  @Inject
  private transient WahldurchfuehrungsgebietSessionSelect wahldurchfuehrungsgebietSessionSelect;

  @Inject
  private transient ErgebnisSessionSelect ergebnisSessionSelect;

  @Inject
  private transient StimmenExtendedDAO stimmenExtendedDAO;

  @Inject
  private transient StimmeingabeErfassenDescriptionResolver stimmeingabeErfassenDescriptionResolver;

  @Inject
  private transient ListenwahlStimmeingabeKonfiguration listenwahlStimmeingabeKonfiguration;

  @Inject
  private transient WahlParameterLoader wahlParameterLoader;

  private AbstractGruppeKumulierteStimmenColumn anzahlDirektstimmenColumn;

  private AbstractGruppeKumulierteStimmenColumn anteilDirektstimmenColumn;

  private AbstractGruppeKumulierteStimmenColumn anzahlDirektstimmenErgebnisartFuerDifferenzColumn;

  private AbstractGruppeKumulierteStimmenColumn anteilDirektstimmenErgebnisartFuerDifferenzColumn;

  private AbstractGruppeKumulierteStimmenColumn anzahlDirektstimmenDifferenzColumn;

  private AbstractGruppeKumulierteStimmenColumn anteilDirektstimmenDifferenzColumn;

  private AbstractGruppeKumulierteStimmenColumn anzahlListenstimmenColumn;

  private AbstractGruppeKumulierteStimmenColumn anteilListenstimmenColumn;

  private AbstractGruppeKumulierteStimmenColumn anzahlListenstimmenErgebnisartFuerDifferenzColumn;

  private AbstractGruppeKumulierteStimmenColumn anteilListenstimmenErgebnisartFuerDifferenzColumn;

  private AbstractGruppeKumulierteStimmenColumn anzahlListenstimmenDifferenzColumn;

  private AbstractGruppeKumulierteStimmenColumn anteilListenstimmenDifferenzColumn;

  private AbstractGruppeKumulierteStimmenColumn anzahlGesamtstimmenColumn;

  private AbstractGruppeKumulierteStimmenColumn anteilGesamtstimmenColumn;

  private AbstractGruppeKumulierteStimmenColumn anzahlGesamtstimmenErgebnisartFuerDifferenzColumn;

  private AbstractGruppeKumulierteStimmenColumn anteilGesamtstimmenErgebnisartFuerDifferenzColumn;

  private AbstractGruppeKumulierteStimmenColumn anzahlGesamtstimmenDifferenzColumn;

  private AbstractGruppeKumulierteStimmenColumn anteilGesamtstimmenDifferenzColumn;

  private AbstractGruppeKumulierteStimmenColumn anzahlStimmzettelNurEinzelnerWahlvorschlagUnveraendertColumn;

  private AbstractGruppeKumulierteStimmenColumn anzahlStimmzettelNurEinzelnerWahlvorschlagVeraendertColumn;

  protected AbstractGruppeKumulierteStimmenColumn gruppenFarbeColumn;

  protected AbstractGruppeKumulierteStimmenColumn gruppenNameColumn;

  protected ColumnGroup<GruppeKumulierteStimmen> direktstimmenGroup;

  protected ColumnGroup<GruppeKumulierteStimmen> listenstimmenGroup;

  protected ColumnGroup<GruppeKumulierteStimmen> gesamtstimmenGroup;

  protected ColumnGroup<GruppeKumulierteStimmen> stimmzettelGroup;

  public AbstractGruppeStimmenColumnBuilder() {
    super();
  }

  @Override
  protected void initColumns() {
    gruppenFarbeColumn = new GruppeKumulierteStimmenGruppenFarbeColumn(getMessages());
    gruppenNameColumn = createGruppenNameColumn();

    Ergebnisart ergebnisartFuerDifferenz = getErgebnisartFuerDifferenz();

    if (isWahlMitDirektwahlgebietenSelected()) {
      anzahlDirektstimmenColumn = new GruppeKumulierteStimmenAnzahlDirektstimmenColumn(getMessages());
      anteilDirektstimmenColumn = new GruppeKumulierteStimmenAnteilDirektstimmenColumn(getMessages());

      anzahlDirektstimmenErgebnisartFuerDifferenzColumn = new GruppeKumulierteStimmenAnzahlDirektstimmenErgebnisartFuerDifferenzColumn(getMessages(), ergebnisartFuerDifferenz);
      anteilDirektstimmenErgebnisartFuerDifferenzColumn = new GruppeKumulierteStimmenAnteilDirektstimmenErgebnisartFuerDifferenzColumn(getMessages(), ergebnisartFuerDifferenz);

      anzahlDirektstimmenDifferenzColumn = new GruppeKumulierteStimmenAnzahlDirektstimmenDifferenzColumn(getMessages());
      anteilDirektstimmenDifferenzColumn = new GruppeKumulierteStimmenAnteilDirektstimmenDifferenzColumn(getMessages());
    }

    if (isWahlMitListenwahlgebietenSelected()) {
      anzahlListenstimmenColumn = new GruppeKumulierteStimmenAnzahlListenstimmenColumn(getMessages());
      anteilListenstimmenColumn = createAnteilListenstimmenColumn();

      anzahlListenstimmenErgebnisartFuerDifferenzColumn = new GruppeKumulierteStimmenAnzahlListenstimmenErgebnisartFuerDifferenzColumn(getMessages(), ergebnisartFuerDifferenz);
      anteilListenstimmenErgebnisartFuerDifferenzColumn = new GruppeKumulierteStimmenAnteilListenstimmenErgebnisartFuerDifferenzColumn(getMessages(), ergebnisartFuerDifferenz);

      anzahlListenstimmenDifferenzColumn = new GruppeKumulierteStimmenAnzahlListenstimmenDifferenzColumn(getMessages());
      anteilListenstimmenDifferenzColumn = new GruppeKumulierteStimmenAnteilListenstimmenDifferenzColumn(getMessages());
    }

    if (isWahlMitGesamtstimmenauswertungSelected()) {
      anzahlGesamtstimmenColumn = new GruppeKumulierteStimmenAnzahlGesamtstimmenColumn(getMessages());
      anteilGesamtstimmenColumn = new GruppeKumulierteStimmenAnteilGesamtstimmenColumn(getMessages());

      anzahlGesamtstimmenErgebnisartFuerDifferenzColumn = new GruppeKumulierteStimmenAnzahlGesamtstimmenErgebnisartFuerDifferenzColumn(getMessages(), ergebnisartFuerDifferenz);
      anteilGesamtstimmenErgebnisartFuerDifferenzColumn = new GruppeKumulierteStimmenAnteilGesamtstimmenErgebnisartFuerDifferenzColumn(getMessages(), ergebnisartFuerDifferenz);

      anzahlGesamtstimmenDifferenzColumn = new GruppeKumulierteStimmenAnzahlGesamtstimmenDifferenzColumn(getMessages());
      anteilGesamtstimmenDifferenzColumn = new GruppeKumulierteStimmenAnteilGesamtstimmenDifferenzColumn(getMessages());
    }

    if (isWahlMitStimmzettelauswertungSelected()) {
      anzahlStimmzettelNurEinzelnerWahlvorschlagUnveraendertColumn = new GruppeKumulierteStimmenAnzahlStimmzettelNurEinzelnerWahlvorschlagUnveraendertColumn(getMessages());
      anzahlStimmzettelNurEinzelnerWahlvorschlagVeraendertColumn = new GruppeKumulierteStimmenAnzahlStimmzettelNurEinzelnerWahlvorschlagVeraendertColumn(getMessages());
    }
  }

  @Nonnull
  protected AbstractGruppeKumulierteStimmenColumn createGruppenNameColumn() {
    return new GruppeKumulierteStimmenGruppenNameColumn(getMessages());
  }

  @Nonnull
  protected GruppeKumulierteStimmenAnteilListenstimmenColumn createAnteilListenstimmenColumn() {
    return new GruppeKumulierteStimmenAnteilListenstimmenColumn(getMessages());
  }

  @Override
  protected void initColumnGroups() {
    if (isGebietSelected() && isErgebnisSelected()) {
      Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet = getSelectedGebiet();

      boolean columnsDifferenzRendered = isColumnsDifferenzRendered();

      boolean wahlMitDirektwahlgebietenSelected = isWahlMitDirektwahlgebietenSelected();
      boolean wahlMitListenwahlgebietenSelected = isWahlMitListenwahlgebietenSelected();
      boolean wahlMitGesamtstimmenauswertungSelected = isWahlMitGesamtstimmenauswertungSelected();

      StimmeingabeStimmenartLabelGenerator stimmenartLabelGenerator = stimmeingabeErfassenDescriptionResolver.getStimmeingabeErfassenDescriptionByGebiet(wahldurchfuehrungsgebiet)
          .getStimmeingabeGruppeStimmenErfassenLabelGenerator();

      if (wahlMitDirektwahlgebietenSelected) {
        if (wahlMitListenwahlgebietenSelected || wahlMitGesamtstimmenauswertungSelected) {
          direktstimmenGroup = new ColumnGroup<>(stimmenartLabelGenerator.getStimmenartName(wahldurchfuehrungsgebiet, Stimmenart.DIREKTSTIMME));
        } else {
          direktstimmenGroup = new ColumnGroup<>(getMessage("gruppe.kumulierte.stimmen.stimmen"));
        }

        direktstimmenGroup.addColumn(anzahlDirektstimmenColumn);
        direktstimmenGroup.addColumn(anteilDirektstimmenColumn);

        if (columnsDifferenzRendered) {
          direktstimmenGroup.addColumn(anzahlDirektstimmenErgebnisartFuerDifferenzColumn);
          direktstimmenGroup.addColumn(anteilDirektstimmenErgebnisartFuerDifferenzColumn);

          direktstimmenGroup.addColumn(anzahlDirektstimmenDifferenzColumn);
          direktstimmenGroup.addColumn(anteilDirektstimmenDifferenzColumn);
        }
      }

      if (wahlMitListenwahlgebietenSelected) {
        if (wahlMitDirektwahlgebietenSelected || wahlMitGesamtstimmenauswertungSelected) {
          listenstimmenGroup = new ColumnGroup<>(stimmenartLabelGenerator.getStimmenartName(wahldurchfuehrungsgebiet, Stimmenart.LISTENSTIMME));
        } else {
          listenstimmenGroup = new ColumnGroup<>(getMessage("gruppe.kumulierte.stimmen.stimmen"));
        }

        listenstimmenGroup.addColumn(anzahlListenstimmenColumn);
        listenstimmenGroup.addColumn(anteilListenstimmenColumn);

        if (columnsDifferenzRendered) {
          listenstimmenGroup.addColumn(anzahlListenstimmenErgebnisartFuerDifferenzColumn);
          listenstimmenGroup.addColumn(anteilListenstimmenErgebnisartFuerDifferenzColumn);

          listenstimmenGroup.addColumn(anzahlListenstimmenDifferenzColumn);
          listenstimmenGroup.addColumn(anteilListenstimmenDifferenzColumn);
        }
      }

      if (wahlMitGesamtstimmenauswertungSelected) {
        if (wahlMitDirektwahlgebietenSelected || wahlMitListenwahlgebietenSelected) {
          gesamtstimmenGroup = new ColumnGroup<>(getMessage("gruppe.kumulierte.stimmen.gesamtstimmen"));
        } else {
          gesamtstimmenGroup = new ColumnGroup<>(getMessage("gruppe.kumulierte.stimmen.stimmen"));
        }

        gesamtstimmenGroup.addColumn(anzahlGesamtstimmenColumn);
        gesamtstimmenGroup.addColumn(anteilGesamtstimmenColumn);

        if (columnsDifferenzRendered) {
          gesamtstimmenGroup.addColumn(anzahlGesamtstimmenErgebnisartFuerDifferenzColumn);
          gesamtstimmenGroup.addColumn(anteilGesamtstimmenErgebnisartFuerDifferenzColumn);

          gesamtstimmenGroup.addColumn(anzahlGesamtstimmenDifferenzColumn);
          gesamtstimmenGroup.addColumn(anteilGesamtstimmenDifferenzColumn);
        }
      }

      if (isWahlMitStimmzettelauswertungSelected()) {
        stimmzettelGroup = new ColumnGroup<>(getMessage("gruppe.kumulierte.stimmen.stimmzettel"));

        if (isStimmzettelNurEinzelnerWahlvorschlagUnveraendertRelevant()) {
          stimmzettelGroup.addColumn(anzahlStimmzettelNurEinzelnerWahlvorschlagUnveraendertColumn);
        }

        if (isStimmzettelNurEinzelnerWahlvorschlagVeraendertRelevant()) {
          stimmzettelGroup.addColumn(anzahlStimmzettelNurEinzelnerWahlvorschlagVeraendertColumn);
        }
      }
    }
  }

  @Override
  protected List<ColumnGroup< ? super GruppeKumulierteStimmen>> getColumnGroupsSorted() {
    initColumnGroups();

    List<ColumnGroup< ? super GruppeKumulierteStimmen>> result = new ArrayList<>();

    if (isWahlMitDirektwahlgebietenSelected()) {
      result.add(direktstimmenGroup);
    }

    if (isWahlMitListenwahlgebietenSelected()) {
      result.add(listenstimmenGroup);
    }

    if (isWahlMitGesamtstimmenauswertungSelected()) {
      result.add(gesamtstimmenGroup);
    }

    if (isWahlMitStimmzettelauswertungSelected()) {
      result.add(stimmzettelGroup);
    }

    return result;
  }

  public List<AbstractGruppeKumulierteStimmenColumn> collectColumnsContainingDarzustellendeWerteGrafisch() {
    List<AbstractGruppeKumulierteStimmenColumn> result = new ArrayList<>();

    initIfNotInitialized();

    boolean columnsDifferenzRendered = isColumnsDifferenzRendered();

    if (isWahlMitDirektwahlgebietenSelected()) {
      result.add(anzahlDirektstimmenColumn);
      result.add(anteilDirektstimmenColumn);

      if (columnsDifferenzRendered) {
        result.add(anzahlDirektstimmenErgebnisartFuerDifferenzColumn);
        result.add(anteilDirektstimmenErgebnisartFuerDifferenzColumn);

        result.add(anzahlDirektstimmenDifferenzColumn);
        result.add(anteilDirektstimmenDifferenzColumn);
      }
    }

    if (isWahlMitListenwahlgebietenSelected()) {
      result.add(anzahlListenstimmenColumn);
      result.add(anteilListenstimmenColumn);

      if (columnsDifferenzRendered) {
        result.add(anzahlListenstimmenErgebnisartFuerDifferenzColumn);
        result.add(anteilListenstimmenErgebnisartFuerDifferenzColumn);

        result.add(anzahlListenstimmenDifferenzColumn);
        result.add(anteilListenstimmenDifferenzColumn);
      }
    }

    if (isWahlMitGesamtstimmenauswertungSelected()) {
      result.add(anzahlGesamtstimmenColumn);
      result.add(anteilGesamtstimmenColumn);

      if (columnsDifferenzRendered) {
        result.add(anzahlGesamtstimmenErgebnisartFuerDifferenzColumn);
        result.add(anteilGesamtstimmenErgebnisartFuerDifferenzColumn);

        result.add(anzahlGesamtstimmenDifferenzColumn);
        result.add(anteilGesamtstimmenDifferenzColumn);
      }
    }

    return result;
  }

  protected abstract Ergebnisart getErgebnisartFuerDifferenz();

  protected boolean isColumnsDifferenzRendered() {
    Wahl wahl = getSelectedWahl();

    if (wahl == null) {
      return false;
    }

    return stimmenExtendedDAO.findByCountStimmenByWahlAndErgebnisart(wahl, getErgebnisartFuerDifferenz()) > 0L;
  }

  protected boolean isWahlMitDirektwahlgebietenSelected() {
    Wahl wahl = getSelectedWahl();

    return wahl != null && wahl.isWahlMitDirektkandidatengebieten();
  }

  protected boolean isWahlMitListenwahlgebietenSelected() {
    Wahl wahl = getSelectedWahl();

    return wahl != null && wahl.isWahlMitListengebieten();
  }

  private boolean isWahlMitGesamtstimmenauswertungSelected() {
    Wahl wahl = getSelectedWahl();

    return wahl != null && wahl.isWahlMitGesamtstimmenauswertung();
  }

  protected boolean isWahlMitStimmzettelauswertungSelected() {
    return isStimmzettelNurEinzelnerWahlvorschlagUnveraendertRelevant() || isStimmzettelNurEinzelnerWahlvorschlagVeraendertRelevant();
  }

  protected boolean isStimmzettelNurEinzelnerWahlvorschlagUnveraendertRelevant() {
    Ergebnis ergebnis = getSelectedErgebnis();

    return ergebnis != null
        && listenwahlStimmeingabeKonfiguration.isStimmzetteleingabeGruppeStimmenNurEinzelnerWahlvorschlagUnveraendertRelevant(wahlParameterLoader, ergebnis.getWahl(), ergebnis.getErgebnisart());
  }

  protected boolean isStimmzettelNurEinzelnerWahlvorschlagVeraendertRelevant() {
    Ergebnis ergebnis = getSelectedErgebnis();

    return ergebnis != null
        && listenwahlStimmeingabeKonfiguration.isStimmzetteleingabeGruppeStimmenNurEinzelnerWahlvorschlagVeraendertRelevant(wahlParameterLoader, ergebnis.getWahl(), ergebnis.getErgebnisart());
  }

  public String getHeaderIgnoringErgebnisartFuerDifferenz(AbstractGruppeKumulierteStimmenColumn columnEntry) {
    initIfNotInitialized();

    String columnGroupHeader = getColumnGroups().stream() //
        .filter(columnGroup -> columnGroup.getColumns().contains(columnEntry)).findFirst().map(columnGroup -> columnGroup.getHeader() + " ").orElse("");

    return columnGroupHeader + columnEntry.getHeaderIgnoringErgebnisartFuerDifferenz();
  }

  @Override
  public String getStyleClass(GruppeKumulierteStimmen gruppeKumulierteStimmen) {
    String result = trimToEmpty(super.getStyleClass(gruppeKumulierteStimmen));

    if (gruppeKumulierteStimmen != null) {
      Map<Severity, Map<String, Object[]>> messagesPerSeverity = gruppeKumulierteStimmen.getMessagesPerSeverity();

      if (messagesPerSeverity.containsKey(FacesMessage.SEVERITY_ERROR)) {
        result += " ui-messages-error";
      } else if (messagesPerSeverity.containsKey(FacesMessage.SEVERITY_WARN)) {
        result += " ui-messages-warn";
      }
    }

    return result;
  }

  protected Wahl getSelectedWahl() {
    Ergebnis ergebnis = getSelectedErgebnis();

    if (ergebnis == null) {
      Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet = getSelectedGebiet();

      if (wahldurchfuehrungsgebiet == null) {
        return null;
      }

      return wahldurchfuehrungsgebiet.getWahl();
    }

    return ergebnis.getWahl();
  }

  protected boolean isGebietSelected() {
    return wahldurchfuehrungsgebietSessionSelect.isSelected();
  }

  protected Wahldurchfuehrungsgebiet getSelectedGebiet() {
    return wahldurchfuehrungsgebietSessionSelect.getEntity();
  }

  protected boolean isErgebnisSelected() {
    return getSelectedErgebnis() != null;
  }

  protected Ergebnis getSelectedErgebnis() {
    return ergebnisSessionSelect.getEntity();
  }
}
