
package de.ivu.elect.wus.exchange.wahlvorschlag;

import java.io.Serializable;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.BiConsumer;
import java.util.function.Function;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.inject.Inject;
import javax.xml.datatype.XMLGregorianCalendar;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import de.ivu.elect.business.CurrentCustomer;
import de.ivu.elect.business.gebietsbaum.boundary.GebietFactory;
import de.ivu.elect.business.gebietsbaum.boundary.NlGebietDarstellungNummerFormatter;
import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
import de.ivu.elect.business.gruppe.boundary.PolitischeGruppeFactory;
import de.ivu.elect.business.gruppe.entity.NlPolitischeGruppe;
import de.ivu.elect.business.gruppe.entity.PolitischeGruppe;
import de.ivu.elect.business.gruppeimgebiet.boundary.GruppeImGebietFactory;
import de.ivu.elect.business.gruppeimgebiet.entity.GruppeImGebiet;
import de.ivu.elect.business.kandidatur.boundary.ErsatzkandidaturFactory;
import de.ivu.elect.business.kandidatur.boundary.ListenkandidaturFactory;
import de.ivu.elect.business.kandidatur.entity.KandidaturStatus;
import de.ivu.elect.business.kandidatur.entity.Listenkandidatur;
import de.ivu.elect.business.kandidatur.entity.NlListenkandidatur;
import de.ivu.elect.business.person.entity.Geschlecht;
import de.ivu.elect.business.person.entity.NlPersonendaten;
import de.ivu.elect.business.person.entity.PersonStatus;
import de.ivu.elect.business.person.entity.PersonenVerknuepfung;
import de.ivu.elect.business.person.entity.VerknuepfungsArt;
import de.ivu.elect.business.person.entity.VerknuepfungsGrundlage;
import de.ivu.elect.business.person.entity.VerknuepfungsStatus;
import de.ivu.elect.business.wahl.boundary.WahlFactory;
import de.ivu.elect.business.wahl.entity.Wahl;
import de.ivu.elect.business.wahlvorschlag.boundary.ListeFactory;
import de.ivu.elect.business.wahlvorschlag.entity.Liste;
import de.ivu.elect.business.wahlvorschlag.entity.NlListe;
import de.ivu.elect.business.wahlvorschlag.entity.Wahlvorschlag;
import de.ivu.elect.nl.eml.readable.AbstractEml230PersonWrapper;
import de.ivu.elect.nl.eml.readable.AffiliationWrapper;
import de.ivu.elect.nl.eml.readable.Eml230CandidateWrapper;
import de.ivu.elect.nl.eml.readable.Eml230ContestWrapper;
import de.ivu.elect.nl.eml.readable.Eml230Wrapper;
import de.ivu.elect.nl.eml.readable.ListSubmissionMode;
import de.ivu.elect.presentation.gebietsbaum.Roman;
import de.ivu.wahl.wus.oasis.eml.GenderType;
import de.ivu.wahl.wus.oasis.eml.kiesraad.ElectionDomain;

public class Eml230Mapper implements Serializable {

  private static final Logger LOG = LoggerFactory.getLogger(Eml230Mapper.class);

  private static final long serialVersionUID = 1L;

  public static final String KEIN_GEBIET_ID = "geen";

  private static final String ALLE_GEBIETE = "alle";

  @Inject
  private transient WahlFactory wahlFactory;

  @Inject
  private transient GebietFactory gebietFactory;

  @Inject
  private transient PolitischeGruppeFactory politischeGruppeFactory;

  @Inject
  private transient ListeFactory listeFactory;

  @Inject
  private transient GruppeImGebietFactory gruppeImGebietFactory;

  @Inject
  private transient ListenkandidaturFactory listenkandidaturFactory;

  @Inject
  private transient ErsatzkandidaturFactory ersatzkandidaturFactory;

  @Inject
  private transient NlGebietDarstellungNummerFormatter nlGebietDarstellungNummerFormatter;

  @Inject
  private transient CurrentCustomer currentCustomer;

  public List<Wahlvorschlag> mappe(Eml230Wrapper eml230Wrapper, List<Gebiet> listenwahlgebieteInDb) {
    List<Wahlvorschlag> result = new ArrayList<>();

    var wahl = wahlFactory.create();
    wahl.setKennzeichen(eml230Wrapper.getElectionId());

    var oberstersGebiet = mappeOberstesGebiet(eml230Wrapper.getOrCreateElectionDomain(), wahl);

    LOG.info("oberstersGebiet = " + oberstersGebiet);

    Map<String, Map<String, List<Listenkandidatur>>> listenkandidaturenNachGruppenschluesselUndShortCode = new HashMap<>();

    Map<String, PolitischeGruppe> politscheGruppenNachName = new HashMap<>();
    Map<String, PolitischeGruppe> politischeGruppenNachId = new HashMap<>();

    Map<PolitischeGruppe, Map<Integer, List<Liste>>> listenNachPolitischerGruppeUndStelNummer = new HashMap<>();

    // ''Contest'' entspricht Wahlgebiet - wahrscheinlich
    for (var contestWrapper : eml230Wrapper.getContest()) {
      List<Gebiet> wahlgebiete = new ArrayList<>();
      if (KEIN_GEBIET_ID.equalsIgnoreCase(contestWrapper.getId()) && listenwahlgebieteInDb.size() == 1) {
        // ELECTVAPP-2117
        konvertiereListenwahlgebiete(listenwahlgebieteInDb, wahlgebiete);
      } else if (oberstersGebiet != null && KEIN_GEBIET_ID.equalsIgnoreCase(contestWrapper.getId())) {
        wahlgebiete.add(oberstersGebiet); // Annahme...
      } else if (!listenwahlgebieteInDb.isEmpty() && ALLE_GEBIETE.equalsIgnoreCase(contestWrapper.getId())) {
        // ELECT-10067
        konvertiereListenwahlgebiete(listenwahlgebieteInDb, wahlgebiete);
      } else {
        wahlgebiete.add(mappeGebiet(wahl, contestWrapper));
      }

      for (var wahlgebiet : wahlgebiete) {

        LOG.info("Gebiet-Nummer: " + wahlgebiet.getNummer() + ", Gebiet-Name: " + wahlgebiet.getName());

        long aktuelleListenposition = 0;
        // Affiliation entspricht Wahlvorschlag (Liste) - wahrscheinlich

        for (var affiliationWrapper : contestWrapper.getAffiliation()) {

          aktuelleListenposition = bestimmeNaechstePosition(aktuelleListenposition, affiliationWrapper.getId() /* nehmen wir das mal als Listenposition */);
          var politischeGruppe = bestimmePolitischeGruppe(affiliationWrapper, politscheGruppenNachName, politischeGruppenNachId, wahl, aktuelleListenposition);

          var liste = listeFactory.create();
          result.add(liste);
          liste.setGruppeImGebiet(createGruppeImGebiet(politischeGruppe, wahlgebiet, aktuelleListenposition));
          NlListe.setName(liste, politischeGruppe.getName()); // mehr haben wir ja nicht

          var belongsToSet = affiliationWrapper.getBelongsToSet();
          if (belongsToSet == null) {
            if (ListSubmissionMode.SET_OF_LISTS.equals(affiliationWrapper.getListSubMissionMode())) {
              belongsToSet = BigInteger.ZERO; // identische Listen sind 0
            }
          }
          if (belongsToSet != null) {
            NlListe.setStelNummer(liste, belongsToSet.intValue(), currentCustomer.getCustomer());
            listenNachPolitischerGruppeUndStelNummer.computeIfAbsent(liste.getGruppe(), x -> new HashMap<>())
                .computeIfAbsent(belongsToSet.intValue(), x -> new ArrayList<>()).add(liste);
          }

          mappeEinreichungsart(affiliationWrapper, liste);

          NlListe.setPublishGender(liste, affiliationWrapper.isPublishGender());

          if (affiliationWrapper.getPublicationLanguageType() != null) {
            NlListe.setPublicationLanguage(liste, affiliationWrapper.getPublicationLanguageType().value());
          }

          var listenkandidaturenDerGruppeNachShortCode = listenkandidaturenNachGruppenschluesselUndShortCode.computeIfAbsent(politischeGruppe.getName(),
              x -> new HashMap<>());

          long aktuelleListenkandidaturposition = 0;

          for (var candidateWrapper : affiliationWrapper.getCandidate()) {
            var listenkandidatur = listenkandidaturFactory.create();

            var nlListenkandidatur = new NlListenkandidatur(listenkandidatur);

            nlListenkandidatur.setListe(liste);
            nlListenkandidatur.setKandidaturStatus(KandidaturStatus.ZUGELASSEN); // im EML sind nur zugelassene Kandidaten

            nlListenkandidatur.setPersonStatus(PersonStatus.GEEIGNET); // im EML nur geeignete Personen

            aktuelleListenkandidaturposition = bestimmeNaechstePosition(aktuelleListenkandidaturposition,
                candidateWrapper.getId()/* nehmen wir das mal als Kandidaturposition */);
            nlListenkandidatur.setListenposition(aktuelleListenkandidaturposition);

            var shortCode = candidateWrapper.getShortCode();
            nlListenkandidatur.setShortCode(shortCode); // ELECT-9732

            if (StringUtils.isNotBlank(shortCode)) {
              listenkandidaturenDerGruppeNachShortCode.computeIfAbsent(shortCode, x -> new ArrayList<>()).add(listenkandidatur);
            }

            // !!!!!! ANFANG - BEI ERGÄNZUNGEN aktualisiere() BEACHTEN !!!!!!
            personendatenUebernehmen(candidateWrapper, nlListenkandidatur);

            // ELECT-10294 (war deaktiviert, aber ELECTVAPP-784 exclusive...)
            if (candidateWrapper.getDateOfBirth() != null) {
              nlListenkandidatur.setGeburtsdatum(mappeDatum(candidateWrapper.getDateOfBirth()));
            }
            if (StringUtils.isNotBlank(candidateWrapper.getDateOfBirthAnnex())) {
              nlListenkandidatur.setGeburtsdatumErfasst(candidateWrapper.getDateOfBirthAnnex());
            }
            nlListenkandidatur.setGeschlecht(mappeGeschlecht(candidateWrapper.getGender()));

            nlListenkandidatur.getWohnanschrift().setStaat(candidateWrapper.getCountryNameCode()); // ELECT-9796

            // !!!!!! ENDE - BEI ERGÄNZUNGEN aktualisiere() BEACHTEN !!!!!!

            // ELECT-10235 Bevollmächtigte
            var agent = candidateWrapper.getAgent();

            if (agent != null) {
              var bevollmaechtigter = ersatzkandidaturFactory.create();
              var nlBevollmaechtigter = new NlPersonendaten(bevollmaechtigter);

              personendatenUebernehmen(agent, nlBevollmaechtigter);

              nlListenkandidatur.setBevollmaechtigter(bevollmaechtigter);
            }
          }
        }
      }
    }

    // In den Altdaten sind Stels mit nur einer Liste enthalten => bereinigen
    for (var listenNachStelNummer : listenNachPolitischerGruppeUndStelNummer.values()) {
      for (var listenEinesStels : listenNachStelNummer.values()) {
        if (listenEinesStels.size() == 1) {
          NlListe.setStelNummer(listenEinesStels.get(0), null, currentCustomer.getCustomer());
        }
      }
    }

    korrigiereEinreichungsart(listenNachPolitischerGruppeUndStelNummer);

    for (var listenkandidaturenMachShortCode : listenkandidaturenNachGruppenschluesselUndShortCode.values()) {
      for (var listenkandidaturen : listenkandidaturenMachShortCode.values()) {
        uebernehmePersonenDaten(listenkandidaturen);
        erstellePersonenverknuepfungen(listenkandidaturen);
      }
    }

    return result;
  }

  private void konvertiereListenwahlgebiete(List<Gebiet> listenwahlgebieteInDb, List<Gebiet> wahlgebiete) {
    for (var wahlgebietInDb : listenwahlgebieteInDb) {
      var wahlgebiet = gebietFactory.create();
      wahlgebiet.setNummer(wahlgebietInDb.getNummer());
      wahlgebiet.setName(wahlgebietInDb.getName());
      wahlgebiete.add(wahlgebiet);
    }
  }

  @Nonnull
  public Gebiet mappeGebiet(Wahl wahl, Eml230ContestWrapper contestWrapper) {
    var wahlgebiet = gebietFactory.create();
    wahlgebiet.setWahl(wahl);
    mappeOberstesGebiet(contestWrapper, wahlgebiet);
    return wahlgebiet;
  }

  protected void korrigiereEinreichungsart(Map<PolitischeGruppe, Map<Integer, List<Liste>>> listenNachPolitischerGruppeUndStelNummer) {
    // nichts zu tun
  }

  protected void mappeEinreichungsart(AffiliationWrapper affiliationWrapper, Liste liste) {
    // nichts zu tun
  }

  private void personendatenUebernehmen(AbstractEml230PersonWrapper<?> from, NlPersonendaten to) {
    // !!!!!! BEI ERGÄNZUNGEN aktualisiere() BEACHTEN !!!!!!
    to.setInitialen(from.getInitials()); // ELECT-9564
    to.setVorname(from.getFirstName()); // ELECT-9564
    to.setFamilienname(from.getLastName());
    to.setFamiliennameBestandteil(from.getNamePrefix());

    to.getWohnanschrift().setOrt(from.getLocalityName()); // ELECT-9796

    // optionale Elemente (nur bei 230c)
    var erreichbarkeitsanschrift = to.getErreichbarkeitsanschrift();

    erreichbarkeitsanschrift.setStrasse(from.getMailingAddressLine());
    erreichbarkeitsanschrift.setOrt(from.getMailingLocalityName());
    erreichbarkeitsanschrift.setPlz(from.getMailingPostalCodeNumber());
    erreichbarkeitsanschrift.setStaat(from.getMailingCountryNameCode());
  }

  private PolitischeGruppe bestimmePolitischeGruppe(AffiliationWrapper affiliationWrapper, Map<String, PolitischeGruppe> politscheGruppenNachName,
                                                    Map<String, PolitischeGruppe> politischeGruppenNachId, Wahl wahl, long aktuelleListenposition) {
    var politischeGruppe = politischeGruppeFactory.create();

    var nlPolitischeGruppe = new NlPolitischeGruppe(politischeGruppe);
    nlPolitischeGruppe.setWahl(wahl);
    nlPolitischeGruppe.setGruppeArt(NlPolitischeGruppe.GRUPPE_ART_DEFAULT);

    // wichtig am Ende für Eml520-Export und die Reihenfolge der Gruppen, die in P22 benutzt wird, siehe ELECT-11089
    nlPolitischeGruppe.setRelativePosition(aktuelleListenposition);
    nlPolitischeGruppe.setVorgegebenePosition(aktuelleListenposition);

    var registeredName = affiliationWrapper.getRegisteredName();

    // ELECT-9776
    if (StringUtils.isBlank(registeredName)) {
      // ELECT-10046
      if (StringUtils.isNotBlank(affiliationWrapper.getId()) && politischeGruppenNachId.containsKey(affiliationWrapper.getId())) {
        return politischeGruppenNachId.get(affiliationWrapper.getId());
      }

      registeredName = formatNameBlancoListe(nlPolitischeGruppe, bestimmeErsterKandidat(affiliationWrapper));

      nlPolitischeGruppe.setBlancoListe(registeredName);
    }

    nlPolitischeGruppe.setName(registeredName);

    if (StringUtils.isNotBlank(affiliationWrapper.getId())) {
      politischeGruppenNachId.putIfAbsent(affiliationWrapper.getId(), politischeGruppe); // ELECT-10046
    }

    // Cache nehmen, wenn bereits vorhanden
    return politscheGruppenNachName.computeIfAbsent(registeredName, x -> politischeGruppe);
  }

  @Nullable
  public Gebiet mappeOberstesGebiet(ElectionDomain electionDomain, Wahl wahl) {
    if (StringUtils.isNotBlank(electionDomain.getId())) {
      var oberstersGebiet = gebietFactory.create();
      oberstersGebiet.setNummer(electionDomain.getId());
      oberstersGebiet.setName(electionDomain.getValue());
      oberstersGebiet.setWahl(wahl);
      return oberstersGebiet;
    }

    return null;
  }

  private void mappeOberstesGebiet(Eml230ContestWrapper contestWrapper, Gebiet wahlgebiet) {
    wahlgebiet.setNummer(contestWrapper.getId());

    // ELECT-13584 in den Altdaten steht ein großes I für eine 1 und andere römische Nummer
    if (StringUtils.isNotBlank(wahlgebiet.getNummer())) {
      try {
        Long.parseLong(wahlgebiet.getNummer());
      } catch (NumberFormatException nfe) {
        try {
          var romanParsed = Roman.toLong(wahlgebiet.getNummer());
          if (romanParsed != null) {
            wahlgebiet.setNummer(Long.toString(romanParsed));
          }
        } catch (NumberFormatException nfe2) {
          // ignore
        }
      }
    }

    wahlgebiet.setName(contestWrapper.getContestName());
  }

  public void mappeOberstesGebiet(Gebiet oberstesGebiet, Eml230ContestWrapper contestWrapper) {
    contestWrapper.setId(nlGebietDarstellungNummerFormatter.formatiereNummer(oberstesGebiet));
    contestWrapper.setContestName(oberstesGebiet.getName());
  }

  // ELECT-10396
  private String formatNameBlancoListe(NlPolitischeGruppe nlPolitischeGruppe, Eml230CandidateWrapper ersteListenkandidaturRepraesentativExtern) {
    var ersteListenkandidaturRepraesentativIntern = new NlListenkandidatur(listenkandidaturFactory.create());

    if (ersteListenkandidaturRepraesentativExtern != null) {
      personendatenUebernehmen(ersteListenkandidaturRepraesentativExtern, ersteListenkandidaturRepraesentativIntern);
    }

    return nlPolitischeGruppe.formatNameBlancoListe(Optional.of(ersteListenkandidaturRepraesentativIntern));
  }

  @Nullable
  private Eml230CandidateWrapper bestimmeErsterKandidat(AffiliationWrapper affiliationWrapper) {
    var candidate = affiliationWrapper.getCandidate();

    if (candidate.isEmpty()) {
      return null;
    }

    return candidate.get(0);
  }

  private void aktualisiere(Listenkandidatur von, Listenkandidatur nach) {
    //  ELECT-9766
    aktualisiere(von, nach, NlListenkandidatur::getInitialen, NlListenkandidatur::setInitialen);
    aktualisiere(von, nach, NlListenkandidatur::getVorname, NlListenkandidatur::setVorname);
    aktualisiere(von, nach, NlListenkandidatur::getFamilienname, NlListenkandidatur::setFamilienname);
    aktualisiere(von, nach, NlListenkandidatur::getFamiliennameBestandteil, NlListenkandidatur::setFamiliennameBestandteil);
    aktualisiere(von, nach, NlListenkandidatur::getGeburtsdatum, NlListenkandidatur::setGeburtsdatum);
    aktualisiere(von, nach, NlListenkandidatur::getGeburtsdatumErfasst, NlListenkandidatur::setGeburtsdatumErfasst);
    aktualisiere(von, nach, NlListenkandidatur::getGeschlecht, NlListenkandidatur::setGeschlecht);

    aktualisiere(von, nach, l -> l.getWohnanschrift().getOrt(), (l, s) -> l.getWohnanschrift().setOrt(s)); // ELECT-9796
    aktualisiere(von, nach, l -> l.getWohnanschrift().getStaat(), (l, s) -> l.getWohnanschrift().setStaat(s)); // ELECT-9796

    aktualisiere(von, nach, l -> l.getErreichbarkeitsanschrift().getStrasse(), (l, s) -> l.getErreichbarkeitsanschrift().setStrasse(s));
    aktualisiere(von, nach, l -> l.getErreichbarkeitsanschrift().getPlz(), (l, s) -> l.getErreichbarkeitsanschrift().setPlz(s));
    aktualisiere(von, nach, l -> l.getErreichbarkeitsanschrift().getOrt(), (l, s) -> l.getErreichbarkeitsanschrift().setOrt(s));
    aktualisiere(von, nach, l -> l.getErreichbarkeitsanschrift().getStaat(), (l, s) -> l.getErreichbarkeitsanschrift().setStaat(s));

    var vonErsatz = von.getZugeordneteErsatzkandidatur();

    if (vonErsatz != null && nach.getZugeordneteErsatzkandidatur() == null) {
      var nachErsatz = ersatzkandidaturFactory.create();
      nach.setZugeordneteErsatzkandidatur(nachErsatz);

      vonErsatz.copyEntityAttributesUsingSetter(nachErsatz);

      var anschriftVon = vonErsatz.getAnschrift();
      var anschriftNach = nachErsatz.getAnschrift();
      anschriftVon.copyEntityAttributesUsingSetter(anschriftNach);
    }
  }

  private <T> void aktualisiere(Listenkandidatur von, Listenkandidatur nach, Function<NlListenkandidatur, T> getter, BiConsumer<NlListenkandidatur, T> setter) {
    var nlVon = new NlListenkandidatur(von);
    var nlNach = new NlListenkandidatur(nach);

    var objectVon = getter.apply(nlVon);
    var objectNach = getter.apply(nlNach);

    if (isNotBlank(objectVon) && isBlank(objectNach)) {
      setter.accept(nlNach, objectVon);
    }
  }

  private <T> boolean isNotBlank(T o) {
    return !isBlank(o);
  }

  private <T> boolean isBlank(T o) {
    if (o == null) {
      return true;
    }
    if (StringUtils.isBlank(o.toString())) {
      return true;
    }
    if (o instanceof Geschlecht) {
      return Geschlecht.UNBEKANNT.equals(o);
    }

    return false;
  }

  private void uebernehmePersonenDaten(List<Listenkandidatur> listenkandidaturen) {
    // Daten in EML unvollständig ELECT-9766
    for (var listenkandidaturOne : listenkandidaturen) {
      for (var listenkandidaturAnother : listenkandidaturen) {
        aktualisiere(listenkandidaturOne, listenkandidaturAnother);
      }
    }

  }

  private void erstellePersonenverknuepfungen(List<Listenkandidatur> listenkandidaturen) {
    if (listenkandidaturen.size() > 1) {
      var listenkandidatur1 = listenkandidaturen.get(0);

      for (var listenkandidaturOther : listenkandidaturen) {
        if (listenkandidatur1 != listenkandidaturOther/* Identität hier zwingend notwendig, alle ohne ID */) {
          var personenVerknuepfung = new PersonenVerknuepfung();
          personenVerknuepfung.setPerson1(listenkandidatur1);
          personenVerknuepfung.setPerson2(listenkandidaturOther);
          personenVerknuepfung.setVerknuepfungsStatus(VerknuepfungsStatus.IDENTISCH);
          personenVerknuepfung.setVerknuepfungsArt(VerknuepfungsArt.WAHLVORSCHLAG_DUBLETTE);
          personenVerknuepfung.setVerknuepfungsGrundlage(VerknuepfungsGrundlage.MASCHINELL);
        }
      }
    }
  }

  private Geschlecht mappeGeschlecht(GenderType gender) {
    if (gender == null) {
      return Geschlecht.UNBEKANNT;
    }

    switch (gender) {
      case MALE:
        return Geschlecht.MAENNLICH;
      case FEMALE:
        return Geschlecht.WEIBLICH;
      case UNKNOWN:
        return Geschlecht.UNBEKANNT;
    }

    return Geschlecht.UNBEKANNT;
  }

  public GenderType mappeGeschlecht(Geschlecht gender) {
    if (gender == null) {
      return GenderType.UNKNOWN;
    }

    switch (gender) {
      case MAENNLICH:
        return GenderType.MALE;
      case WEIBLICH:
        return GenderType.FEMALE;
    }

    return GenderType.UNKNOWN;
  }

  @Nullable
  private Date mappeDatum(XMLGregorianCalendar dateOfBirth) {
    if (dateOfBirth == null) {
      return null;
    }

    return dateOfBirth.toGregorianCalendar().getTime();
  }

  private GruppeImGebiet createGruppeImGebiet(PolitischeGruppe politischeGruppe, Gebiet wahlgebiet, long aktuelleListenposition) {
    var result = gruppeImGebietFactory.create();
    result.setGruppe(politischeGruppe);
    result.setGebiet(wahlgebiet);
    result.setRelativePosition(aktuelleListenposition);
    result.setVorgegebenePosition(aktuelleListenposition);
    return result;
  }

  private long bestimmeNaechstePosition(long aktuellePosition, String id) {
    var idAsLong = parseLong(id);
    if (idAsLong == null) {
      return aktuellePosition + 1;
    }
    return idAsLong;
  }

  @Nullable
  private Long parseLong(String id) {
    if (StringUtils.isBlank(id)) {
      return null;
    }
    try {
      return Long.parseLong(id);
    } catch (Exception e) {
      return null;
    }
  }
}
