
package de.ivu.elect.business.stimmeingabe.entity;

import static java.util.Collections.emptyList;
import static java.util.stream.Collectors.joining;
import static org.apache.commons.lang3.StringUtils.defaultIfBlank;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;

import javax.annotation.Nullable;

import org.apache.commons.lang3.StringUtils;

import de.ivu.elect.business.stimmeingabe.control.plausi.StimmeingabePlausi;
import de.ivu.elect.business.wahlvorschlag.entity.WahlvorschlagStimmzettelPosition;
import de.ivu.elect.jee.entity.tk.EntityTK;
import de.ivu.elect.jee.presentation.dynamicform.AbstractStringProperty;

/**
 * Eine Zeile in der GUI zur Eingabe der Stimmen einer Gruppe.
 */
public class StimmeingabeGruppeStimmen extends AbstractStimmeingabeStimmen {

  private static final long serialVersionUID = 1L;

  private final WahlvorschlagStimmzettelPosition wahlvorschlagStimmzettelPosition;

  private final String kennung;

  private final String bezeichnung;

  private final List<Stimmeingabefehler> stimmeingabefehlerLetzterErgebniseingang;

  private final Stimmeingabe stimmeingabeDirektstimme;

  private final String stimmeingabeDirektstimmeGruppeBezeichnung;

  private final String stimmeingabeDirektstimmeKennung;

  private final String stimmeingabeDirektstimmeBezeichnung;

  private final String stimmeingabeDirektstimmeTooltip;

  private final List<Stimmeingabefehler> stimmeingabefehlerDirektstimmeLetzterErgebniseingang;

  private final Stimmeingabe stimmeingabeDirektstimmeVergleich;

  private final Set<String> stimmeingabeDirektstimmeStyleClasses;

  private final Stimmeingabe stimmeingabeListenstimme;

  private final String stimmeingabeListenstimmeGruppeBezeichnung;

  private final String stimmeingabeListenstimmeKennung;

  private final String stimmeingabeListenstimmeBezeichnung;

  private final String stimmeingabeListenstimmeTooltip;

  private final List<Stimmeingabefehler> stimmeingabefehlerListenstimmeLetzterErgebniseingang;

  private final Stimmeingabe stimmeingabeListenstimmeVergleich;

  private final Set<String> stimmeingabeListenstimmeStyleClasses;

  protected StimmeingabeGruppeStimmen(StimmeingabeGruppeStimmen stimmeingabeGruppeStimmen) {
    this( //
        stimmeingabeGruppeStimmen.wahlvorschlagStimmzettelPosition, stimmeingabeGruppeStimmen.kennung, stimmeingabeGruppeStimmen.bezeichnung, //

        stimmeingabeGruppeStimmen.stimmeingabeDirektstimme, stimmeingabeGruppeStimmen.stimmeingabeDirektstimmeGruppeBezeichnung, stimmeingabeGruppeStimmen.stimmeingabeDirektstimmeKennung,
        stimmeingabeGruppeStimmen.stimmeingabeDirektstimmeBezeichnung, stimmeingabeGruppeStimmen.stimmeingabeDirektstimmeTooltip,
        stimmeingabeGruppeStimmen.stimmeingabefehlerDirektstimmeLetzterErgebniseingang, stimmeingabeGruppeStimmen.stimmeingabeDirektstimmeVergleich, //

        stimmeingabeGruppeStimmen.stimmeingabeListenstimme, stimmeingabeGruppeStimmen.stimmeingabeListenstimmeGruppeBezeichnung, stimmeingabeGruppeStimmen.stimmeingabeListenstimmeKennung,
        stimmeingabeGruppeStimmen.stimmeingabeListenstimmeBezeichnung, stimmeingabeGruppeStimmen.stimmeingabeListenstimmeTooltip,
        stimmeingabeGruppeStimmen.stimmeingabefehlerListenstimmeLetzterErgebniseingang, stimmeingabeGruppeStimmen.stimmeingabeListenstimmeVergleich //
    );
  }

  public StimmeingabeGruppeStimmen( //
      WahlvorschlagStimmzettelPosition wahlvorschlagStimmzettelPosition, String kennung, String bezeichnung, //

      @Nullable Stimmeingabe stimmeingabeDirektstimme, String stimmeingabeDirektstimmeGruppeBezeichnung, String stimmeingabeDirektstimmeKennung, String stimmeingabeDirektstimmeBezeichnung,
      String stimmeingabeDirektstimmeTooltip, List<Stimmeingabefehler> stimmeingabefehlerDirektstimmeLetzterErgebniseingang, @Nullable Stimmeingabe stimmeingabeDirektstimmeVergleich, //

      @Nullable Stimmeingabe stimmeingabeListenstimme, String stimmeingabeListenstimmeGruppeBezeichnung, String stimmeingabeListenstimmeKennung, String stimmeingabeListenstimmeBezeichnung,
      String stimmeingabeListenstimmeTooltip, List<Stimmeingabefehler> stimmeingabefehlerListenstimmeLetzterErgebniseingang, @Nullable Stimmeingabe stimmeingabeListenstimmeVergleich //
  ) {
    super();

    this.wahlvorschlagStimmzettelPosition = wahlvorschlagStimmzettelPosition;

    this.kennung = kennung;
    this.bezeichnung = bezeichnung;
    this.stimmeingabefehlerLetzterErgebniseingang = new ArrayList<>();
    this.stimmeingabefehlerLetzterErgebniseingang.addAll(stimmeingabefehlerDirektstimmeLetzterErgebniseingang);
    this.stimmeingabefehlerLetzterErgebniseingang.addAll(stimmeingabefehlerListenstimmeLetzterErgebniseingang);

    this.stimmeingabeDirektstimme = stimmeingabeDirektstimme;
    this.stimmeingabeDirektstimmeGruppeBezeichnung = stimmeingabeDirektstimmeGruppeBezeichnung;
    this.stimmeingabeDirektstimmeKennung = stimmeingabeDirektstimmeKennung;
    this.stimmeingabeDirektstimmeBezeichnung = stimmeingabeDirektstimmeBezeichnung;
    this.stimmeingabeDirektstimmeTooltip = stimmeingabeDirektstimmeTooltip;
    this.stimmeingabefehlerDirektstimmeLetzterErgebniseingang = stimmeingabefehlerDirektstimmeLetzterErgebniseingang;
    this.stimmeingabeDirektstimmeVergleich = stimmeingabeDirektstimmeVergleich;
    this.stimmeingabeDirektstimmeStyleClasses = new HashSet<>();

    this.stimmeingabeListenstimme = stimmeingabeListenstimme;
    this.stimmeingabeListenstimmeGruppeBezeichnung = stimmeingabeListenstimmeGruppeBezeichnung;
    this.stimmeingabeListenstimmeKennung = stimmeingabeListenstimmeKennung;
    this.stimmeingabeListenstimmeBezeichnung = stimmeingabeListenstimmeBezeichnung;
    this.stimmeingabeListenstimmeTooltip = stimmeingabeListenstimmeTooltip;
    this.stimmeingabefehlerListenstimmeLetzterErgebniseingang = stimmeingabefehlerListenstimmeLetzterErgebniseingang;
    this.stimmeingabeListenstimmeVergleich = stimmeingabeListenstimmeVergleich;
    this.stimmeingabeListenstimmeStyleClasses = new HashSet<>();
  }

  @Override
  public boolean hasTk(EntityTK tk) {
    return hasStimmeingabeDirektstimmeTk(tk) || hasStimmeingabeListenstimmeTk(tk);
  }

  public boolean isWaehlerBenannteWahlvorschlaegeErfassbar() {
    return wahlvorschlagStimmzettelPosition != null && wahlvorschlagStimmzettelPosition.isWaehlerBenannteWahlvorschlaegeErfassbar();
  }

  public boolean hasStimmzettelPosition() {
    return wahlvorschlagStimmzettelPosition != null && wahlvorschlagStimmzettelPosition.hasPosition();
  }

  public long getStimmzettelPosition() {
    if (hasStimmzettelPosition()) {
      return wahlvorschlagStimmzettelPosition.getPosition();
    }

    throw new IllegalStateException(
        "Unerwartet keine Stimmzettelposition vorhanden zu [kennung=" + getKennung() + ", bezeichnung=" + getBezeichnung() + ", gruppeImGebiet=" + getGruppeImGebiet() + "]");
  }

  @Override
  public String getKennung() {
    return kennung;
  }

  @Override
  public String getBezeichnung() {
    return bezeichnung;
  }

  @Override
  public Collection<Stimmeingabe> getStimmeingaben() {
    Collection<Stimmeingabe> result = new ArrayList<>();

    if (hasStimmeingabeDirektstimme()) {
      result.add(getStimmeingabeDirektstimme());
    }

    if (hasStimmeingabeListenstimme()) {
      result.add(getStimmeingabeListenstimme());
    }

    return result;
  }

  @Override
  public List<Stimmeingabefehler> getStimmeingabefehlerLetzterErgebniseingang() {
    return stimmeingabefehlerLetzterErgebniseingang;
  }

  public void addStimmeingabeStyleClass(Stimmeingabe stimmeingabe, String styleClass) {
    if (stimmeingabe != null) {
      if (Objects.equals(stimmeingabe, getStimmeingabeDirektstimme())) {
        addStimmeingabeDirektstimmeStyleClass(styleClass);
      } else if (Objects.equals(stimmeingabe, getStimmeingabeListenstimme())) {
        addStimmeingabeListenstimmeStyleClass(styleClass);
      }
    }
  }

  @Override
  public String getBaseClassName() {
    return getClass().getName();
  }

  public boolean isAnzahlEingabeDirektstimmeRendered() {
    return hasStimmeingabeDirektstimme();
  }

  public boolean hasAnzahlEingabeDirektstimme() {
    return hasStimmeingabeDirektstimme() && getStimmeingabeDirektstimme().hasAnzahlEingabe();
  }

  public String getAnzahlEingabeDirektstimmeOrDefault(String defaultValue) {
    if (hasStimmeingabeDirektstimme()) {
      return defaultIfBlank(getAnzahlEingabeDirektstimme(), defaultValue);
    }

    return "";
  }

  public String getAnzahlEingabeDirektstimme() {
    return getStimmeingabeDirektstimmeAnzahlEingabeProperty().getValue();
  }

  public void setAnzahlEingabeDirektstimme(String value) {
    getStimmeingabeDirektstimmeAnzahlEingabeProperty().setValue(value);
  }

  public String getAnzahlEingabeDirektstimmeTooltip() {
    return getStimmeingabeDirektstimmeTooltip();
  }

  private AbstractStringProperty getStimmeingabeDirektstimmeAnzahlEingabeProperty() {
    return getStimmeingabeDirektstimme().getProperties().getAnzahlEingabeProperty();
  }

  public boolean hasAnzahlDirektstimme() {
    return hasStimmeingabeDirektstimme() && getStimmeingabeDirektstimme().hasAnzahlGroesserGleichNull();
  }

  public Long getAnzahlDirektstimme() {
    return getStimmeingabeDirektstimme().getAnzahl();
  }

  public void setAnzahlDirektstimme(Long value) {
    getStimmeingabeDirektstimme().setAnzahl(value);
  }

  private boolean hasStimmeingabeDirektstimmeTk(EntityTK tk) {
    return hasStimmeingabeDirektstimme() && getStimmeingabeDirektstimme().getTk().equals(tk);
  }

  public boolean hasStimmeingabeDirektstimme() {
    return getStimmeingabeDirektstimme() != null;
  }

  public Stimmeingabe getStimmeingabeDirektstimme() {
    return stimmeingabeDirektstimme;
  }

  public String getStimmeingabeDirektstimmeGruppeBezeichnung() {
    return stimmeingabeDirektstimmeGruppeBezeichnung;
  }

  public String getStimmeingabeDirektstimmeKennung() {
    return stimmeingabeDirektstimmeKennung;
  }

  public String getStimmeingabeDirektstimmeBezeichnung() {
    return stimmeingabeDirektstimmeBezeichnung;
  }

  public String getStimmeingabeDirektstimmeTooltip() {
    return stimmeingabeDirektstimmeTooltip;
  }

  public String getStimmeingabeDirektstimmeStyleClass() {
    return getStimmeingabeStyleClass(getStimmeingabefehlerDirektstimmeLetzterErgebniseingang()) + " "
        + stimmeingabeDirektstimmeStyleClasses.stream().filter(StringUtils::isNotBlank).collect(joining(" ")) + " ";
  }

  public void addStimmeingabeDirektstimmeStyleClass(String styleClass) {
    stimmeingabeDirektstimmeStyleClasses.add(styleClass);
  }

  public boolean hasKannStimmeingabefehlerDirektstimme() {
    return containsKannStimmeingabefehler(getStimmeingabefehlerDirektstimmeLetzterErgebniseingang());
  }

  public boolean hasMussStimmeingabefehlerDirektstimme() {
    return containsMussStimmeingabefehler(getStimmeingabefehlerDirektstimmeLetzterErgebniseingang());
  }

  public List<Stimmeingabefehler> getStimmeingabefehlerDirektstimmeLetzterErgebniseingang() {
    return stimmeingabefehlerDirektstimmeLetzterErgebniseingang;
  }

  public void addDirektstimmePlausi(StimmeingabePlausi stimmeingabePlausi) {
    getStimmeingabeDirektstimme().addStimmeingabePlausi(stimmeingabePlausi);
  }

  public boolean hasAnzahlEingabeDirektstimmeVergleich() {
    return hasStimmeingabeDirektstimmeVergleich() && getStimmeingabeDirektstimmeVergleich().hasAnzahlEingabe();
  }

  public String getAnzahlEingabeDirektstimmeVergleichOrDefault(String defaultValue) {
    if (hasStimmeingabeDirektstimmeVergleich()) {
      return defaultIfBlank(getAnzahlEingabeDirektstimmeVergleich(), defaultValue);
    }

    return "";
  }

  public String getAnzahlEingabeDirektstimmeVergleich() {
    return getStimmeingabeDirektstimmeVergleich().getAnzahlEingabe();
  }

  public boolean hasAnzahlDirektstimmeVergleich() {
    return hasStimmeingabeDirektstimmeVergleich() && getStimmeingabeDirektstimmeVergleich().hasAnzahlGroesserGleichNull();
  }

  public Long getAnzahlDirektstimmeVergleich() {
    return getStimmeingabeDirektstimmeVergleich().getAnzahl();
  }

  public boolean hasStimmeingabeDirektstimmeVergleich() {
    return getStimmeingabeDirektstimmeVergleich() != null;
  }

  public Stimmeingabe getStimmeingabeDirektstimmeVergleich() {
    return stimmeingabeDirektstimmeVergleich;
  }

  public boolean isAnzahlEingabeListenstimmeRendered() {
    return hasStimmeingabeListenstimme();
  }

  public boolean hasAnzahlEingabeListenstimme() {
    return hasStimmeingabeListenstimme() && getStimmeingabeListenstimme().hasAnzahlEingabe();
  }

  public String getAnzahlEingabeListenstimmeOrDefault(String defaultValue) {
    if (hasStimmeingabeListenstimme()) {
      return defaultIfBlank(getAnzahlEingabeListenstimme(), defaultValue);
    }

    return "";
  }

  public String getAnzahlEingabeListenstimme() {
    return getStimmeingabeListenstimmeAnzahlEingabeProperty().getValue();
  }

  public void setAnzahlEingabeListenstimme(String value) {
    getStimmeingabeListenstimmeAnzahlEingabeProperty().setValue(value);
  }

  public String getAnzahlEingabeListenstimmeTooltip() {
    return getStimmeingabeListenstimmeTooltip();
  }

  private AbstractStringProperty getStimmeingabeListenstimmeAnzahlEingabeProperty() {
    return getStimmeingabeListenstimme().getProperties().getAnzahlEingabeProperty();
  }

  public boolean hasAnzahlListenstimme() {
    return hasStimmeingabeListenstimme() && getStimmeingabeListenstimme().hasAnzahlGroesserGleichNull();
  }

  public Long getAnzahlListenstimme() {
    return getStimmeingabeListenstimme().getAnzahl();
  }

  public void setAnzahlListenstimme(Long value) {
    getStimmeingabeListenstimme().setAnzahl(value);
  }

  private boolean hasStimmeingabeListenstimmeTk(EntityTK tk) {
    return hasStimmeingabeListenstimme() && getStimmeingabeListenstimme().getTk().equals(tk);
  }

  public boolean hasStimmeingabeListenstimme() {
    return getStimmeingabeListenstimme() != null;
  }

  public Stimmeingabe getStimmeingabeListenstimme() {
    return stimmeingabeListenstimme;
  }

  public String getStimmeingabeListenstimmeGruppeBezeichnung() {
    return stimmeingabeListenstimmeGruppeBezeichnung;
  }

  public String getStimmeingabeListenstimmeKennung() {
    return stimmeingabeListenstimmeKennung;
  }

  public String getStimmeingabeListenstimmeBezeichnung() {
    return stimmeingabeListenstimmeBezeichnung;
  }

  public String getStimmeingabeListenstimmeTooltip() {
    return stimmeingabeListenstimmeTooltip;
  }

  public String getStimmeingabeListenstimmeStyleClass() {
    return getStimmeingabeStyleClass(getStimmeingabefehlerListenstimmeLetzterErgebniseingang()) + " "
        + stimmeingabeListenstimmeStyleClasses.stream().filter(StringUtils::isNotBlank).collect(joining(" ")) + " ";
  }

  public void addStimmeingabeListenstimmeStyleClass(String styleClass) {
    stimmeingabeListenstimmeStyleClasses.add(styleClass);
  }

  public boolean hasKannStimmeingabefehlerListenstimme() {
    return containsKannStimmeingabefehler(getStimmeingabefehlerListenstimmeLetzterErgebniseingang());
  }

  public boolean hasMussStimmeingabefehlerListenstimme() {
    return containsMussStimmeingabefehler(getStimmeingabefehlerListenstimmeLetzterErgebniseingang());
  }

  public List<Stimmeingabefehler> getStimmeingabefehlerListenstimmeLetzterErgebniseingang() {
    return stimmeingabefehlerListenstimmeLetzterErgebniseingang;
  }

  public void addListenstimmePlausi(StimmeingabePlausi stimmeingabePlausi) {
    getStimmeingabeListenstimme().addStimmeingabePlausi(stimmeingabePlausi);
  }

  public boolean hasAnzahlEingabeListenstimmeVergleich() {
    return hasStimmeingabeListenstimmeVergleich() && getStimmeingabeListenstimmeVergleich().hasAnzahlEingabe();
  }

  public String getAnzahlEingabeListenstimmeVergleichOrDefault(String defaultValue) {
    if (hasStimmeingabeListenstimmeVergleich()) {
      return defaultIfBlank(getAnzahlEingabeListenstimmeVergleich(), defaultValue);
    }

    return "";
  }

  public String getAnzahlEingabeListenstimmeVergleich() {
    return getStimmeingabeListenstimmeVergleich().getAnzahlEingabe();
  }

  public boolean hasAnzahlListenstimmeVergleich() {
    return hasStimmeingabeListenstimmeVergleich() && getStimmeingabeListenstimmeVergleich().hasAnzahlGroesserGleichNull();
  }

  public Long getAnzahlListenstimmeVergleich() {
    return getStimmeingabeListenstimmeVergleich().getAnzahl();
  }

  public boolean hasStimmeingabeListenstimmeVergleich() {
    return getStimmeingabeListenstimmeVergleich() != null;
  }

  public Stimmeingabe getStimmeingabeListenstimmeVergleich() {
    return stimmeingabeListenstimmeVergleich;
  }

  public static final StimmeingabeGruppeStimmen empty() {
    return new StimmeingabeGruppeStimmen(WahlvorschlagStimmzettelPosition.EMPTY, "", "", //
        null, "", "", "", "", emptyList(), null, //
        null, "", "", "", "", emptyList(), null //
    );
  }
}
