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

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

import javax.inject.Inject;

import de.ivu.elect.business.ergebnis.control.StimmenPoolExtendedDAOFactory;
import de.ivu.elect.business.gebietsbaum.boundary.GebietSessionSelect;
import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
import de.ivu.elect.business.gruppeimgebiet.control.GruppeImGebietReihenfolgeVorsortierer;
import de.ivu.elect.business.kandidatur.control.StimmzettelFormatter;
import de.ivu.elect.business.kandidatur.entity.Kandidatur;
import de.ivu.elect.business.kandidatur.entity.WaehlerBenannteKandidaturen;
import de.ivu.elect.business.stimmzettel.boundary.EntityAufStimmzettelDatenStimmzettelPositionBerechnung;
import de.ivu.elect.business.stimmzettel.boundary.EntityAufStimmzettelDatenVorgeladenFactory;
import de.ivu.elect.business.stimmzettel.entity.EntityAufStimmzettelDaten;
import de.ivu.elect.business.wahl.boundary.WahlSessionSelect;
import de.ivu.elect.business.wahl.entity.Wahl;
import de.ivu.elect.business.wahl.entity.WahlParameterExtendedLoader;
import de.ivu.elect.business.wahlvorschlag.entity.Wahlvorschlag;
import de.ivu.elect.jee.presentation.dynamictable.AbstractColumnBuilder;
import de.ivu.elect.jee.presentation.dynamictable.ColumnEntry;
import de.ivu.elect.presentation.gebietsbaum.GebietNummerFormatter;
import de.ivu.elect.presentation.person.PersonendatenArtFormatter;
import de.ivu.elect.presentation.person.PersonendatenFormatter;
import de.ivu.elect.presentation.pruefung.ZulassungPrueferRequestScope;
import de.ivu.elect.presentation.security.AbstractGebietSecurityFilter;
import de.ivu.elect.presentation.security.BearbeitenGebietSecurity;
import de.ivu.elect.presentation.security.EinsehenGebietSecurity;
import de.ivu.elect.presentation.security.GebietSecurity;
import de.ivu.elect.presentation.wahl.CurrentWahlZulassungspruefungExtended;
import de.ivu.elect.presentation.wahlvorschlag.WahlvorschlagRequestCache;

public abstract class AbstractKandidaturColumnBuilder extends AbstractColumnBuilder<Kandidatur> {

  private static final long serialVersionUID = 1L;

  public static final String WAEHLER_BENANNTE_KANDIDATUR_SONSTIGE_STYLE_CLASS = " ui-messages-warn";

  @Inject
  private transient WahlSessionSelect wahlSessionSelect;

  @Inject
  private transient GebietSessionSelect gebietSessionSelect;

  @Inject
  private transient GebietSecurity gebietSecurity;

  @Inject
  private transient GebietNummerFormatter gebietNummerFormatter;

  @Inject
  private transient PersonendatenFormatter personendatenFormatter;

  @Inject
  private transient PersonendatenArtFormatter personendatenArtFormatter;

  @Inject
  private transient CurrentWahlZulassungspruefungExtended currentWahlZulassungspruefung;

  @Inject
  private transient WahlvorschlagRequestCache wahlvorschlagRequestCache;

  @Inject
  private transient ZulassungPrueferRequestScope zulassungPrueferRequestScope;

  @Inject
  private transient WahlParameterExtendedLoader wahlParameterExtendedLoader;

  @Inject
  private transient StimmzettelFormatter stimmzettelFormatter;

  @Inject
  private transient EntityAufStimmzettelDatenStimmzettelPositionBerechnung entityAufStimmzettelDatenStimmzettelPositionBerechnung;

  @Inject
  private transient StimmenPoolExtendedDAOFactory stimmenPoolExtendedDAOFactory;

  @Inject
  private transient GruppeImGebietReihenfolgeVorsortierer gruppeImGebietReihenfolgeVorsortierer;

  private EntityAufStimmzettelDaten entityAufStimmzettelDaten;

  protected AbstractKandidaturColumnBuilder() {
    super();
  }

  @Override
  protected List<ColumnEntry< ? super Kandidatur>> getColumnEntriesSorted() {
    List<ColumnEntry< ? super Kandidatur>> result = new ArrayList<>();

    addColumnEntriesGruppeGebiet(result);

    addColumnEntriesStimmzetteldaten(result);

    addColumnEntriesKandidaturdaten(result);

    addColumnEntriesPersonendaten(result);

    addColumnEntriesBeschaeftigungsdaten(result);

    addColumnEntriesAnschrift(result);

    addColumnEntriesGewaehlt(result);

    addColumnEntriesVerknuepfungen(result);

    addColumnEntriesZulassungspruefung(result);

    return result;
  }

  protected abstract void addColumnEntriesGruppeGebiet(List<ColumnEntry< ? super Kandidatur>> result);

  protected abstract void addColumnEntriesStimmzetteldaten(List<ColumnEntry< ? super Kandidatur>> result);

  protected abstract void addColumnEntriesKandidaturdaten(List<ColumnEntry< ? super Kandidatur>> result);

  protected abstract void addColumnEntriesPersonendaten(List<ColumnEntry< ? super Kandidatur>> result);

  protected abstract void addColumnEntriesBeschaeftigungsdaten(List<ColumnEntry< ? super Kandidatur>> result);

  protected abstract void addColumnEntriesAnschrift(List<ColumnEntry< ? super Kandidatur>> result);

  protected abstract void addColumnEntriesGewaehlt(List<ColumnEntry< ? super Kandidatur>> result);

  protected abstract void addColumnEntriesVerknuepfungen(List<ColumnEntry< ? super Kandidatur>> result);

  protected abstract void addColumnEntriesZulassungspruefung(List<ColumnEntry< ? super Kandidatur>> result);

  @SafeVarargs
  protected static void replace(List<ColumnEntry< ? super Kandidatur>> result, ColumnEntry< ? super Kandidatur> column, ColumnEntry< ? super Kandidatur> one,
      ColumnEntry< ? super Kandidatur>... more) {
    addAfter(result, column, one, more);

    if (column != null) {
      result.remove(column);
    }
  }

  @SafeVarargs
  protected static void addAfter(List<ColumnEntry< ? super Kandidatur>> result, ColumnEntry< ? super Kandidatur> column, ColumnEntry< ? super Kandidatur> one,
      ColumnEntry< ? super Kandidatur>... more) {
    if (column != null) {
      addAtIndex(result, result.indexOf(column) + 1, one, more);
    }
  }

  @SafeVarargs
  protected static void remove(List<ColumnEntry< ? super Kandidatur>> result, ColumnEntry< ? super Kandidatur> one, ColumnEntry< ? super Kandidatur>... more) {
    for (ColumnEntry< ? super Kandidatur> entry : asNonNullList(one, more)) {
      result.remove(entry);
    }
  }

  @SafeVarargs
  protected static void add(List<ColumnEntry< ? super Kandidatur>> result, ColumnEntry< ? super Kandidatur> one, ColumnEntry< ? super Kandidatur>... more) {
    addAtIndex(result, result.size(), one, more);
  }

  @SafeVarargs
  private static void addAtIndex(List<ColumnEntry< ? super Kandidatur>> result, int indexZeroBased, ColumnEntry< ? super Kandidatur> one, ColumnEntry< ? super Kandidatur>... more) {
    List<ColumnEntry< ? super Kandidatur>> entriesSortedReverse = asNonNullList(one, more);
    Collections.reverse(entriesSortedReverse);

    remove(result, one, more);

    int indexOrDefaultZeroBased = Math.max(0, Math.min(indexZeroBased, result.size()));

    for (ColumnEntry< ? super Kandidatur> entry : entriesSortedReverse) {
      result.add(indexOrDefaultZeroBased, entry);
    }
  }

  @SafeVarargs
  private static List<ColumnEntry< ? super Kandidatur>> asNonNullList(ColumnEntry< ? super Kandidatur> one, ColumnEntry< ? super Kandidatur>... more) {
    List<ColumnEntry< ? super Kandidatur>> result = new ArrayList<>();

    if (one != null) {
      result.add(one);
    }

    for (ColumnEntry< ? super Kandidatur> another : more) {
      if (another != null) {
        result.add(another);
      }
    }

    return result;
  }

  protected boolean isWahlSelectedMitErsatzkandidaturen() {
    return isWahlSelected() && getSelectedWahl().isWahlMitErsatzkandidaturen();
  }

  protected boolean isWahlSelectedMitMehrfachnennungen() {
    if (isWahlSelected()) {
      Wahl wahl = getSelectedWahl();

      return wahl != null && wahl.isWahlMitListenkandidatenStimmen() && wahl.isWahlMitMehrfachnennungen();
    }

    return false;
  }

  protected boolean isWahlSelected() {
    return wahlSessionSelect.isSelected();
  }

  protected Wahl getSelectedWahl() {
    return wahlSessionSelect.getEntity();
  }

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

  protected Gebiet getSelectedGebiet() {
    return gebietSessionSelect.getEntity();
  }

  protected AbstractGebietSecurityFilter<Kandidatur> getGebietSecurityFilter() {
    return AbstractGebietSecurityFilter.createEntityMitGebietSecurityFilter(getEinsehenGebietSecurity());
  }

  protected BearbeitenGebietSecurity getBearbeitenGebietSecurity() {
    return gebietSecurity.getBearbeitenGebietSecurity();
  }

  protected EinsehenGebietSecurity getEinsehenGebietSecurity() {
    return gebietSecurity.getEinsehenGebietSecurity();
  }

  protected GebietNummerFormatter getGebietNummerFormatter() {
    return gebietNummerFormatter;
  }

  protected PersonendatenFormatter getPersonendatenFormatter() {
    return personendatenFormatter;
  }

  protected PersonendatenArtFormatter getPersonendatenArtFormatter() {
    return personendatenArtFormatter;
  }

  protected EntityAufStimmzettelDaten getEntityAufStimmzettelDaten() {
    if (entityAufStimmzettelDaten == null) {
      List<Wahlvorschlag> wahlvorschlaegeInWahl = wahlvorschlagRequestCache.getAlleWahlvorschlaege();

      entityAufStimmzettelDaten = new EntityAufStimmzettelDatenVorgeladenFactory(wahlParameterExtendedLoader, zulassungPrueferRequestScope.getZulassungPruefer(), stimmzettelFormatter,
          entityAufStimmzettelDatenStimmzettelPositionBerechnung, stimmenPoolExtendedDAOFactory, gruppeImGebietReihenfolgeVorsortierer, wahlvorschlaegeInWahl).getEntityAufStimmzettelDaten();
    }

    return entityAufStimmzettelDaten;
  }

  protected CurrentWahlZulassungspruefungExtended getCurrentWahlZulassungspruefung() {
    return currentWahlZulassungspruefung;
  }

  protected boolean isWaehlerBenannteKandidaturSonstige(Kandidatur kandidatur) {
    return WaehlerBenannteKandidaturen.isWaehlerBenannteDirektkandidaturSonstige(getMessages(), kandidatur)
        || WaehlerBenannteKandidaturen.isWaehlerBenannteListenkandidaturSonstige(getMessages(), kandidatur);
  }

  @Override
  public String getStyleClass(Kandidatur kandidatur) {
    String result = super.getStyleClass(kandidatur);

    if (isWaehlerBenannteKandidaturSonstige(kandidatur)) {
      result += WAEHLER_BENANNTE_KANDIDATUR_SONSTIGE_STYLE_CLASS; // ELECT-9513, cf. AbstractKandidaturKumulierteStimmenColumnBuilder!!!
    }

    return result;
  }
}
