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

import static java.util.Collections.emptySet;
import static org.apache.commons.lang3.StringUtils.defaultIfBlank;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;

import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

import javax.annotation.Nullable;

import de.ivu.elect.business.ergebnis.entity.AbstractKumulierteStimmen;
import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
import de.ivu.elect.business.gebietsbaum.entity.Wahldurchfuehrungsgebiet;
import de.ivu.elect.business.gruppeimgebiet.entity.GruppeImGebiet;
import de.ivu.elect.business.gruppeimgebiet.entity.GruppeImGebietPK;
import de.ivu.elect.business.gruppeimgebiet.entity.GruppeImGebietTK;
import de.ivu.elect.business.gruppeimgebiet.entity.GruppeImGebietUK;
import de.ivu.elect.business.kandidatur.entity.Direktkandidatur;
import de.ivu.elect.business.stimmeingabe.control.StimmeingabeGruppeStimmenErfassenLabelGenerator;
import de.ivu.elect.business.stimmzettel.entity.EntityAufStimmzettelDaten;
import de.ivu.elect.business.wahl.entity.WahlartErgebnisart;
import de.ivu.elect.business.wahlvorschlag.entity.Direktkandidat;
import de.ivu.elect.business.wahlvorschlag.entity.Wahlvorschlag;
import de.ivu.elect.jee.entity.Entity;
import de.ivu.elect.jee.entity.EntityContainer;
import de.ivu.elect.jee.entity.EntityFachlichAbhaengigNode;
import de.ivu.elect.jee.entity.EntityUtil;
import de.ivu.elect.jee.entity.tk.EntityTK;
import de.ivu.elect.jee.protokollierung.VorherNachherWert;

public class GruppeKumulierteStimmen extends AbstractKumulierteStimmen implements Entity<GruppeImGebietPK, GruppeImGebietUK> {

  private static final long serialVersionUID = 1L;

  private final SystemGruppeEigenschaften systemGruppeEigenschaften;

  @Nullable
  private final GruppeImGebiet gruppeImGebiet;

  @Nullable
  private final Gruppe gruppe;

  @Nullable
  private final Wahldurchfuehrungsgebiet gebiet;

  private final Long anzahlStimmzettelNurEinzelnerWahlvorschlagUnveraendert;

  private final Long anzahlStimmzettelNurEinzelnerWahlvorschlagVeraendert;

  private final Long anzahlStimmzettelNurEinzelnerWahlvorschlagPanaschiert;

  private Long anzahlDirektstimmenAllerGruppen = null;

  private Long anzahlListenstimmenAllerGruppen = null;

  private Long anzahlStimmzettelNurEinzelnerWahlvorschlagUnveraendertAllerGruppen = null;

  private Long anzahlStimmzettelNurEinzelnerWahlvorschlagVeraendertAllerGruppen = null;

  private Long anzahlStimmzettelNurEinzelnerWahlvorschlagPanaschiertAllerGruppen = null;

  private Long anzahlDirektstimmenVergleich = 0L;

  private Long anzahlListenstimmenVergleich = 0L;

  private Long anzahlStimmzettelNurEinzelnerWahlvorschlagUnveraendertVergleich = 0L;

  private Long anzahlStimmzettelNurEinzelnerWahlvorschlagVeraendertVergleich = 0L;

  private Long anzahlStimmzettelNurEinzelnerWahlvorschlagPanaschiertVergleich = 0L;

  private Long anzahlDirektstimmenAllerGruppenVergleich = 0L;

  private Long anzahlListenstimmenAllerGruppenVergleich = 0L;

  private Long anzahlStimmzettelNurEinzelnerWahlvorschlagUnveraendertAllerGruppenVergleich = 0L;

  private Long anzahlStimmzettelNurEinzelnerWahlvorschlagVeraendertAllerGruppenVergleich = 0L;

  private Long anzahlStimmzettelNurEinzelnerWahlvorschlagPanaschiertAllerGruppenVergleich = 0L;

  private boolean anzahlDirektstimmenVorperiodeVorhanden = false;

  private Long anzahlDirektstimmenVorperiode = 0L;

  private boolean anzahlListenstimmenVorperiodeVorhanden = false;

  private Long anzahlListenstimmenVorperiode = 0L;

  private Long anzahlStimmzettelNurEinzelnerWahlvorschlagUnveraendertVorperiode = 0L;

  private Long anzahlStimmzettelNurEinzelnerWahlvorschlagVeraendertVorperiode = 0L;

  private Long anzahlStimmzettelNurEinzelnerWahlvorschlagPanaschiertVorperiode = 0L;

  private Long anzahlDirektstimmenVorperiodeAllerGruppen = 0L;

  private boolean anzahlDirektstimmenVorperiodeAllerGruppenVorhanden = false;

  private Long anzahlListenstimmenVorperiodeAllerGruppen = 0L;

  private boolean anzahlListenstimmenVorperiodeAllerGruppenVorhanden = false;

  private Long anzahlStimmzettelNurEinzelnerWahlvorschlagUnveraendertVorperiodeAllerGruppen = 0L;

  private Long anzahlStimmzettelNurEinzelnerWahlvorschlagVeraendertVorperiodeAllerGruppen = 0L;

  private Long anzahlStimmzettelNurEinzelnerWahlvorschlagPanaschiertVorperiodeAllerGruppen = 0L;

  private boolean hatDirektkandidatenInGebietshierarchie = true;

  private boolean hatListeInGebietshierarchie = true;

  private Long position = null;

  private String name = null;

  private String nameDirektkandidat = null;

  private String kurzname = null;

  private String kennung = null;

  private boolean vorperiodenStimmenVorhanden = false;

  private String anteilDirektstimmenTooltip;

  private String anteilDirektstimmenVorperiodeTooltip;

  private String anteilListenstimmenTooltip;

  private String anteilListenstimmenVorperiodeTooltip;

  private String anteilGesamtstimmenTooltip;

  private String anteilGesamtstimmenVorperiodeTooltip;

  private final Map<Gruppe, Set<GruppeImGebiet>> gigNachGruppen;

  public GruppeKumulierteStimmen(GruppeKumulierteStimmen other) {
    this(other.entityAufStimmzettelDaten, other.systemGruppeEigenschaften, other.getGruppeImGebiet(), other.getAnzahlDirektstimmen(), other.getAnzahlListenstimmen(),
        other.getAnzahlStimmzettelNurEinzelnerWahlvorschlagUnveraendert(), other.getAnzahlStimmzettelNurEinzelnerWahlvorschlagVeraendert(),
        other.getAnzahlStimmzettelNurEinzelnerWahlvorschlagPanaschiert(), other.gigNachGruppen);
    anzahlDirektstimmenAllerGruppen = other.anzahlDirektstimmenAllerGruppen;
    anzahlListenstimmenAllerGruppen = other.anzahlListenstimmenAllerGruppen;
    anzahlStimmzettelNurEinzelnerWahlvorschlagUnveraendertAllerGruppen = other.anzahlStimmzettelNurEinzelnerWahlvorschlagUnveraendertAllerGruppen;
    anzahlStimmzettelNurEinzelnerWahlvorschlagVeraendertAllerGruppen = other.anzahlStimmzettelNurEinzelnerWahlvorschlagVeraendertAllerGruppen;
    anzahlStimmzettelNurEinzelnerWahlvorschlagPanaschiertAllerGruppen = other.anzahlStimmzettelNurEinzelnerWahlvorschlagPanaschiertAllerGruppen;
    anzahlDirektstimmenVergleich = other.anzahlDirektstimmenVergleich;
    anzahlListenstimmenVergleich = other.anzahlListenstimmenVergleich;
    anzahlStimmzettelNurEinzelnerWahlvorschlagUnveraendertVergleich = other.anzahlStimmzettelNurEinzelnerWahlvorschlagUnveraendertVergleich;
    anzahlStimmzettelNurEinzelnerWahlvorschlagVeraendertVergleich = other.anzahlStimmzettelNurEinzelnerWahlvorschlagVeraendertVergleich;
    anzahlStimmzettelNurEinzelnerWahlvorschlagPanaschiertVergleich = other.anzahlStimmzettelNurEinzelnerWahlvorschlagPanaschiertVergleich;
    anzahlDirektstimmenAllerGruppenVergleich = other.anzahlDirektstimmenAllerGruppenVergleich;
    anzahlListenstimmenAllerGruppenVergleich = other.anzahlListenstimmenAllerGruppenVergleich;
    anzahlStimmzettelNurEinzelnerWahlvorschlagUnveraendertAllerGruppenVergleich = other.anzahlStimmzettelNurEinzelnerWahlvorschlagUnveraendertAllerGruppenVergleich;
    anzahlStimmzettelNurEinzelnerWahlvorschlagVeraendertAllerGruppenVergleich = other.anzahlStimmzettelNurEinzelnerWahlvorschlagVeraendertAllerGruppenVergleich;
    anzahlStimmzettelNurEinzelnerWahlvorschlagPanaschiertAllerGruppenVergleich = other.anzahlStimmzettelNurEinzelnerWahlvorschlagPanaschiertAllerGruppenVergleich;
    anzahlDirektstimmenVorperiodeVorhanden = other.anzahlDirektstimmenVorperiodeVorhanden;
    anzahlDirektstimmenVorperiode = other.anzahlDirektstimmenVorperiode;
    anzahlListenstimmenVorperiodeVorhanden = other.anzahlListenstimmenVorperiodeVorhanden;
    anzahlListenstimmenVorperiode = other.anzahlListenstimmenVorperiode;
    anzahlStimmzettelNurEinzelnerWahlvorschlagUnveraendertVorperiode = other.anzahlStimmzettelNurEinzelnerWahlvorschlagUnveraendertVorperiode;
    anzahlStimmzettelNurEinzelnerWahlvorschlagVeraendertVorperiode = other.anzahlStimmzettelNurEinzelnerWahlvorschlagVeraendertVorperiode;
    anzahlStimmzettelNurEinzelnerWahlvorschlagPanaschiertVorperiode = other.anzahlStimmzettelNurEinzelnerWahlvorschlagPanaschiertVorperiode;
    anzahlDirektstimmenVorperiodeAllerGruppen = other.anzahlDirektstimmenVorperiodeAllerGruppen;
    anzahlDirektstimmenVorperiodeAllerGruppenVorhanden = other.anzahlDirektstimmenVorperiodeAllerGruppenVorhanden;
    anzahlListenstimmenVorperiodeAllerGruppen = other.anzahlListenstimmenVorperiodeAllerGruppen;
    anzahlListenstimmenVorperiodeAllerGruppenVorhanden = other.anzahlListenstimmenVorperiodeAllerGruppenVorhanden;
    anzahlStimmzettelNurEinzelnerWahlvorschlagUnveraendertVorperiodeAllerGruppen = other.anzahlStimmzettelNurEinzelnerWahlvorschlagUnveraendertVorperiodeAllerGruppen;
    anzahlStimmzettelNurEinzelnerWahlvorschlagVeraendertVorperiodeAllerGruppen = other.anzahlStimmzettelNurEinzelnerWahlvorschlagVeraendertVorperiodeAllerGruppen;
    anzahlStimmzettelNurEinzelnerWahlvorschlagPanaschiertVorperiodeAllerGruppen = other.anzahlStimmzettelNurEinzelnerWahlvorschlagPanaschiertVorperiodeAllerGruppen;
    hatDirektkandidatenInGebietshierarchie = other.hatDirektkandidatenInGebietshierarchie;
    hatListeInGebietshierarchie = other.hatListeInGebietshierarchie;
    position = other.position;
    name = other.name;
    nameDirektkandidat = other.nameDirektkandidat;
    kurzname = other.kurzname;
    kennung = other.kennung;
    vorperiodenStimmenVorhanden = other.vorperiodenStimmenVorhanden;
    anteilDirektstimmenTooltip = other.anteilDirektstimmenTooltip;
    anteilDirektstimmenVorperiodeTooltip = other.anteilDirektstimmenVorperiodeTooltip;
    anteilListenstimmenTooltip = other.anteilListenstimmenTooltip;
    anteilListenstimmenVorperiodeTooltip = other.anteilListenstimmenVorperiodeTooltip;
    anteilGesamtstimmenTooltip = other.anteilGesamtstimmenTooltip;
    anteilGesamtstimmenVorperiodeTooltip = other.anteilGesamtstimmenVorperiodeTooltip;
  }

  public GruppeKumulierteStimmen(EntityAufStimmzettelDaten entityAufStimmzettelDaten, SystemGruppeEigenschaften systemGruppeEigenschaften, @Nullable GruppeImGebiet gruppeImGebiet,
      @Nullable Long anzahlDirektstimmen, @Nullable Long anzahlListenstimmen, @Nullable Long anzahlStimmzettelNurEinzelnerWahlvorschlagUnveraendert,
      @Nullable Long anzahlStimmzettelNurEinzelnerWahlvorschlagVeraendert, @Nullable Long anzahlStimmzettelNurEinzelnerWahlvorschlagPanaschiert, Map<Gruppe, Set<GruppeImGebiet>> gigNachGruppen) {
    super(entityAufStimmzettelDaten, anzahlDirektstimmen, anzahlListenstimmen);

    this.systemGruppeEigenschaften = systemGruppeEigenschaften;

    this.gruppeImGebiet = gruppeImGebiet;

    if (gruppeImGebiet != null) {
      this.gruppe = gruppeImGebiet.getGruppe();
      if (!this.gruppe.isGruppenImGebietLoaded() && gigNachGruppen.containsKey(this.gruppe)) {
        this.gruppe.setGruppenImGebietLoaded(new HashSet<>(gigNachGruppen.getOrDefault(this.gruppe, Collections.emptySet())));
      }
      this.gebiet = EntityUtil.castTo(gruppeImGebiet.getGebiet(), Wahldurchfuehrungsgebiet.class);
      this.kurzname = defaultIfBlank(gruppeImGebiet.getKurzname(), gruppeImGebiet.getGruppe().getKurzname());
    } else {
      this.gruppe = null;
      this.gebiet = null;
      this.kurzname = "";
    }

    this.anzahlStimmzettelNurEinzelnerWahlvorschlagUnveraendert = anzahlStimmzettelNurEinzelnerWahlvorschlagUnveraendert;
    this.anzahlStimmzettelNurEinzelnerWahlvorschlagVeraendert = anzahlStimmzettelNurEinzelnerWahlvorschlagVeraendert;
    this.anzahlStimmzettelNurEinzelnerWahlvorschlagPanaschiert = anzahlStimmzettelNurEinzelnerWahlvorschlagPanaschiert;

    this.gigNachGruppen = gigNachGruppen;
  }

  public GruppeKumulierteStimmen(EntityAufStimmzettelDaten entityAufStimmzettelDaten, SystemGruppeEigenschaften systemGruppeEigenschaften, Gruppe gruppe, Gebiet gebiet,
      @Nullable Long anzahlDirektstimmen, @Nullable Long anzahlListenstimmen, @Nullable Long anzahlStimmzettelNurEinzelnerWahlvorschlagUnveraendert,
      @Nullable Long anzahlStimmzettelNurEinzelnerWahlvorschlagVeraendert, @Nullable Long anzahlStimmzettelNurEinzelnerWahlvorschlagPanaschiert, Map<Gruppe, Set<GruppeImGebiet>> gigNachGruppen) {
    super(entityAufStimmzettelDaten, anzahlDirektstimmen, anzahlListenstimmen);

    this.systemGruppeEigenschaften = systemGruppeEigenschaften;

    this.gruppeImGebiet = null;

    this.gruppe = gruppe;
    if (this.gruppe != null && !this.gruppe.isGruppenImGebietLoaded() && gigNachGruppen.containsKey(this.gruppe)) {
      this.gruppe.setGruppenImGebietLoaded(new HashSet<>(gigNachGruppen.getOrDefault(this.gruppe, Collections.emptySet())));
    }
    this.gebiet = gebiet == null ? null : EntityUtil.castTo(gebiet, Wahldurchfuehrungsgebiet.class);
    this.kurzname = "";

    this.anzahlStimmzettelNurEinzelnerWahlvorschlagUnveraendert = anzahlStimmzettelNurEinzelnerWahlvorschlagUnveraendert;
    this.anzahlStimmzettelNurEinzelnerWahlvorschlagVeraendert = anzahlStimmzettelNurEinzelnerWahlvorschlagVeraendert;
    this.anzahlStimmzettelNurEinzelnerWahlvorschlagPanaschiert = anzahlStimmzettelNurEinzelnerWahlvorschlagPanaschiert;

    this.gigNachGruppen = gigNachGruppen;
  }

  @Override
  public GruppeImGebietPK getPk() {
    if (gruppeImGebiet == null) {
      return new GruppeImGebietPK(gebiet.getId());
    }

    return gruppeImGebiet.getPk();
  }

  @Override
  public Long getId() {
    if (gruppeImGebiet == null) {
      return gebiet.getId();
    }

    return gruppeImGebiet.getId();
  }

  @Override
  public GruppeImGebietUK getUk() {
    if (gruppeImGebiet == null) {
      return new GruppeImGebietUK(gebiet.getUk(), gruppe.getUk());
    }

    return gruppeImGebiet.getUk();
  }

  @Override
  public Class< ? > getEntityClass() {
    throw new UnsupportedOperationException();
  }

  @Override
  public EntityTK getTk() {
    if (gruppeImGebiet == null) {
      return new GruppeImGebietTK(gebiet.getGebietTK(), gruppe.getGruppeTK());
    }

    return gruppeImGebiet.getTk();
  }

  @Override
  public String getEntityTechnicalName() {
    throw new UnsupportedOperationException();
  }

  @Override
  public boolean isAenderungenProtokollieren() {
    return false;
  }

  @Override
  public Map<String, VorherNachherWert> getProtokollierteAttributeMitWerten() {
    throw new UnsupportedOperationException();
  }

  @Override
  public void resetProtokollierteAttribute() {
    throw new UnsupportedOperationException();
  }

  @Override
  public List<EntityFachlichAbhaengigNode> getFachlichAbhaengigeEntitaeten() {
    throw new UnsupportedOperationException();
  }

  @Override
  public boolean isInstanceOf(Class< ? > clazz) {
    return false;
  }

  @Override
  public <E> EntityContainer<E> castTo(Class<E> clazz) {
    throw new UnsupportedOperationException();
  }

  @Nullable
  public GruppeImGebiet getGruppeImGebiet() {
    return gruppeImGebiet;
  }

  public Long getAnzahlStimmzettelNurEinzelnerWahlvorschlagUnveraendert() {
    return anzahlStimmzettelNurEinzelnerWahlvorschlagUnveraendert;
  }

  public Long getAnzahlStimmzettelNurEinzelnerWahlvorschlagVeraendert() {
    return anzahlStimmzettelNurEinzelnerWahlvorschlagVeraendert;
  }

  public Long getAnzahlStimmzettelNurEinzelnerWahlvorschlagPanaschiert() {
    return anzahlStimmzettelNurEinzelnerWahlvorschlagPanaschiert;
  }

  public Long getAnzahlDirektstimmenVergleich() {
    return anzahlDirektstimmenVergleich;
  }

  public void setAnzahlDirektstimmenVergleich(Long anzahlDirektstimmenVergleich) {
    this.anzahlDirektstimmenVergleich = anzahlDirektstimmenVergleich;
  }

  public Long getAnzahlListenstimmenVergleich() {
    return anzahlListenstimmenVergleich;
  }

  public void setAnzahlListenstimmenVergleich(Long anzahlListenstimmenVergleich) {
    this.anzahlListenstimmenVergleich = anzahlListenstimmenVergleich;
  }

  public Long getAnzahlStimmzettelNurEinzelnerWahlvorschlagUnveraendertVergleich() {
    return anzahlStimmzettelNurEinzelnerWahlvorschlagUnveraendertVergleich;
  }

  public void setAnzahlStimmzettelNurEinzelnerWahlvorschlagUnveraendertVergleich(Long anzahlStimmzettelNurEinzelnerWahlvorschlagUnveraendertVergleich) {
    this.anzahlStimmzettelNurEinzelnerWahlvorschlagUnveraendertVergleich = anzahlStimmzettelNurEinzelnerWahlvorschlagUnveraendertVergleich;
  }

  public Long getAnzahlStimmzettelNurEinzelnerWahlvorschlagVeraendertVergleich() {
    return anzahlStimmzettelNurEinzelnerWahlvorschlagVeraendertVergleich;
  }

  public void setAnzahlStimmzettelNurEinzelnerWahlvorschlagVeraendertVergleich(Long anzahlStimmzettelNurEinzelnerWahlvorschlagVeraendertVergleich) {
    this.anzahlStimmzettelNurEinzelnerWahlvorschlagVeraendertVergleich = anzahlStimmzettelNurEinzelnerWahlvorschlagVeraendertVergleich;
  }

  public Long getAnzahlStimmzettelNurEinzelnerWahlvorschlagPanaschiertVergleich() {
    return anzahlStimmzettelNurEinzelnerWahlvorschlagPanaschiertVergleich;
  }

  public void setAnzahlStimmzettelNurEinzelnerWahlvorschlagPanaschiertVergleich(Long anzahlStimmzettelNurEinzelnerWahlvorschlagPanaschiertVergleich) {
    this.anzahlStimmzettelNurEinzelnerWahlvorschlagPanaschiertVergleich = anzahlStimmzettelNurEinzelnerWahlvorschlagPanaschiertVergleich;
  }

  public Long getAnzahlDirektstimmenAllerGruppen() {
    return anzahlDirektstimmenAllerGruppen;
  }

  public void setAnzahlDirektstimmenAllerGruppen(Long anzahlDirektstimmenAllerGruppen) {
    this.anzahlDirektstimmenAllerGruppen = anzahlDirektstimmenAllerGruppen;
  }

  public Long getAnzahlListenstimmenAllerGruppen() {
    return anzahlListenstimmenAllerGruppen;
  }

  public void setAnzahlListenstimmenAllerGruppen(Long anzahlListenstimmenAllerGruppen) {
    this.anzahlListenstimmenAllerGruppen = anzahlListenstimmenAllerGruppen;
  }

  @Nullable
  public Gruppe getGruppe() {
    return gruppe;
  }

  @Nullable
  public Long getAnzahlGesamtstimmenAlleGruppen() {
    if (getAnzahlDirektstimmenAllerGruppen() == null && getAnzahlListenstimmenAllerGruppen() == null) {
      return null;
    }

    Long result = 0L;

    if (getAnzahlDirektstimmenAllerGruppen() != null) {
      result += getAnzahlDirektstimmenAllerGruppen();
    }

    if (getAnzahlListenstimmenAllerGruppen() != null) {
      result += getAnzahlListenstimmenAllerGruppen();
    }

    return result;
  }

  public Long getAnzahlStimmzettelNurEinzelnerWahlvorschlagUnveraendertAllerGruppen() {
    return anzahlStimmzettelNurEinzelnerWahlvorschlagUnveraendertAllerGruppen;
  }

  public void setAnzahlStimmzettelNurEinzelnerWahlvorschlagUnveraendertAllerGruppen(Long anzahlStimmzettelNurEinzelnerWahlvorschlagUnveraendertAllerGruppen) {
    this.anzahlStimmzettelNurEinzelnerWahlvorschlagUnveraendertAllerGruppen = anzahlStimmzettelNurEinzelnerWahlvorschlagUnveraendertAllerGruppen;
  }

  public Long getAnzahlStimmzettelNurEinzelnerWahlvorschlagVeraendertAllerGruppen() {
    return anzahlStimmzettelNurEinzelnerWahlvorschlagVeraendertAllerGruppen;
  }

  public void setAnzahlStimmzettelNurEinzelnerWahlvorschlagVeraendertAllerGruppen(Long anzahlStimmzettelNurEinzelnerWahlvorschlagVeraendertAllerGruppen) {
    this.anzahlStimmzettelNurEinzelnerWahlvorschlagVeraendertAllerGruppen = anzahlStimmzettelNurEinzelnerWahlvorschlagVeraendertAllerGruppen;
  }

  public Long getAnzahlStimmzettelNurEinzelnerWahlvorschlagPanaschiertAllerGruppen() {
    return anzahlStimmzettelNurEinzelnerWahlvorschlagPanaschiertAllerGruppen;
  }

  public void setAnzahlStimmzettelNurEinzelnerWahlvorschlagPanaschiertAllerGruppen(Long anzahlStimmzettelNurEinzelnerWahlvorschlagPanaschiertAllerGruppen) {
    this.anzahlStimmzettelNurEinzelnerWahlvorschlagPanaschiertAllerGruppen = anzahlStimmzettelNurEinzelnerWahlvorschlagPanaschiertAllerGruppen;
  }

  public Double getAnteilDirektstimmen() {
    return rundeAufFixeAnzahlNachkommastellen(getAnteilDirektstimmenUngerundet());
  }

  @Nullable
  public Double getAnteilDirektstimmenUngerundet() {
    return getAnteilStimmenUngerundet(getAnzahlDirektstimmen(), getAnzahlDirektstimmenAllerGruppen());
  }

  public String getAnteilDirektstimmenTooltip() {
    return anteilDirektstimmenTooltip;
  }

  public void setAnteilDirektstimmenTooltip(String anteilDirektstimmenTooltip) {
    this.anteilDirektstimmenTooltip = anteilDirektstimmenTooltip;
  }

  public Double getAnteilListenstimmen() {
    return rundeAufFixeAnzahlNachkommastellen(getAnteilListenstimmenUngerundet());
  }

  @Nullable
  public Double getAnteilListenstimmenUngerundet() {
    return getAnteilStimmenUngerundet(getAnzahlListenstimmen(), getAnzahlListenstimmenAllerGruppen());
  }

  public String getAnteilListenstimmenTooltip() {
    return anteilListenstimmenTooltip;
  }

  public void setAnteilListenstimmenTooltip(String anteilListenstimmenTooltip) {
    this.anteilListenstimmenTooltip = anteilListenstimmenTooltip;
  }

  public Double getAnteilGesamtstimmen() {
    return rundeAufFixeAnzahlNachkommastellen(getAnteilGesamtstimmenUngerundet());
  }

  @Nullable
  public Double getAnteilGesamtstimmenUngerundet() {
    return getAnteilStimmenUngerundet(getAnzahlGesamtstimmen(), getAnzahlGesamtstimmenAlleGruppen());
  }

  public String getAnteilGesamtstimmenTooltip() {
    return anteilGesamtstimmenTooltip;
  }

  public void setAnteilGesamtstimmenTooltip(String anteilGesamtstimmenTooltip) {
    this.anteilGesamtstimmenTooltip = anteilGesamtstimmenTooltip;
  }

  public Long getAnzahlListenstimmenAllerGruppenVergleich() {
    return anzahlListenstimmenAllerGruppenVergleich;
  }

  public void setAnzahlListenstimmenAllerGruppenVergleich(Long anzahlListenstimmenAllerGruppenVergleich) {
    this.anzahlListenstimmenAllerGruppenVergleich = anzahlListenstimmenAllerGruppenVergleich;
  }

  public Long getAnzahlDirektstimmenAllerGruppenVergleich() {
    return anzahlDirektstimmenAllerGruppenVergleich;
  }

  public void setAnzahlDirektstimmenAllerGruppenVergleich(Long anzahlDirektstimmenAllerGruppenVergleich) {
    this.anzahlDirektstimmenAllerGruppenVergleich = anzahlDirektstimmenAllerGruppenVergleich;
  }

  public Long getAnzahlStimmzettelNurEinzelnerWahlvorschlagUnveraendertAllerGruppenVergleich() {
    return anzahlStimmzettelNurEinzelnerWahlvorschlagUnveraendertAllerGruppenVergleich;
  }

  public void setAnzahlStimmzettelNurEinzelnerWahlvorschlagUnveraendertAllerGruppenVergleich(Long anzahlStimmzettelNurEinzelnerWahlvorschlagUnveraendertAllerGruppenVergleich) {
    this.anzahlStimmzettelNurEinzelnerWahlvorschlagUnveraendertAllerGruppenVergleich = anzahlStimmzettelNurEinzelnerWahlvorschlagUnveraendertAllerGruppenVergleich;
  }

  public Long getAnzahlStimmzettelNurEinzelnerWahlvorschlagVeraendertAllerGruppenVergleich() {
    return anzahlStimmzettelNurEinzelnerWahlvorschlagVeraendertAllerGruppenVergleich;
  }

  public void setAnzahlStimmzettelNurEinzelnerWahlvorschlagVeraendertAllerGruppenVergleich(Long anzahlStimmzettelNurEinzelnerWahlvorschlagVeraendertAllerGruppenVergleich) {
    this.anzahlStimmzettelNurEinzelnerWahlvorschlagVeraendertAllerGruppenVergleich = anzahlStimmzettelNurEinzelnerWahlvorschlagVeraendertAllerGruppenVergleich;
  }

  public Long getAnzahlStimmzettelNurEinzelnerWahlvorschlagPanaschiertAllerGruppenVergleich() {
    return anzahlStimmzettelNurEinzelnerWahlvorschlagPanaschiertAllerGruppenVergleich;
  }

  public void setAnzahlStimmzettelNurEinzelnerWahlvorschlagPanaschiertAllerGruppenVergleich(Long anzahlStimmzettelNurEinzelnerWahlvorschlagPanaschiertAllerGruppenVergleich) {
    this.anzahlStimmzettelNurEinzelnerWahlvorschlagPanaschiertAllerGruppenVergleich = anzahlStimmzettelNurEinzelnerWahlvorschlagPanaschiertAllerGruppenVergleich;
  }

  public boolean isAnzahlDirektstimmenVorperiodeVorhanden() {
    return anzahlDirektstimmenVorperiodeVorhanden;
  }

  public void setAnzahlDirektstimmenVorperiodeVorhanden(boolean anzahlDirektstimmenVorperiodeVorhanden) {
    this.anzahlDirektstimmenVorperiodeVorhanden = anzahlDirektstimmenVorperiodeVorhanden;
  }

  public Long getAnzahlDirektstimmenVorperiode() {
    return anzahlDirektstimmenVorperiode;
  }

  public void setAnzahlDirektstimmenVorperiode(Long anzahlDirektstimmenVorperiode) {
    this.anzahlDirektstimmenVorperiode = anzahlDirektstimmenVorperiode;
  }

  public boolean isAnzahlListenstimmenVorperiodeVorhanden() {
    return anzahlListenstimmenVorperiodeVorhanden;
  }

  public void setAnzahlListenstimmenVorperiodeVorhanden(boolean anzahlListenstimmenVorperiodeVorhanden) {
    this.anzahlListenstimmenVorperiodeVorhanden = anzahlListenstimmenVorperiodeVorhanden;
  }

  public Long getAnzahlListenstimmenVorperiode() {
    return anzahlListenstimmenVorperiode;
  }

  public void setAnzahlListenstimmenVorperiode(Long anzahlListenstimmenVorperiode) {
    this.anzahlListenstimmenVorperiode = anzahlListenstimmenVorperiode;
  }

  public boolean isAnzahlGesamtstimmenVorperiodeVorhanden() {
    return isAnzahlDirektstimmenVorperiodeVorhanden() || isAnzahlListenstimmenVorperiodeVorhanden();
  }

  @Nullable
  public Long getAnzahlGesamtstimmenVorperiode() {
    Long anzahlDirektstimmenVorperiode = getAnzahlDirektstimmenVorperiode();
    Long anzahlListenstimmenVorperiode = getAnzahlListenstimmenVorperiode();

    if (anzahlDirektstimmenVorperiode == null) {
      return anzahlListenstimmenVorperiode;
    }

    if (anzahlListenstimmenVorperiode == null) {
      return anzahlDirektstimmenVorperiode;
    }

    return anzahlDirektstimmenVorperiode + anzahlListenstimmenVorperiode;
  }

  public Long getAnzahlStimmzettelNurEinzelnerWahlvorschlagUnveraendertVorperiode() {
    return anzahlStimmzettelNurEinzelnerWahlvorschlagUnveraendertVorperiode;
  }

  public void setAnzahlStimmzettelNurEinzelnerWahlvorschlagUnveraendertVorperiode(Long anzahlStimmzettelNurEinzelnerWahlvorschlagUnveraendertVorperiode) {
    this.anzahlStimmzettelNurEinzelnerWahlvorschlagUnveraendertVorperiode = anzahlStimmzettelNurEinzelnerWahlvorschlagUnveraendertVorperiode;
  }

  public Long getAnzahlStimmzettelNurEinzelnerWahlvorschlagVeraendertVorperiode() {
    return anzahlStimmzettelNurEinzelnerWahlvorschlagVeraendertVorperiode;
  }

  public void setAnzahlStimmzettelNurEinzelnerWahlvorschlagVeraendertVorperiode(Long anzahlStimmzettelNurEinzelnerWahlvorschlagVeraendertVorperiode) {
    this.anzahlStimmzettelNurEinzelnerWahlvorschlagVeraendertVorperiode = anzahlStimmzettelNurEinzelnerWahlvorschlagVeraendertVorperiode;
  }

  public Long getAnzahlStimmzettelNurEinzelnerWahlvorschlagPanaschiertVorperiode() {
    return anzahlStimmzettelNurEinzelnerWahlvorschlagPanaschiertVorperiode;
  }

  public void setAnzahlStimmzettelNurEinzelnerWahlvorschlagPanaschiertVorperiode(Long anzahlStimmzettelNurEinzelnerWahlvorschlagPanaschiertVorperiode) {
    this.anzahlStimmzettelNurEinzelnerWahlvorschlagPanaschiertVorperiode = anzahlStimmzettelNurEinzelnerWahlvorschlagPanaschiertVorperiode;
  }

  public boolean isAnzahlDirektstimmenVorperiodeAllerGruppenVorhanden() {
    return anzahlDirektstimmenVorperiodeAllerGruppenVorhanden;
  }

  public void setAnzahlDirektstimmenVorperiodeAllerGruppenVorhanden(boolean anzahlDirektstimmenVorperiodeAllerGruppenVorhanden) {
    this.anzahlDirektstimmenVorperiodeAllerGruppenVorhanden = anzahlDirektstimmenVorperiodeAllerGruppenVorhanden;
  }

  public Long getAnzahlDirektstimmenVorperiodeAllerGruppen() {
    return anzahlDirektstimmenVorperiodeAllerGruppen;
  }

  public void setAnzahlDirektstimmenVorperiodeAllerGruppen(Long anzahlDirektstimmenVorperiodeAllerGruppen) {
    this.anzahlDirektstimmenVorperiodeAllerGruppen = anzahlDirektstimmenVorperiodeAllerGruppen;
  }

  public boolean isAnzahlListenstimmenVorperiodeAllerGruppenVorhanden() {
    return anzahlListenstimmenVorperiodeAllerGruppenVorhanden;
  }

  public void setAnzahlListenstimmenVorperiodeAllerGruppenVorhanden(boolean anzahlListenstimmenVorperiodeAllerGruppenVorhanden) {
    this.anzahlListenstimmenVorperiodeAllerGruppenVorhanden = anzahlListenstimmenVorperiodeAllerGruppenVorhanden;
  }

  public Long getAnzahlListenstimmenVorperiodeAllerGruppen() {
    return anzahlListenstimmenVorperiodeAllerGruppen;
  }

  public void setAnzahlListenstimmenVorperiodeAllerGruppen(Long anzahlListenstimmenVorperiodeAllerGruppen) {
    this.anzahlListenstimmenVorperiodeAllerGruppen = anzahlListenstimmenVorperiodeAllerGruppen;
  }

  public boolean isAnzahlGesamtstimmenVorperiodeAlleGruppen() {
    return isAnzahlDirektstimmenVorperiodeAllerGruppenVorhanden() || isAnzahlListenstimmenVorperiodeAllerGruppenVorhanden();
  }

  @Nullable
  public Long getAnzahlGesamtstimmenVorperiodeAlleGruppen() {
    if (getAnzahlDirektstimmenVorperiodeAllerGruppen() == null && getAnzahlListenstimmenVorperiodeAllerGruppen() == null) {
      return null;
    }

    Long result = 0L;

    if (getAnzahlDirektstimmenVorperiodeAllerGruppen() != null) {
      result += getAnzahlDirektstimmenVorperiodeAllerGruppen();
    }

    if (getAnzahlListenstimmenVorperiodeAllerGruppen() != null) {
      result += getAnzahlListenstimmenVorperiodeAllerGruppen();
    }

    return result;
  }

  public Long getAnzahlStimmzettelNurEinzelnerWahlvorschlagUnveraendertVorperiodeAllerGruppen() {
    return anzahlStimmzettelNurEinzelnerWahlvorschlagUnveraendertVorperiodeAllerGruppen;
  }

  public void setAnzahlStimmzettelNurEinzelnerWahlvorschlagUnveraendertVorperiodeAllerGruppen(Long anzahlStimmzettelNurEinzelnerWahlvorschlagUnveraendertVorperiodeAllerGruppen) {
    this.anzahlStimmzettelNurEinzelnerWahlvorschlagUnveraendertVorperiodeAllerGruppen = anzahlStimmzettelNurEinzelnerWahlvorschlagUnveraendertVorperiodeAllerGruppen;
  }

  public Long getAnzahlStimmzettelNurEinzelnerWahlvorschlagVeraendertVorperiodeAllerGruppen() {
    return anzahlStimmzettelNurEinzelnerWahlvorschlagVeraendertVorperiodeAllerGruppen;
  }

  public void setAnzahlStimmzettelNurEinzelnerWahlvorschlagVeraendertVorperiodeAllerGruppen(Long anzahlStimmzettelNurEinzelnerWahlvorschlagVeraendertVorperiodeAllerGruppen) {
    this.anzahlStimmzettelNurEinzelnerWahlvorschlagVeraendertVorperiodeAllerGruppen = anzahlStimmzettelNurEinzelnerWahlvorschlagVeraendertVorperiodeAllerGruppen;
  }

  public Long getAnzahlStimmzettelNurEinzelnerWahlvorschlagPanaschiertVorperiodeAllerGruppen() {
    return anzahlStimmzettelNurEinzelnerWahlvorschlagPanaschiertVorperiodeAllerGruppen;
  }

  public void setAnzahlStimmzettelNurEinzelnerWahlvorschlagPanaschiertVorperiodeAllerGruppen(Long anzahlStimmzettelNurEinzelnerWahlvorschlagPanaschiertVorperiodeAllerGruppen) {
    this.anzahlStimmzettelNurEinzelnerWahlvorschlagPanaschiertVorperiodeAllerGruppen = anzahlStimmzettelNurEinzelnerWahlvorschlagPanaschiertVorperiodeAllerGruppen;
  }

  public Double getAnteilDirektstimmenVorperiode() {
    return rundeAufFixeAnzahlNachkommastellen(getAnteilDirektstimmenVorperiodeUngerundet());
  }

  @Nullable
  public Double getAnteilDirektstimmenVorperiodeUngerundet() {
    return getAnteilStimmenUngerundet(getAnzahlDirektstimmenVorperiode(), getAnzahlDirektstimmenVorperiodeAllerGruppen());
  }

  public String getAnteilDirektstimmenVorperiodeTooltip() {
    return anteilDirektstimmenVorperiodeTooltip;
  }

  public void setAnteilDirektstimmenVorperiodeTooltip(String anteilDirektstimmenVorperiodeTooltip) {
    this.anteilDirektstimmenVorperiodeTooltip = anteilDirektstimmenVorperiodeTooltip;
  }

  public Double getAnteilListenstimmenVorperiode() {
    return rundeAufFixeAnzahlNachkommastellen(getAnteilListenstimmenVorperiodeUngerundet());
  }

  @Nullable
  public Double getAnteilListenstimmenVorperiodeUngerundet() {
    return getAnteilStimmenUngerundet(getAnzahlListenstimmenVorperiode(), getAnzahlListenstimmenVorperiodeAllerGruppen());
  }

  public String getAnteilListenstimmenVorperiodeTooltip() {
    return anteilListenstimmenVorperiodeTooltip;
  }

  public void setAnteilListenstimmenVorperiodeTooltip(String anteilListenstimmenVorperiodeTooltip) {
    this.anteilListenstimmenVorperiodeTooltip = anteilListenstimmenVorperiodeTooltip;
  }

  public Double getAnteilGesamtstimmenVorperiode() {
    return rundeAufFixeAnzahlNachkommastellen(getAnteilGesamtstimmenVorperiodeUngerundet());
  }

  @Nullable
  public Double getAnteilGesamtstimmenVorperiodeUngerundet() {
    return getAnteilStimmenUngerundet(getAnzahlGesamtstimmenVorperiode(), getAnzahlGesamtstimmenVorperiodeAlleGruppen());
  }

  public String getAnteilGesamtstimmenVorperiodeTooltip() {
    return anteilGesamtstimmenVorperiodeTooltip;
  }

  public void setAnteilGesamtstimmenVorperiodeTooltip(String anteilGesamtstimmenVorperiodeTooltip) {
    this.anteilGesamtstimmenVorperiodeTooltip = anteilGesamtstimmenVorperiodeTooltip;
  }

  public @Nullable Long getAnzahlDirektstimmenDifferenz() {
    return differenz(getAnzahlDirektstimmen(), getAnzahlDirektstimmenVorperiode());
  }

  public @Nullable Double getAnteilDirektstimmenDifferenz() {
    return differenz(getAnteilDirektstimmenUngerundet(), getAnteilDirektstimmenVorperiodeUngerundet());
  }

  public @Nullable Long getAnzahlListenstimmenDifferenz() {
    return differenz(getAnzahlListenstimmen(), getAnzahlListenstimmenVorperiode());
  }

  public @Nullable Double getAnteilListenstimmenDifferenz() {
    return differenz(getAnteilListenstimmenUngerundet(), getAnteilListenstimmenVorperiodeUngerundet());
  }

  public @Nullable Long getAnzahlGesamtstimmenDifferenz() {
    return differenz(getAnzahlGesamtstimmen(), getAnzahlGesamtstimmenVorperiode());
  }

  public @Nullable Double getAnteilGesamtstimmenDifferenz() {
    return differenz(getAnteilGesamtstimmenUngerundet(), getAnteilGesamtstimmenVorperiodeUngerundet());
  }

  public boolean isSystemGruppe() {
    return gruppe != null && gruppe.isSystemGruppe();
  }

  public boolean isSystemGruppeWaehler() {
    return isSystemGruppe(SystemGruppe.WAEHLER);
  }

  public boolean isSystemGruppeWahlberechtigte() {
    return isSystemGruppe(SystemGruppe.WAHLBERECHTIGTE);
  }

  public boolean isSystemGruppeGueltige() {
    return isSystemGruppe(SystemGruppe.GUELTIGE);
  }

  public boolean isSystemGruppeUngueltige() {
    return isSystemGruppe(SystemGruppe.UNGUELTIGE);
  }

  private boolean isSystemGruppe(SystemGruppe systemGruppe) {
    return isSystemGruppe() && systemGruppe != null && systemGruppe.equalsGruppe(gruppe, systemGruppeEigenschaften);
  }

  public Long getPosition() {
    if (position == null && gruppeImGebiet != null) {
      return gruppeImGebiet.getRelativePositionAbgeleitet();
    }

    return position;
  }

  public void setPosition(Long position) {
    this.position = position;
  }

  public void setKennungUndNamenAufGebiet(WahlartErgebnisart ergebnisart, Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, Map<Gruppe, Set<Wahlvorschlag>> wahlvorschlaegeInWahlPerGruppe,
      Collection<GruppeImGebiet> gruppenImGebiet, StimmeingabeGruppeStimmenErfassenLabelGenerator stimmeingabeGruppeStimmenErfassenLabelGenerator) {
    if (gruppeImGebiet != null) {
      if (gruppeImGebiet.isSystemGruppe() || gruppeImGebiet.hatWahlvorschlagAufStimmzettelInUebergeordneterGebietshierarchie(wahlvorschlaegeInWahlPerGruppe)) {
        setKennung(stimmeingabeGruppeStimmenErfassenLabelGenerator.getGruppeStimmenKennung(entityAufStimmzettelDaten, gruppeImGebiet, ergebnisart, gruppenImGebiet));
        setName(stimmeingabeGruppeStimmenErfassenLabelGenerator.getGruppeStimmenBezeichnung(entityAufStimmzettelDaten, gruppeImGebiet, ergebnisart, gruppenImGebiet));
      }

      if (wahldurchfuehrungsgebiet != null) {
        Gebiet direktwahlgebiet = wahldurchfuehrungsgebiet.getUebergeordnetesDirektwahlgebiet();

        if (direktwahlgebiet != null) {
          wahlvorschlaegeInWahlPerGruppe.getOrDefault(gruppeImGebiet.getGruppe(), emptySet()).stream() //
              .filter(wahlvorschlag -> wahlvorschlag.isDirektkandidat() && Objects.equals(wahlvorschlag.getGebiet(), direktwahlgebiet)).findFirst() //
              .map(wahlvorschlag -> wahlvorschlag.castTo(Direktkandidat.class).getEntity()).ifPresent(direktkandidat -> {
                Direktkandidatur direktkandidatur = direktkandidat.getDirektkandidatur();
                GruppeImGebiet direktkandidaturGruppeImGebiet = direktkandidatur.getGruppeImGebiet();

                setName(direktkandidat.getKurznameOderKennwort()); // ELECT-13186, Kurzname oder Kennwort, um bisheriges Verhalten bei Erst- und Zweitstimme beizubehalten...
                setKurzname(direktkandidaturGruppeImGebiet.getKurznameAufGebiet());
                setNameDirektkandidat(getName(direktkandidatur));
              });
        }
      }
    }
  }

  public String getKennung() {
    return kennung;
  }

  public void setKennung(String kennung) {
    this.kennung = kennung;
  }

  public String getName() {
    if (name == null) {
      name = bestimmeName();
    }

    if (isNotBlank(nameDirektkandidat)) {
      if (name == null) {
        return getNameDirektkandidat();
      }

      return name + " (" + nameDirektkandidat + ")";
    }

    return name;
  }

  private String bestimmeName() {
    if (gruppeImGebiet == null) {
      return null;
    }

    return gruppeImGebiet.getNameAufGebiet();
  }

  public String getNameOhneDirektkandidat() {
    if (name == null) {
      name = bestimmeName();
    }

    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  protected @Nullable Double rundeAufFixeAnzahlNachkommastellen(@Nullable Double value) {
    return value == null ? null : Math.round(value * 1000d) / 1000d;
  }

  protected @Nullable Double differenz(@Nullable Double x, @Nullable Double y) {
    return x == null ? null : y == null ? null : x - y;
  }

  protected @Nullable Long differenz(@Nullable Long x, @Nullable Long y) {
    return x == null ? null : y == null ? null : x - y;
  }

  /**
   * @return ob es in der Gebietshierarchie einen ZUGELASSENEN Direktkandidaten der Gruppe gibt
   */
  public boolean isHatDirektkandidatenInGebietshierarchie() {
    return hatDirektkandidatenInGebietshierarchie;
  }

  public void setHatDirektkandidatenInGebietshierarchie(boolean hatDirektkandidatenInGebietshierarchie) {
    this.hatDirektkandidatenInGebietshierarchie = hatDirektkandidatenInGebietshierarchie;
  }

  public boolean isHatListeInGebietshierarchie() {
    return hatListeInGebietshierarchie;
  }

  public void setHatListeInGebietshierarchie(boolean hatListeInGebietshierarchie) {
    this.hatListeInGebietshierarchie = hatListeInGebietshierarchie;
  }

  public boolean isVorperiodenStimmenVorhanden() {
    return vorperiodenStimmenVorhanden;
  }

  public void setVorperiodenStimmenVorhanden(boolean vorperiodenStimmenVorhanden) {
    this.vorperiodenStimmenVorhanden = vorperiodenStimmenVorhanden;
  }

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

  @Nullable
  public Wahldurchfuehrungsgebiet getGebiet() {
    return gebiet;
  }

  public String getNameDirektkandidat() {
    if (isNotBlank(nameDirektkandidat)) {
      if (isBlank(kurzname)) {
        return nameDirektkandidat;
      }

      return nameDirektkandidat + " (" + kurzname + ")";
    }

    return getNameOhneDirektkandidat();
  }

  public void setNameDirektkandidat(String nameDirektkandidat) {
    this.nameDirektkandidat = nameDirektkandidat;
  }

  public String getKurzname() {
    return kurzname;
  }

  public void setKurzname(String kurzname) {
    this.kurzname = kurzname;
  }

  protected Double getAnteilStimmenUngerundet(Long anzahlStimmen, Long anzahlStimmenAllerGruppen) {
    if (anzahlStimmen == null || anzahlStimmenAllerGruppen == null || anzahlStimmenAllerGruppen == 0) {
      return null;
    }

    return 1.0d * anzahlStimmen / anzahlStimmenAllerGruppen;
  }
}
