
package de.ivu.elect.dokumente.wahlvorschlag;

import static de.ivu.elect.jee.entity.EntityUtil.castTo;
import static java.util.stream.Collectors.toSet;
import static org.apache.commons.lang3.StringUtils.isNotBlank;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import de.ivu.document.generator.template.annotations.Document;
import de.ivu.document.generator.template.annotations.DocumentComplexField;
import de.ivu.document.generator.template.annotations.DocumentRepeat;
import de.ivu.document.generator.template.annotations.DocumentSimpleField;
import de.ivu.document.generator.template.annotations.DocumentSubTemplate;
import de.ivu.elect.business.dokumente.entity.DokumentVorlageArt;
import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
import de.ivu.elect.business.wahlvorschlag.control.ListeByGebietUndRelativePositionComparator;
import de.ivu.elect.business.wahlvorschlag.control.ListenverbindungNachPositionListenComparator;
import de.ivu.elect.business.wahlvorschlag.control.WahlvorschlagByRelativePositionComparator;
import de.ivu.elect.business.wahlvorschlag.control.WahlvorschlagByStimmzettelPositionComparator;
import de.ivu.elect.business.wahlvorschlag.control.WahlvorschlagByVorgegebenePositionComparator;
import de.ivu.elect.business.wahlvorschlag.control.WahlvorschlagByZeitpunktDerEinreichungComparator;
import de.ivu.elect.business.wahlvorschlag.entity.Direktkandidat;
import de.ivu.elect.business.wahlvorschlag.entity.Liste;
import de.ivu.elect.business.wahlvorschlag.entity.Listenverbindung;
import de.ivu.elect.business.wahlvorschlag.entity.ReferendumOption;
import de.ivu.elect.business.wahlvorschlag.entity.Wahlvorschlag;
import de.ivu.elect.dokumente.CoreElectContext;
import de.ivu.elect.dokumente.ElectDocument;
import de.ivu.elect.dokumente.gebiet.GebietDokument;
import de.ivu.elect.dokumente.gebiet.GebietMitUntergeordnetenErfassungsgebietenDocument;
import de.ivu.elect.dokumente.wahlausschuss.WahlausschussDokument;

@Document(DokumentVorlageArt.WAHLVORSCHLAEGE_ALS_LISTE_MAENGEL_DOKUMENT_PREFIX)
public class WahlvorschlaegeAlsListeDocument extends ElectDocument {

  private static final long serialVersionUID = 1L;

  @DocumentComplexField("Wurzelgebiet")
  public final GebietDokument wurzelgebiet; // ELECT-10132

  @DocumentComplexField("Gebiet")
  public final GebietMitUntergeordnetenErfassungsgebietenDocument gebiet;

  @DocumentSubTemplate
  @DocumentComplexField("Wahlausschuss")
  public final WahlausschussDokument wahlausschuss;

  /**
   * @deprecated
   *
   * @see WahlvorschlaegeAlsListeDocument#wahlausschuss
   */
  @Deprecated
  @DocumentSubTemplate
  @DocumentComplexField("Wahlauschuss")
  public final WahlausschussDokument wahlauschuss;

  /**
   * @see WahlvorschlaegeAlsListeDocument#alleWahlvorschlaegeSortiertNachStimmzettelPosition
   */
  @DocumentSubTemplate
  @DocumentRepeat(value = "WahlvorschlaegeInGebiet", elementClass = WahlvorschlagsdatenDocument.class, itemName = "wahlvorschlag")
  public final List<WahlvorschlagsdatenDocument> wahlvorschlaegeInGebiet;

  @DocumentSubTemplate
  @DocumentRepeat(value = "WahlvorschlaegeInGebietshierarchieUebergeordnet", elementClass = WahlvorschlagsdatenDocument.class, itemName = "wahlvorschlag")
  public final List<WahlvorschlagsdatenDocument> wahlvorschlaegeInGebietshierarchieUebergeordnet;

  /**
   * @deprecated
   *
   * @see WahlvorschlaegeAlsListeDocument#wahlvorschlaegeInGebiet
   */
  @Deprecated
  @DocumentSubTemplate
  @DocumentRepeat(value = "Wahlvorschlaege", elementClass = WahlvorschlagsdatenDocument.class, itemName = "wahlvorschlag")
  public final List<WahlvorschlagsdatenDocument> alleWahlvorschlaege;

  @DocumentSubTemplate
  @DocumentRepeat(value = "WahlvorschlaegeInGebietNachPositionStimmzettel", elementClass = WahlvorschlagsdatenDocument.class, itemName = "wahlvorschlag")
  public final List<WahlvorschlagsdatenDocument> alleWahlvorschlaegeSortiertNachStimmzettelPosition;

  @DocumentSubTemplate
  @DocumentRepeat(value = "WahlvorschlaegeInGebietNachPositionVorgegeben", elementClass = WahlvorschlagsdatenDocument.class, itemName = "wahlvorschlag")
  public final List<WahlvorschlagsdatenDocument> alleWahlvorschlaegeSortiertNachVorgegebenerPosition;

  @DocumentSubTemplate
  @DocumentRepeat(value = "WahlvorschlaegeInGebietNachPositionRelativ", elementClass = WahlvorschlagsdatenDocument.class, itemName = "wahlvorschlag")
  public final List<WahlvorschlagsdatenDocument> alleWahlvorschlaegeSortiertNachRelativerPosition;

  @DocumentSubTemplate
  @DocumentRepeat(value = "WahlvorschlaegeInGebietNachZeitpunktDerEinreichung", elementClass = WahlvorschlagsdatenDocument.class, itemName = "wahlvorschlag")
  public final List<WahlvorschlagsdatenDocument> alleWahlvorschlaegeSortiertNachZeitpunktDerEinreichung; // ELECT-11614

  @DocumentSimpleField("AnzahlWahlvorschlaegeInGebietInsgesamt")
  public final Long anzahlWahlvorschlaegeInsgesamt;

  @DocumentSimpleField("AnzahlWahlvorschlaegeInGebietZulassenErlaubt")
  public final Long anzahlWahlvorschlaegeZulassenErlaubt;

  @DocumentSimpleField("AnzahlWahlvorschlaegeInGebietZugelassen")
  public final Long anzahlWahlvorschlaegeZugelassen;

  @DocumentSimpleField("AnzahlWahlvorschlaegeInGebietNichtZugelassen")
  public final Long anzahlWahlvorschlaegeNichtZugelassen;

  @DocumentSubTemplate
  @DocumentRepeat(value = "ListenInGebiet", elementClass = WahlvorschlagsdatenDocument.class, itemName = "liste")
  public final List<WahlvorschlagsdatenDocument> listenInGebiet;

  @DocumentSubTemplate
  @DocumentRepeat(value = "ListenInGebietshierarchieUebergeordnet", elementClass = WahlvorschlagsdatenDocument.class, itemName = "liste")
  public final List<WahlvorschlagsdatenDocument> listenInGebietshierarchieUebergeordnet;

  @DocumentRepeat(value = "ListenverbindungenInGebiet", elementClass = ListenverbindungDokument.class, itemName = "listenverbindung")
  public final List<ListenverbindungDokument> listenverbindungenInGebiet;

  @DocumentRepeat(value = "ListenverbindungenInGebietshierarchieUebergeordnet", elementClass = ListenverbindungDokument.class, itemName = "listenverbindung")
  public final List<ListenverbindungDokument> listenverbindungenInGebietshierarchieUebergeordnet;

  /**
   * @deprecated
   *
   * @see WahlvorschlaegeAlsListeDocument#listenverbindungenInGebiet#size
   */
  @Deprecated
  @DocumentSimpleField("ListenverbindungenVorhanden")
  public final boolean listenverbindungenVorhanden;

  /**
   * @deprecated
   *
   * @see WahlvorschlaegeAlsListeDocument#listenverbindungenInGebiet
   */
  @Deprecated
  @DocumentRepeat(value = "Listenverbindungen", elementClass = ListenverbindungDokument.class, itemName = "listenverbindung")
  public final List<ListenverbindungDokument> listenverbindungen;

  @DocumentSimpleField("AnzahlListenInGebietInsgesamt")
  public final Long anzahlListenInsgesamt;

  @DocumentSimpleField("AnzahlListenInGebietZulassenErlaubt")
  public final Long anzahlListenZulassenErlaubt;

  @DocumentSimpleField("AnzahlListenInGebietZugelassen")
  public final Long anzahlListenZugelassen;

  @DocumentSimpleField("AnzahlListenInGebietNichtZugelassen")
  public final Long anzahlListenNichtZugelassen;

  @DocumentSimpleField("AnzahlListenkandidaturenInGebietInsgesamt")
  public final Long anzahlListenkandidaturenInsgesamt;

  @DocumentSimpleField("AnzahlListenkandidaturenInGebietInsgesamtAufListenZulassenErlaubt")
  public final Long anzahlListenkandidaturenInsgesamtAufListenZulassenErlaubt;

  @DocumentSimpleField("AnzahlListenkandidaturenInGebietInsgesamtAufListenZugelassen")
  public final Long anzahlListenkandidaturenInsgesamtAufListenZugelassen;

  @DocumentSimpleField("AnzahlListenkandidaturenInGebietInsgesamtAufListenNichtZugelassen")
  public final Long anzahlListenkandidaturenInsgesamtAufListenNichtZugelassen;

  @DocumentSimpleField("AnzahlListenkandidaturenInGebietZulassenErlaubt")
  public final Long anzahlListenkandidaturenZulassenErlaubt;

  @DocumentSimpleField("AnzahlListenkandidaturenInGebietZulassenErlaubtAufListenZulassenErlaubt")
  public final Long anzahlListenkandidaturenZulassenErlaubtAufListenZulassenErlaubt;

  @DocumentSimpleField("AnzahlListenkandidaturenInGebietZulassenErlaubtAufListenZugelassen")
  public final Long anzahlListenkandidaturenZulassenErlaubtAufListenZugelassen;

  @DocumentSimpleField("AnzahlListenkandidaturenInGebietZulassenErlaubtAufListenNichtZugelassen")
  public final Long anzahlListenkandidaturenZulassenErlaubtAufListenNichtZugelassen;

  @DocumentSimpleField("AnzahlListenkandidaturenInGebietZugelassen")
  public final Long anzahlListenkandidaturenZugelassen;

  @DocumentSimpleField("AnzahlListenkandidaturenInGebietZugelassenAufListenZulassenErlaubt")
  public final Long anzahlListenkandidaturenZugelassenAufListenZulassenErlaubt;

  @DocumentSimpleField("AnzahlListenkandidaturenInGebietZugelassenAufListenZugelassen")
  public final Long anzahlListenkandidaturenZugelassenAufListenZugelassen;

  @DocumentSimpleField("AnzahlListenkandidaturenInGebietZugelassenAufListenNichtZugelassen")
  public final Long anzahlListenkandidaturenZugelassenAufListenNichtZugelassen;

  @DocumentSimpleField("AnzahlListenkandidaturenInGebietNichtZugelassen")
  public final Long anzahlListenkandidaturenNichtZugelassen;

  @DocumentSimpleField("AnzahlListenkandidaturenInGebietNichtZugelassenAufListenZulassenErlaubt")
  public final Long anzahlListenkandidaturenNichtZugelassenAufListenZulassenErlaubt;

  @DocumentSimpleField("AnzahlListenkandidaturenInGebietNichtZugelassenAufListenZugelassen")
  public final Long anzahlListenkandidaturenNichtZugelassenAufListenZugelassen;

  @DocumentSimpleField("AnzahlListenkandidaturenInGebietNichtZugelassenAufListenNichtZugelassen")
  public final Long anzahlListenkandidaturenNichtZugelassenAufListenNichtZugelassen;

  @DocumentSubTemplate
  @DocumentRepeat(value = "DirektkandidatenInGebiet", elementClass = WahlvorschlagsdatenDocument.class, itemName = "direktkandidat")
  public final List<WahlvorschlagsdatenDocument> direktkandidatenInGebiet;

  @DocumentSubTemplate
  @DocumentRepeat(value = "DirektkandidatenInGebietshierarchieUebergeordnet", elementClass = WahlvorschlagsdatenDocument.class, itemName = "direktkandidat")
  public final List<WahlvorschlagsdatenDocument> direktkandidatenInGebietshierarchieUebergeordnet;

  @DocumentSimpleField("AnzahlDirektkandidatenInGebietInsgesamt")
  public final Long anzahlDirektkandidatenInsgesamt;

  @DocumentSimpleField("AnzahlDirektkandidatenInGebietZulassenErlaubt")
  public final Long anzahlDirektkandidatenZulassenErlaubt;

  @DocumentSimpleField("AnzahlDirektkandidatenInGebietZugelassen")
  public final Long anzahlDirektkandidatenZugelassen;

  @DocumentSimpleField("AnzahlDirektkandidatenInGebietNichtZugelassen")
  public final Long anzahlDirektkandidatenNichtZugelassen;

  @DocumentSubTemplate
  @DocumentRepeat(value = "ReferendumOptionenInGebiet", elementClass = WahlvorschlagsdatenDocument.class, itemName = "referendumOption")
  public final List<WahlvorschlagsdatenDocument> referendumOptionenInGebiet;

  @DocumentSubTemplate
  @DocumentRepeat(value = "ReferendumOptionenInGebietshierarchieUebergeordnet", elementClass = WahlvorschlagsdatenDocument.class, itemName = "referendumOption")
  public final List<WahlvorschlagsdatenDocument> referendumOptionenInGebietshierarchieUebergeordnet;

  @DocumentSimpleField("AnzahlReferendumOptionenInGebietInsgesamt")
  public final Long anzahlReferendumOptionenInsgesamt;

  @DocumentSimpleField("AnzahlReferendumOptionenInGebietZulassenErlaubt")
  public final Long anzahlReferendumOptionenZulassenErlaubt;

  @DocumentSimpleField("AnzahlReferendumOptionenInGebietZugelassen")
  public final Long anzahlReferendumOptionenZugelassen;

  @DocumentSimpleField("AnzahlReferendumOptionenInGebietNichtZugelassen")
  public final Long anzahlReferendumOptionenNichtZugelassen;

  public WahlvorschlaegeAlsListeDocument(CoreElectContext coreElectContext, Gebiet gebiet, WahlvorschlagCoreDaten wahlvorschlagCoreDaten, Collection<Wahlvorschlag> wahlvorschlaege) {
    super(coreElectContext);

    Collection<Wahlvorschlag> wahlvorschlaegeInGebiet = wahlvorschlaege;
    Collection<Wahlvorschlag> wahlvorschlaegeInGebietshierarchieUebergeordnet = new HashSet<>(wahlvorschlaegeInGebiet); // ELECT-9180

    if (gebiet == null) {
      this.wurzelgebiet = GebietDokument.EMPTY_OBJECT;

      this.gebiet = GebietMitUntergeordnetenErfassungsgebietenDocument.EMPTY_OBJECT;
    } else {
      this.wurzelgebiet = new GebietDokument(coreElectContext, gebiet.getWurzelgebiet());

      this.gebiet = new GebietMitUntergeordnetenErfassungsgebietenDocument(coreElectContext, gebiet);

      for (Entry<Gebiet, Collection<Wahlvorschlag>> entry : wahlvorschlagCoreDaten.getWahlvorschlaegeInWahlPerGebiet().entrySet()) {
        if (gebiet.isInGebietshierarchieUntergeordnetesGebietVon(entry.getKey())) {
          wahlvorschlaegeInGebietshierarchieUebergeordnet.addAll(entry.getValue());
        }
      }
    }

    this.wahlausschuss = new WahlausschussDokument(coreElectContext, gebiet, wahlvorschlagCoreDaten.getWahlausschussmitgliederInWahlPerGebiet());
    this.wahlauschuss = this.wahlausschuss;

    this.wahlvorschlaegeInGebiet = createWahlvorschlagDocuments(coreElectContext, gebiet, wahlvorschlagCoreDaten, wahlvorschlaegeInGebiet);
    this.wahlvorschlaegeInGebietshierarchieUebergeordnet = createWahlvorschlagDocuments(coreElectContext, gebiet, wahlvorschlagCoreDaten, wahlvorschlaegeInGebietshierarchieUebergeordnet);

    this.alleWahlvorschlaege = this.wahlvorschlaegeInGebiet;

    this.alleWahlvorschlaegeSortiertNachStimmzettelPosition = createWahlvorschlagDocuments(coreElectContext, gebiet, wahlvorschlagCoreDaten, wahlvorschlaegeInGebiet,
        new WahlvorschlagByStimmzettelPositionComparator(gebiet, wahlvorschlagCoreDaten.getEntityAufStimmzettelDaten()));
    this.alleWahlvorschlaegeSortiertNachVorgegebenerPosition = createWahlvorschlagDocuments(coreElectContext, gebiet, wahlvorschlagCoreDaten, wahlvorschlaegeInGebiet,
        new WahlvorschlagByVorgegebenePositionComparator());
    this.alleWahlvorschlaegeSortiertNachRelativerPosition = createWahlvorschlagDocuments(coreElectContext, gebiet, wahlvorschlagCoreDaten, wahlvorschlaegeInGebiet,
        new WahlvorschlagByRelativePositionComparator());
    this.alleWahlvorschlaegeSortiertNachZeitpunktDerEinreichung = createWahlvorschlagDocuments(coreElectContext, gebiet, wahlvorschlagCoreDaten, wahlvorschlaegeInGebiet,
        WahlvorschlagByZeitpunktDerEinreichungComparator.ASCENDING);

    CoreWahlvorschlagsdatenAggregator coreWahlvorschlagsdatenAggregator = new CoreWahlvorschlagsdatenAggregator(wahlvorschlagCoreDaten.getZulassungPruefer(), wahlvorschlaege);

    this.anzahlWahlvorschlaegeInsgesamt = coreWahlvorschlagsdatenAggregator.countWahlvorschlaege();
    this.anzahlWahlvorschlaegeZulassenErlaubt = coreWahlvorschlagsdatenAggregator.countWahlvorschlaegeZulassenErlaubt();
    this.anzahlWahlvorschlaegeZugelassen = coreWahlvorschlagsdatenAggregator.countWahlvorschlaegeZugelassen();
    this.anzahlWahlvorschlaegeNichtZugelassen = coreWahlvorschlagsdatenAggregator.countWahlvorschlaegeNichtZugelassen();

    Collection<Liste> listenInGebiet = collectListen(wahlvorschlaegeInGebiet);
    Collection<Liste> listenInGebietshierarchieUebergeordnet = collectListen(wahlvorschlaegeInGebietshierarchieUebergeordnet);

    this.listenInGebiet = createWahlvorschlagDocuments(coreElectContext, gebiet, wahlvorschlagCoreDaten, listenInGebiet);
    this.listenInGebietshierarchieUebergeordnet = createWahlvorschlagDocuments(coreElectContext, gebiet, wahlvorschlagCoreDaten, listenInGebietshierarchieUebergeordnet);

    this.listenverbindungenInGebiet = createListenverbindungDocuments(coreElectContext, gebiet, wahlvorschlagCoreDaten, listenInGebiet);
    this.listenverbindungenInGebietshierarchieUebergeordnet = createListenverbindungDocuments(coreElectContext, gebiet, wahlvorschlagCoreDaten, listenInGebietshierarchieUebergeordnet);

    this.listenverbindungen = this.listenverbindungenInGebiet;
    this.listenverbindungenVorhanden = !this.listenverbindungen.isEmpty();

    this.anzahlListenInsgesamt = coreWahlvorschlagsdatenAggregator.countListen();
    this.anzahlListenZulassenErlaubt = coreWahlvorschlagsdatenAggregator.countListenZulassenErlaubt();
    this.anzahlListenZugelassen = coreWahlvorschlagsdatenAggregator.countListenZugelassen();
    this.anzahlListenNichtZugelassen = coreWahlvorschlagsdatenAggregator.countListenNichtZugelassen();

    this.anzahlListenkandidaturenInsgesamt = coreWahlvorschlagsdatenAggregator.countListenkandidaturen();
    this.anzahlListenkandidaturenInsgesamtAufListenZulassenErlaubt = coreWahlvorschlagsdatenAggregator.countListenkandidaturenAufListenZulassenErlaubt();
    this.anzahlListenkandidaturenInsgesamtAufListenZugelassen = coreWahlvorschlagsdatenAggregator.countListenkandidaturenAufListenZugelassen();
    this.anzahlListenkandidaturenInsgesamtAufListenNichtZugelassen = coreWahlvorschlagsdatenAggregator.countListenkandidaturenAufListenNichtZugelassen();

    this.anzahlListenkandidaturenZulassenErlaubt = coreWahlvorschlagsdatenAggregator.countListenkandidaturenZulassenErlaubt();
    this.anzahlListenkandidaturenZulassenErlaubtAufListenZulassenErlaubt = coreWahlvorschlagsdatenAggregator.countListenkandidaturenZulassenErlaubtAufListenZulassenErlaubt();
    this.anzahlListenkandidaturenZulassenErlaubtAufListenZugelassen = coreWahlvorschlagsdatenAggregator.countListenkandidaturenZulassenErlaubtAufListenZugelassen();
    this.anzahlListenkandidaturenZulassenErlaubtAufListenNichtZugelassen = coreWahlvorschlagsdatenAggregator.countListenkandidaturenZulassenErlaubtAufListenNichtZugelassen();

    this.anzahlListenkandidaturenZugelassen = coreWahlvorschlagsdatenAggregator.countListenkandidaturenZugelassen();
    this.anzahlListenkandidaturenZugelassenAufListenZulassenErlaubt = coreWahlvorschlagsdatenAggregator.countListenkandidaturenZugelassenAufListenZulassenErlaubt();
    this.anzahlListenkandidaturenZugelassenAufListenZugelassen = coreWahlvorschlagsdatenAggregator.countListenkandidaturenZugelassenAufListenZugelassen();
    this.anzahlListenkandidaturenZugelassenAufListenNichtZugelassen = coreWahlvorschlagsdatenAggregator.countListenkandidaturenZugelassenAufListenNichtZugelassen();

    this.anzahlListenkandidaturenNichtZugelassen = coreWahlvorschlagsdatenAggregator.countListenkandidaturenNichtZugelassen();
    this.anzahlListenkandidaturenNichtZugelassenAufListenZulassenErlaubt = coreWahlvorschlagsdatenAggregator.countListenkandidaturenNichtZugelassenAufListenZulassenErlaubt();
    this.anzahlListenkandidaturenNichtZugelassenAufListenZugelassen = coreWahlvorschlagsdatenAggregator.countListenkandidaturenNichtZugelassenAufListenZugelassen();
    this.anzahlListenkandidaturenNichtZugelassenAufListenNichtZugelassen = coreWahlvorschlagsdatenAggregator.countListenkandidaturenNichtZugelassenAufListenNichtZugelassen();

    Collection<Direktkandidat> direktkandidatenInGebiet = collectDirektkandidaten(wahlvorschlaegeInGebiet);
    Collection<Direktkandidat> direktkandidatenInGebietshierarchieUebergeordnet = collectDirektkandidaten(wahlvorschlaegeInGebietshierarchieUebergeordnet);

    this.direktkandidatenInGebiet = createWahlvorschlagDocuments(coreElectContext, gebiet, wahlvorschlagCoreDaten, direktkandidatenInGebiet);
    this.direktkandidatenInGebietshierarchieUebergeordnet = createWahlvorschlagDocuments(coreElectContext, gebiet, wahlvorschlagCoreDaten, direktkandidatenInGebietshierarchieUebergeordnet);

    this.anzahlDirektkandidatenInsgesamt = coreWahlvorschlagsdatenAggregator.countDirektkandidaten();
    this.anzahlDirektkandidatenZulassenErlaubt = coreWahlvorschlagsdatenAggregator.countDirektkandidatenZulassenErlaubt();
    this.anzahlDirektkandidatenZugelassen = coreWahlvorschlagsdatenAggregator.countDirektkandidatenZugelassen();
    this.anzahlDirektkandidatenNichtZugelassen = coreWahlvorschlagsdatenAggregator.countDirektkandidatenNichtZugelassen();

    Collection<ReferendumOption> referendumOptionenInGebiet = collectReferendumOptionen(wahlvorschlaegeInGebiet);
    Collection<ReferendumOption> referendumOptionenInGebietshierarchieUebergeordnet = collectReferendumOptionen(wahlvorschlaegeInGebietshierarchieUebergeordnet);

    this.referendumOptionenInGebiet = createWahlvorschlagDocuments(coreElectContext, gebiet, wahlvorschlagCoreDaten, referendumOptionenInGebiet);
    this.referendumOptionenInGebietshierarchieUebergeordnet = createWahlvorschlagDocuments(coreElectContext, gebiet, wahlvorschlagCoreDaten, referendumOptionenInGebietshierarchieUebergeordnet);

    this.anzahlReferendumOptionenInsgesamt = coreWahlvorschlagsdatenAggregator.countReferendumOptionen();
    this.anzahlReferendumOptionenZulassenErlaubt = coreWahlvorschlagsdatenAggregator.countReferendumOptionenZulassenErlaubt();
    this.anzahlReferendumOptionenZugelassen = coreWahlvorschlagsdatenAggregator.countReferendumOptionenZugelassen();
    this.anzahlReferendumOptionenNichtZugelassen = coreWahlvorschlagsdatenAggregator.countReferendumOptionenNichtZugelassen();
  }

  private List<WahlvorschlagsdatenDocument> createWahlvorschlagDocuments(CoreElectContext coreElectContext, Gebiet gebiet, WahlvorschlagCoreDaten wahlvorschlagCoreDaten,
      Collection< ? extends Wahlvorschlag> wahlvorschlaege) {
    return createWahlvorschlagDocuments(coreElectContext, gebiet, wahlvorschlagCoreDaten, wahlvorschlaege,
        new WahlvorschlagByStimmzettelPositionComparator(gebiet, wahlvorschlagCoreDaten.getEntityAufStimmzettelDaten())); // ELECT-3441
  }

  private List<WahlvorschlagsdatenDocument> createWahlvorschlagDocuments(CoreElectContext coreElectContext, Gebiet gebiet, WahlvorschlagCoreDaten wahlvorschlagCoreDaten,
      Collection< ? extends Wahlvorschlag> wahlvorschlaege, Comparator<Wahlvorschlag> comparator) {
    List<WahlvorschlagsdatenDocument> result = new ArrayList<>();

    List<Wahlvorschlag> wahlvorschlaegeSorted = new ArrayList<>(wahlvorschlaege);
    wahlvorschlaegeSorted.sort(new WahlvorschlagByRelativePositionComparator()); // ELECT-11444, Standardsortierung...
    wahlvorschlaegeSorted.sort(comparator);

    Long laufendeNummer = 0L;

    for (Wahlvorschlag wahlvorschlag : wahlvorschlaegeSorted) {
      laufendeNummer++;

      result.add(WahlvorschlagsdatenDocument.lookupOrCreate(coreElectContext, gebiet, wahlvorschlagCoreDaten, wahlvorschlag, laufendeNummer));
    }

    return result;
  }

  private List<ListenverbindungDokument> createListenverbindungDocuments(CoreElectContext coreElectContext, Gebiet gebiet, WahlvorschlagCoreDaten wahlvorschlagCoreDaten, Collection<Liste> listen) {
    Map<Liste, WahlvorschlagsdatenOhnePruefungenDocument> wahlvorschlagsdatenDocumentPerListe = new HashMap<>();

    Long laufendeNummer = 0L;

    for (Liste liste : listen) {
      laufendeNummer++;

      wahlvorschlagsdatenDocumentPerListe.put(liste, new WahlvorschlagsdatenOhnePruefungenDocument(coreElectContext, gebiet, wahlvorschlagCoreDaten, liste, laufendeNummer));
    }

    return bestimmeListenverbindungen(wahlvorschlagsdatenDocumentPerListe);
  }

  public static List<ListenverbindungDokument> bestimmeListenverbindungen(Map<Liste, WahlvorschlagsdatenOhnePruefungenDocument> wahlvorschlagsdatenDocumentPerListe) {
    List<ListenverbindungDokument> result = new ArrayList<>();

    List<Liste> listen = new ArrayList<>(wahlvorschlagsdatenDocumentPerListe.keySet());

    Set<Listenverbindung> listenverbindungen = new HashSet<>();
    Set<Liste> listenNurListenverbindungEingereicht = new HashSet<>();

    for (Liste liste : listen) {
      Listenverbindung listenverbindung = liste.getListenverbindung();

      if (listenverbindung == null) {
        if (isNotBlank(liste.getListenverbindungEingereicht())) {
          listenNurListenverbindungEingereicht.add(liste);
        }
      } else {
        listenverbindungen.add(listenverbindung);
      }
    }

    List<Listenverbindung> listenverbindungenSorted = new ArrayList<>(listenverbindungen);
    Collections.sort(listenverbindungenSorted, new ListenverbindungNachPositionListenComparator());

    List<Liste> listenNurListenverbindungEingereichtSorted = new ArrayList<>(listenNurListenverbindungEingereicht);
    Collections.sort(listenNurListenverbindungEingereichtSorted, new ListeByGebietUndRelativePositionComparator());

    Long laufendeNummer = 0L;

    for (Listenverbindung listenverbindung : listenverbindungenSorted) {
      List<WahlvorschlagsdatenOhnePruefungenDocument> listeDocuments = new ArrayList<>();

      for (Liste liste : listenverbindung.getListenSortiertNachPosition()) {
        WahlvorschlagsdatenOhnePruefungenDocument wahlvorschlagsdatenDocument = wahlvorschlagsdatenDocumentPerListe.get(liste);

        if (wahlvorschlagsdatenDocument != null) {
          listeDocuments.add(wahlvorschlagsdatenDocument);
        }
      }

      laufendeNummer++;

      result.add(new ListenverbindungDokument(listenverbindung, listeDocuments, laufendeNummer));
    }

    for (Liste liste : listenNurListenverbindungEingereichtSorted) {
      WahlvorschlagsdatenOhnePruefungenDocument wahlvorschlagsdatenDocument = wahlvorschlagsdatenDocumentPerListe.get(liste);

      if (wahlvorschlagsdatenDocument != null) {
        laufendeNummer++;

        result.add(new ListenverbindungDokument(wahlvorschlagsdatenDocument, laufendeNummer));
      }
    }

    return result;
  }

  private Collection<Liste> collectListen(Collection<Wahlvorschlag> wahlvorschlaege) {
    return wahlvorschlaege.stream().filter(wahlvorschlag -> wahlvorschlag.isInstanceOf(Liste.class)).map(wahlvorschlag -> castTo(wahlvorschlag, Liste.class)).collect(toSet());
  }

  private Collection<Direktkandidat> collectDirektkandidaten(Collection<Wahlvorschlag> wahlvorschlaege) {
    return wahlvorschlaege.stream().filter(wahlvorschlag -> wahlvorschlag.isInstanceOf(Direktkandidat.class)).map(wahlvorschlag -> castTo(wahlvorschlag, Direktkandidat.class)).collect(toSet());
  }

  private Collection<ReferendumOption> collectReferendumOptionen(Collection<Wahlvorschlag> wahlvorschlaege) {
    return wahlvorschlaege.stream().filter(wahlvorschlag -> wahlvorschlag.isInstanceOf(ReferendumOption.class)).map(wahlvorschlag -> castTo(wahlvorschlag, ReferendumOption.class)).collect(toSet());
  }
}
