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

import java.util.Collection;
import java.util.Date;
import java.util.EnumSet;
import java.util.Set;
import java.util.function.LongSupplier;
import java.util.function.Supplier;

import javax.annotation.Nullable;

import de.ivu.elect.business.gebietsbaum.boundary.AbstractGebietDAO;
import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
import de.ivu.elect.business.gebietsbaum.entity.Gebietsarteigenschaft;
import de.ivu.elect.business.gebietsbaum.entity.NlGebiet;
import de.ivu.elect.business.gebietsbaum.entity.NlGebietsart;
import de.ivu.elect.business.security.Customer;
import de.ivu.elect.business.wahl.boundary.WahlartTemplate;
import de.ivu.elect.presentation.NlElectionLevel;


import static de.ivu.elect.business.wahl.entity.WahlartSitzberechnungsalgorithmus.KEIN;
import static java.util.Arrays.stream;
import static java.util.stream.Collectors.toSet;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import static org.apache.commons.lang3.StringUtils.startsWithAny;

/**
 * Beachte: https://support.elect-it.com/wiki/display/ELECT/Wahlarten+in+VAPP
 */
public enum NlWahlart implements WahlartTemplate {

  EERSTE_KAMER {
    @Override
    public String getWahlartName() {
      return "EK";
    }

    @Override
    public Set<NlGebietsart> getCsbs() {
      return EnumSet.of(NlGebietsart.STAAT);
    }

    @Override
    public Set<NlGebietsart> getHsbs() {
      return EnumSet.of(NlGebietsart.PROVINCIE);
    }

    @Override
    public Set<NlGebietsart> getPsbs() {
      return EnumSet.of(NlGebietsart.PROVINCIE);
    }

    @Override
    public String getEml110ContestIdentifier() {
      return "alle"; // ELECTVAPP-1834
    }

    @Override
    protected int getNominierungAnzahlDerTageVorWahltag() {
      return 35; // ELECTVAPP-2250 (vorher ELECT-9642)
    }

    @Override
    public long getPreferencialBarrierNumerator(Long anzahlSitze) {
      return 100;
    }

    @Override
    public boolean isPersonenregisterAbgleichUnterstuetzt() {
      return true;
    }

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

    @Override
    public boolean isSupportedInMasterElectionTree() {
      return true; // ELECT-13323
    }

    @Override
    public boolean isUseShortCodeForCandidateIdIn510dUnd520() {
      return true; // ELECT-13754
    }

    @Override
    public boolean isContestidentifierAlleIn5xxCsb() {
      return true; // ELECT-13754 aber dann ELECT-14316 und doch wieder....
    }

    @Override
    public String getEml510dNameMessageKey() {
      return "eml510d.reporting.unit.name.ek";
    }

    @Override
    public boolean isWahlartMitMehrerenWahlebenen() {
      return true; // ELECT-14316 wieder zurück und doch wieder hin, weil alle Testfälle scheitern
    }

    @Override
    public boolean isGruppenAendernErlaubt() {
      return true; // ELECT-14548
    }

    @Override
    public boolean isPublicKeySopported() {
      return false; // ELECTVAPP-1940
    }
  },

  TWEEDE_KAMER {
    @Override
    public String getWahlartName() {
      return "TK";
    }

    @Override
    public Set<NlGebietsart> getCsbs() {
      return EnumSet.of(NlGebietsart.STAAT);
    }

    @Override
    public Set<NlGebietsart> getHsbs() {
      return EnumSet.of(NlGebietsart.KIESKRING);
    }

    @Override
    public Set<NlGebietsart> getPsbs() {
      return EnumSet.of(NlGebietsart.GEMEENTE, NlGebietsart.GEMEENTE_FRYSK /* ELECTVAPP-1203 */);
    }

    @Override
    public String getEml110ContestIdentifier() {
      return "alle"; // ELECTVAPP-1834
    }

    @Override
    public boolean isSupportedInMasterElectionTree() {
      return true;
    }

    @Override
    public long getPreferencialBarrierNumerator(Long anzahlSitze) {
      return 25;
    }

    @Override
    public boolean isBriefwahlAuslandUnterstuetzt() {
      return true;
    }

    @Override
    public boolean isPersonenregisterAbgleichUnterstuetzt() {
      return true;
    }

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

    @Override
    public boolean isUseShortCodeForCandidateIdIn510dUnd520() {
      return true; // ELECTVAPP-1336
    }

    @Override
    public boolean isContestidentifierAlleIn5xxCsb() {
      return true; // ELECTVAPP-1335
    }

    @Override
    public boolean isGruppenAendernErlaubt() {
      return true; // ELECT-14548
    }

  },

  EUROPEES_PARLEMENT {
    @Override
    public String getWahlartName() {
      return "EP";
    }

    @Override
    public Set<NlGebietsart> getCsbs() {
      return EnumSet.of(NlGebietsart.STAAT);
    }

    @Override
    public Set<NlGebietsart> getHsbs() {
      return EnumSet.of(NlGebietsart.KIESKRING);
    }

    @Override
    public Set<NlGebietsart> getPsbs() {
      return EnumSet.of(NlGebietsart.GEMEENTE, NlGebietsart.GEMEENTE_FRYSK /* ELECTVAPP-1203 */);
    }

    @Override
    public String getEml110ContestIdentifier() {
      return "alle"; // ELECTVAPP-1834
    }

    @Override
    protected int getNominierungAnzahlDerTageVorWahltag() {
      return 45; // ELECT-9642
    }

    @Override
    public long getPreferencialBarrierNumerator(Long anzahlSitze) {
      return 10;
    }

    @Override
    public boolean isBriefwahlAuslandUnterstuetzt() {
      return true;
    }

    @Override
    public boolean isPersonenregisterAbgleichUnterstuetzt() {
      return true;
    }

    @Override
    public boolean isWahlartMitWahlkreisen() {
      return false; // ELECTVAPP-1493
    }

    @Override
    public boolean isSupportedInMasterElectionTree() {
      return true; // ELECT-13323
    }

    @Override
    public boolean isWahlkreiseBeiImportPruefen() {
      return false; // ELECT-13428
    }

    @Override
    public boolean isContestIdentifierMitWahlgebietFuellen() {
      return false; // ELECT-13534
    }

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

    @Override
    public boolean isContestidentifierAlleIn5xxCsb() {
      return true; // ELECT-13754
    }

    // ELECT-13807
    //    @Override
    //    public String getEml510bIdMessageKey() {
    //      return "eml510b.reporting.unit.id.ep"; // ELECT-13760
    //    }

    // ELECT-13807
    //    @Override
    //    public boolean isEml510bNameAusgaben() {
    //      return false; // ELECT-13760
    //    }
  },

  GEMEENTERAAD {
    @Override
    public String getWahlartName() {
      return "GR";
    }

    @Override
    public boolean isWahlartMitWahlkreisen() {
      return false; // ELECTVAPP-1493
    }

    @Override
    public Set<NlGebietsart> getCsbs() {
      return EnumSet.of(NlGebietsart.GEMEENTE, NlGebietsart.GEMEENTE_FRYSK/* ELECTVAPP-1203 */);
    }

    @Override
    public Set<NlGebietsart> getHsbs() {
      return EnumSet.of(NlGebietsart.GEMEENTE, NlGebietsart.GEMEENTE_FRYSK/* ELECTVAPP-1203 */);
    }

    @Override
    public Set<NlGebietsart> getPsbs() {
      return EnumSet.of(NlGebietsart.GEMEENTE, NlGebietsart.GEMEENTE_FRYSK/* ELECTVAPP-1203 */);
    }

    @Override
    public String getEml110ContestIdentifier() {
      return "geen"; // ELECTVAPP-1834
    }

    @Override
    public boolean isSupportedInMasterElectionTree() {
      return true;
    }

    @Override
    public long getPreferencialBarrierNumerator(Long anzahlSitze) {
      if (anzahlSitze != null && anzahlSitze < 19) {
        return 50;
      }
      return 25;
    }

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

    @Override
    public boolean isOberstesGebietAnWahlKennzeichenHaengen() {
      return true; // ELECT-13574 ELECT-9991
    }

    @Override
    public boolean isElectionDomainIdInEmlAusgaben() {
      return true; // war in Altdaten so
    }

    @Override
    public boolean isEml230CentraalStembuero() {
      return true;
    }

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

    @Override
    public String getEml510dIdMessageKey() {
      return "eml510d.reporting.unit.id.gr"; // ELECT-13723
    }

    @Override
    public String getEml510dNameMessageKey() {
      return "eml510d.reporting.unit.name.gr"; // ELECT-13723
    }

    @Override
    public boolean isEml510dReportingUnitsAusgeben() {
      return false; // ELECTVAPP-1704
    }

    @Override
    public boolean isEml510GebietAnhaengen() {
      return false; // ELECTVAPP-1728
    }

  },
  // ELECT-9645 deaktiviert
  //  KIESCOLLEG {
  //    @Override
  //    public String getWahlartName() {
  //      return "KC"; //  "KC" kann nicht stimmen muss ein Wert aus de.ivu.wahl.wus.oasis.eml.kiesraad.ElectionCategoryType sein
  //    }
  //
  //    @Override
  //    public NlGebietsart getCsb() {
  //      return NlGebietsart.UNBEKANNT;
  //    }
  //
  //    @Override
  //    public NlGebietsart getHsb() {
  //      return NlGebietsart.UNBEKANNT;
  //    }
  //
  //    @Override
  //    public NlGebietsart getPsb() {
  //      return NlGebietsart.UNBEKANNT;
  //    }
  //
  //  },

  EILANDSRAAD {
    @Override
    public String getWahlartName() {
      return "ER";
    }

    @Override
    public Set<NlGebietsart> getCsbs() {
      return EnumSet.of(NlGebietsart.OPENBAAR_LICHAAM);
    }

    @Override
    public Set<NlGebietsart> getHsbs() {
      return EnumSet.of(NlGebietsart.OPENBAAR_LICHAAM);
    }

    @Override
    public Set<NlGebietsart> getPsbs() {
      return EnumSet.of(NlGebietsart.OPENBAAR_LICHAAM);
    }

    @Override
    public String getEml110ContestIdentifier() {
      return "geen"; // ELECTVAPP-1834
    }

    @Override
    public boolean isEml230CentraalStembuero() {
      return true;
    }

    @Override
    public long getPreferencialBarrierNumerator(Long anzahlSitze) {
      return GEMEENTERAAD.getPreferencialBarrierNumerator(anzahlSitze);
    }

    @Override
    public boolean isWahlartMitWahlkreisen() {
      return false; // ELECTVAPP-1493
    }

    @Override
    public boolean isSupportedInMasterElectionTree() {
      return true; // ELECT-13323
    }

    @Override
    public String getHelpPath() {
      return GEMEENTERAAD.getHelpPath(); // selbe Hilfe
    }

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

    @Override
    public boolean isElectionDomainIdInEmlAusgaben() {
      return true; // war in Altdaten so
    }

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

    @Override
    public String getEml510dIdMessageKey() {
      return "eml510d.reporting.unit.id.gr"; // ELECT-13723
    }

    @Override
    public String getEml510dNameMessageKey() {
      return "eml510d.reporting.unit.name.gr"; // ELECT-13723
    }

    @Override
    public boolean isEml510dReportingUnitsAusgeben() {
      return false; // ELECTVAPP-1704
    }

    @Override
    public boolean isEml510GebietAnhaengen() {
      return false; // ELECTVAPP-1728
    }

  },

  // Nur für die Definition einer PROVINCIALE_STATEN WAHL in WUS notwendig - eine konkrete Wahl im WAS ist dann PS1 oder PS2
  PROVINCIALE_STATEN {
    @Override
    public boolean isWahlartFuerDurchfuehrbareWahl() {
      return false; // MUSS PS1 oder PS2 sein(!)
    }

    @Override
    public long getPreferencialBarrierNumerator(Long anzahlSitze) {
      return 25;
    }

    @Override
    public String getWahlartName() {
      return "PS";
    }

    @Override
    public boolean equalsWahlart(String wahlartName) {
      return "PS_HSB_CSB_DEF_LST".equalsIgnoreCase(wahlartName); // nur bei exaktem Treffer (nicht startsWith)
    }

    @Override
    public Set<NlGebietsart> getCsbs() {
      return EnumSet.of(NlGebietsart.UNBEKANNT); // erst bei PS1/PS2 klar
    }

    @Override
    public Set<NlGebietsart> getHsbs() {
      return EnumSet.of(NlGebietsart.UNBEKANNT);
    }

    @Override
    public boolean isEml230CentraalStembuero() {
      return true;
    }

    @Override
    public Set<NlGebietsart> getPsbs() {
      return EnumSet.of(NlGebietsart.UNBEKANNT);
    }

    @Override
    public String getEml110ContestIdentifier() {
      return "alle"; // ELECT-13568, ELECTVAPP-1834
    }

    @Override
    public boolean isSupportedInMasterElectionTree() {
      return true; // ELECT-13323
    }

    @Override
    public boolean isOberstesGebietAnWahlKennzeichenHaengen() {
      return true; // ELECT-13571
    }

    @Override
    public boolean isPS_1_2() {
      return true;
    }
  },
  PROVINCIALE_STATEN1/* EIN WAHLKREIS */ {
    @Override
    public String getWahlartName() {
      return "PS_PS1";
    }

    @Override
    public String getKennzeichen() {
      return "PS";
    }

    @Override
    public Set<NlGebietsart> getCsbs() {
      return EnumSet.of(NlGebietsart.PROVINCIE);
    }

    @Override
    public Set<NlGebietsart> getHsbs() {
      return EnumSet.of(NlGebietsart.PROVINCIE);
    }

    @Override
    public Set<NlGebietsart> getPsbs() {
      return EnumSet.of(NlGebietsart.GEMEENTE, NlGebietsart.GEMEENTE_FRYSK /* ELECTVAPP-1203 */);
    }

    @Override
    public String getEml110ContestIdentifier() {
      return "geen"; // ELECT-13568 aber ELECTVAPP-1834
    }

    @Override
    public long getPreferencialBarrierNumerator(Long anzahlSitze) {
      return PROVINCIALE_STATEN.getPreferencialBarrierNumerator(anzahlSitze);
    }

    @Override
    public boolean isWahlartMitWahlkreisen() {
      return false; // ELECTVAPP-1493
    }

    @Override
    public String getElectionCategoryType() {
      return PROVINCIALE_STATEN.getElectionCategoryType();
    }

    @Override
    public String getHelpPath() {
      return PROVINCIALE_STATEN.getHelpPath();
    }

    @Override
    public boolean isOberstesGebietAnWahlKennzeichenHaengen() {
      return PROVINCIALE_STATEN.isOberstesGebietAnWahlKennzeichenHaengen();
    }

    @Override
    @Nullable
    public String getEml510dIdMessageKey() {
      return null; // ELECT-13644
    }

    @Override
    @Nullable
    public String getEml510dNameMessageKey() {
      return null; // ELECT-13644
    }

    @Override
    public boolean isElectionDomainImmerInEmlAusgeben() {
      return true; // ELECT-13753
    }

    @Override
    public boolean isEml230CentraalStembuero() {
      return PROVINCIALE_STATEN.isEml230CentraalStembuero();
    }

    @Override
    public boolean isPS_1_2() {
      return true;
    }

  },

  PROVINCIALE_STATEN2/* MEHRERE WAHLKREISE */ {
    @Override
    public String getWahlartName() {
      return "PS_PS2";
    }

    @Override
    public String getKennzeichen() {
      return "PS";
    }

    @Override
    public Set<NlGebietsart> getCsbs() {
      return EnumSet.of(NlGebietsart.PROVINCIE);
    }

    @Override
    public Set<NlGebietsart> getHsbs() {
      return EnumSet.of(NlGebietsart.KIESKRING);
    }

    @Override
    public Set<NlGebietsart> getPsbs() {
      return EnumSet.of(NlGebietsart.GEMEENTE, NlGebietsart.GEMEENTE_FRYSK /* ELECTVAPP-1203 */);
    }

    @Override
    public long getPreferencialBarrierNumerator(Long anzahlSitze) {
      return PROVINCIALE_STATEN.getPreferencialBarrierNumerator(anzahlSitze);
    }

    @Override
    public String getEml110ContestIdentifier() {
      return PROVINCIALE_STATEN.getEml110ContestIdentifier();
    }

    @Override
    public String getElectionCategoryType() {
      return PROVINCIALE_STATEN.getElectionCategoryType();
    }

    @Override
    public String getHelpPath() {
      return PROVINCIALE_STATEN.getHelpPath();
    }

    @Override
    public boolean isEml230CentraalStembuero() {
      return PROVINCIALE_STATEN.isEml230CentraalStembuero();
    }

    @Override
    public boolean isOberstesGebietAnWahlKennzeichenHaengen() {
      return PROVINCIALE_STATEN.isOberstesGebietAnWahlKennzeichenHaengen();
    }

    @Override
    public boolean isUseShortCodeForCandidateIdIn510dUnd520() {
      return true; // ELECT-13754
    }

    @Override
    public boolean isContestidentifierAlleIn5xxCsb() {
      return true; // ELECT-13754
    }

    @Override
    public boolean isElectionDomainImmerInEmlAusgeben() {
      return true; // ELECT-13753
    }

    @Override
    public boolean isGruppenAendernErlaubt() {
      return true; // ELECT-14548
    }

    @Override
    public boolean isPS_1_2() {
      return true;
    }

  },

  REFERENDUM/* National */ {
    @Override
    public String getWahlartName() {
      return "NR";
    }

    @Override
    public String getKennzeichen() {
      return "NR"; // aus EML(?)
    }

    @Override
    public CommonWahlart getCommonWahlart() {
      return CommonWahlart.VOLKSENTSCHEID;
    }

    @Override
    protected boolean isListenwahl() {
      return false;
    }

    @Override
    public Set<NlGebietsart> getCsbs() {
      return EnumSet.of(NlGebietsart.STAAT);
    }

    @Override
    public Set<NlGebietsart> getHsbs() {
      return EnumSet.of(NlGebietsart.KIESKRING);
    }

    @Override
    public Set<NlGebietsart> getPsbs() {
      return EnumSet.of(NlGebietsart.GEMEENTE, NlGebietsart.GEMEENTE_FRYSK/* ELECTVAPP-1203 */);
    }

    @Override
    public String getEml110ContestIdentifier() {
      return "geen"; // ???
    }

    @Override
    public long getPreferencialBarrierNumerator(Long anzahlSitze) {
      return 0;
    }

    @Override
    protected boolean isReferendum() {
      return true;
    }

    @Override
    public boolean isBriefwahlAuslandUnterstuetzt() {
      return true;
    }

    @Override
    public String getHelpPath() {
      return UNBEKANNT.getHelpPath(); // unklar, welche Hilfe...
    }

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

  },

  WATERSCHAP {
    @Override
    public String getWahlartName() {
      return "AB";
    }

    @Override
    public Set<NlGebietsart> getCsbs() {
      return EnumSet.of(NlGebietsart.WATERSCHAP);
    }

    @Override
    public Set<NlGebietsart> getHsbs() {
      return EnumSet.of(NlGebietsart.WATERSCHAP);
    }

    @Override
    public Set<NlGebietsart> getPsbs() {
      return EnumSet.of(NlGebietsart.GEMEENTE, NlGebietsart.GEMEENTE_FRYSK/* ELECTVAPP-1203 */);
    }

    @Override
    public boolean isEml230CentraalStembuero() {
      return true;
    }

    @Override
    public String getEml110ContestIdentifier() {
      return "geen"; // ELECT-13512 und ELECTVAPP-1834
    }

    @Override
    public long getPreferencialBarrierNumerator(Long anzahlSitze) {
      return 25;
    }

    @Override
    public boolean isWahlartMitWahlkreisen() {
      return false; // ELECTVAPP-1493
    }

    @Override
    public boolean isSupportedInMasterElectionTree() {
      return true; // ELECT-13323
    }

    //    @Override
    //    public String getHelpPath() {
    //      return GEMEENTERAAD.getHelpPath(); // ELECT-13478
    //    }

    @Override
    public boolean isOberstesGebietAnWahlKennzeichenHaengen() {
      return true; // ELECT-13571
    }

    @Override
    public boolean isElectionDomainIdInEmlAusgaben() {
      return true; // ELECT-13597
    }

    @Override
    @Nullable
    public String getEml510dIdMessageKey() {
      return null; // ELECT-13644
    }

    @Override
    @Nullable
    public String getEml510dNameMessageKey() {
      return null; // ELECT-13644
    }

    @Override
    public boolean isElectionDomainImmerInEmlAusgeben() {
      return true; // ELECT-13753
    }

  },
  BESTUURSCOMMISSIE { // ELECT-

    @Override
    public String getWahlartName() {
      return "BC";
    }

    @Override
    public boolean isWahlartMitWahlkreisen() {
      return false; // ELECTVAPP-1493
    }

    @Override
    public Set<NlGebietsart> getCsbs() {
      return EnumSet.of(NlGebietsart.DEELGEMEENTE);
    }

    @Override
    public Set<NlGebietsart> getHsbs() {
      return EnumSet.of(NlGebietsart.DEELGEMEENTE);
    }

    @Override
    public Set<NlGebietsart> getPsbs() {
      return EnumSet.of(NlGebietsart.DEELGEMEENTE);
    }

    @Override
    public String getEml110ContestIdentifier() {
      return "geen"; // ELECTVAPP-1834
    }

    @Override
    public boolean isSupportedInMasterElectionTree() {
      return true;
    }

    @Override
    public long getPreferencialBarrierNumerator(Long anzahlSitze) {
      return 50;
    }

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

    @Override
    public boolean isOberstesGebietAnWahlKennzeichenHaengen() {
      return true; // ELECT-13574 ELECT-9991
    }

    @Override
    public boolean isElectionDomainIdInEmlAusgaben() {
      return true; // war in Altdaten so
    }

    @Override
    public boolean isEml230CentraalStembuero() {
      return true;
    }

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

    @Override
    public String getEml510dIdMessageKey() {
      return "eml510d.reporting.unit.id.gr"; // ELECT-13723
    }

    @Override
    public String getEml510dNameMessageKey() {
      return "eml510d.reporting.unit.name.gr"; // ELECT-13723
    }

    @Override
    public boolean isEml510dReportingUnitsAusgeben() {
      return false; // ELECTVAPP-1704
    }

    @Override
    public boolean isEml510GebietAnhaengen() {
      return false; // ELECTVAPP-1728
    }

  },

  UNBEKANNT /* ELECT-9627 */ {
    @Override
    public String getWahlartName() {
      return "<UNBEKANNT>";
    }

    @Override
    public boolean isWahlartMitWahlkreisen() {
      return false; // ELECTVAPP-1493
    }

    @Override
    protected boolean isReferendum() {
      return false;
    }

    @Override
    protected boolean isListenwahl() {
      return false;
    }

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

    @Override
    public Set<NlGebietsart> getCsbs() {
      return EnumSet.of(NlGebietsart.UNBEKANNT);
    }

    @Override
    public Set<NlGebietsart> getHsbs() {
      return EnumSet.of(NlGebietsart.UNBEKANNT);
    }

    @Override
    public Set<NlGebietsart> getPsbs() {
      return EnumSet.of(NlGebietsart.UNBEKANNT);
    }

    @Override
    public String getEml110ContestIdentifier() {
      return "";
    }

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

    @Override
    public long getPreferencialBarrierNumerator(Long anzahlSitze) {
      return 0;
    }

    @Override
    public String getElectionCategoryType() {
      return "";
    }

    @Override
    public String getHelpPath() {
      return GEMEENTERAAD.getHelpPath(); // Fallback
    }

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

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

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

  /**
   * Weitere Wahlarten aus http://bitbucket.local.elect-it.com/projects/RETIRED/repos/osv/browse/de.ivu.wahl.wus.electioncategory/src/main/java/de/ivu/wahl/wus/electioncategory/ElectionCategory.java
   * NATIONAL_REFERENDUM {
   *
   * @Override public String getWahlartTitel() { return "National referendum"; }
   *
   * @Override public CommonWahlart getCommonWahlart() { return CommonWahlart.BEGEHREN; }
   *
   * @Override public WahlartSitzberechnungsalgorithmus getSitzberechnungsalgorithmus() { return WahlartSitzberechnungsalgorithmus.KEIN; }
   *
   * @Override public String getKennzeichen() { return "NR"; }
   *
   * @Override public boolean equalsWahlart(Wahlart wahlart) { return wahlart != null && wahlart.getWahlartName().contains("National Referendum"); } },
   *
   *           PROVINCIAL_REFERENDUM {
   *
   * @Override public String getWahlartTitel() { return "Provincial referendum"; }
   *
   * @Override public CommonWahlart getCommonWahlart() { return CommonWahlart.BEGEHREN; }
   *
   * @Override public WahlartSitzberechnungsalgorithmus getSitzberechnungsalgorithmus() { return WahlartSitzberechnungsalgorithmus.KEIN; }
   *
   * @Override public String getKennzeichen() { return "PR"; }
   *
   * @Override public boolean equalsWahlart(Wahlart wahlart) { return wahlart != null && wahlart.getWahlartName().contains("Referendum"); } },
   *
   *           LOCAL_REFERENDUM {
   *
   * @Override public String getWahlartTitel() { return "Local referendum"; }
   *
   * @Override public CommonWahlart getCommonWahlart() { return CommonWahlart.BEGEHREN; }
   *
   * @Override public WahlartSitzberechnungsalgorithmus getSitzberechnungsalgorithmus() { return WahlartSitzberechnungsalgorithmus.KEIN; }
   *
   * @Override public String getKennzeichen() { return "LR"; }
   *
   * @Override public boolean equalsWahlart(Wahlart wahlart) { return wahlart != null && wahlart.getWahlartName().contains("Local Referendum"); } },
   *
   *           ISLANDS_REFERENDUM {
   * @Override public String getWahlartTitel() { return "Islands referendum"; }
   *
   * @Override public CommonWahlart getCommonWahlart() { return CommonWahlart.BEGEHREN; }
   *
   * @Override public WahlartSitzberechnungsalgorithmus getSitzberechnungsalgorithmus() { return WahlartSitzberechnungsalgorithmus.KEIN; }
   *
   * @Override public String getKennzeichen() { return "IR"; }
   *
   * @Override public boolean equalsWahlart(Wahlart wahlart) { return wahlart != null && wahlart.getWahlartName().contains("Referendum"); }
   *
   *           };
   */
  /**
   * Bestuurscommissie - Amsterdam BC(0x41), Gebiedscommissie - Rotterdam GC(0x42), Waterschapsverkiezing AB(0x43)
   */

  public static final String KUERZEL_WAHLEBENE_CSB = "CSB"; // Wahlgebiet - Sitzberechnung

  public static final String KUERZEL_WAHLEBENE_HSB = "HSB"; // Aggregationsebene

  public static final String KUERZEL_WAHLEBENE_PSB = "PSB"; // Erfassungsebene

  NlWahlart() {
  }

  @Override
  public CommonWahlart getCommonWahlart() {
    return CommonWahlart.UNBEKANNT;
  }

  @Override
  public String getKennzeichen() {
    return getWahlartName();
  }

  @Override
  public WahlartSitzberechnungsalgorithmus getSitzberechnungsalgorithmus() {
    return KEIN;
  }

  @Override
  public boolean equalsWahlart(Wahlart wahlart) { // "Eerste Kamer" == "Eerste Kamer (Kiesraad)"!
    return WahlartTemplate.super.equalsWahlart(wahlart) || wahlart != null && equalsWahlart(wahlart.getWahlartName());
  }

  public boolean equalsWahlart(String wahlartName) {
    if (isNotBlank(wahlartName)) {
      String another = CommonWahlart.normalizeWahlartName(wahlartName);

      return startsWithAny(another, CommonWahlart.normalizeWahlartName(getWahlartName()), CommonWahlart.normalizeWahlartName(name()));
    }

    return false;
  }

  public boolean isBekannteWahlart() {
    return true;
  }

  public boolean isBriefwahlGemeindeUnterstuetzt() {
    return false; // ELECTVAPP-1152, ELECTVAPP-1592 -> false!!!
  }

  public boolean isBriefwahlAuslandUnterstuetzt() {
    return false;
  }

  protected boolean isReferendum() {
    return false;
  }

  protected boolean isListenwahl() {
    return true;
  }

  @Deprecated // NICHT MEHR VERWENDEN, INFORMATIONEN KOMMEN AUS DEM MASTERELECTION TREE UND SIND HIER WAHRSCHEINLICH VERALTET
  public abstract Set<NlGebietsart> getCsbs();

  @Deprecated // NICHT MEHR VERWENDEN, INFORMATIONEN KOMMEN AUS DEM MASTERELECTION TREE UND SIND HIER WAHRSCHEINLICH VERALTET
  public abstract Set<NlGebietsart> getHsbs();

  @Deprecated // NICHT MEHR VERWENDEN, INFORMATIONEN KOMMEN AUS DEM MASTERELECTION TREE UND SIND HIER WAHRSCHEINLICH VERALTET
  public abstract Set<NlGebietsart> getPsbs();

  @Nullable
  public abstract String getEml110ContestIdentifier();

  public boolean isPersonenregisterAbgleichUnterstuetzt() {
    return false;
  }

  public String getHelpPath() {
    return getWahlartName();
  }

  public boolean isContestIdentifierMitWahlgebietFuellen() {
    return true;
  }

  public boolean isElectionDomainIdInEmlAusgaben() {
    return false;
  }

  public abstract boolean isEml230CentraalStembuero();

  public boolean isEml510dReportingUnitsAusgeben() {
    return true; // ELECTVAPP-1704
  }

  public boolean isEml510GebietAnhaengen() {
    return true; // ELECTVAPP-1728
  }

  public boolean isPublicKeySopported() {
    return true; // legacy
  }

  public boolean isStimmabgabeVorzeitigUnterstuetzt() {
    return false; // ELECTVAPP-1958
  }

  public boolean isPS_1_2() {
    return false;
  }

  public static boolean isBriefwahlGemeindeUnterstuetzt(Gebiet gebietImGebietsbaum) {
    if (gebietImGebietsbaum != null) {
      Wahl wahl = gebietImGebietsbaum.getWahl();

      if (wahl != null) {
        boolean briefwahlGemeindeUnterstuetzt = valueOf(wahl.getWahlart()).isBriefwahlGemeindeUnterstuetzt();

        return gebietImGebietsbaum.getGebietshierarchie().stream() //
            .filter(Gebiet::isPersistent) // ELECT-13318
            .map(NlGebiet::new) //
            .anyMatch(gebietInGebietshierarchie -> gebietInGebietshierarchie.isBriefwahlgebietGemeinde()
                || briefwahlGemeindeUnterstuetzt && gebietInGebietshierarchie.isBriefwahlGemeindeInGebietshierarchieUnterhalbZulaessig());
      }
    }

    return false;
  }

  public static boolean isBriefwahlAuslandUnterstuetzt(Gebiet gebietImGebietsbaum) {

    if (gebietImGebietsbaum != null) {
      Wahl wahl = gebietImGebietsbaum.getWahl();

      if (wahl != null) {
        boolean briefwahlAuslandUnterstuetzt = valueOf(wahl.getWahlart()).isBriefwahlAuslandUnterstuetzt();

        return gebietImGebietsbaum.getGebietshierarchie().stream() //
            .filter(Gebiet::isPersistent) // ELECT-13318
            .map(NlGebiet::new) //
            .anyMatch(gebietInGebietshierarchie -> gebietInGebietshierarchie.isBriefwahlgebietAusland()
                || briefwahlAuslandUnterstuetzt && gebietInGebietshierarchie.isBriefwahlAuslandInGebietshierarchieUnterhalbZulaessig());
      }
    }
    return false;
  }

  public static boolean isListenwahl(Wahl wahl) {
    return wahl != null && isListenwahl(wahl.getWahlart());
  }

  public static boolean isListenwahl(Wahlart wahlart) {
    return wahlart != null && valueOf(wahlart).isListenwahl();
  }

  public static Collection<NlWahlart> valuesListenwahl() {
    return stream(NlWahlart.values()).filter(NlWahlart::isListenwahl).collect(toSet());
  }

  public static boolean isReferendum(Wahl wahl) {
    return wahl != null && valueOf(wahl.getWahlart()).isReferendum();
  }

  public static Collection<NlWahlart> valuesReferendum() {
    return stream(NlWahlart.values()).filter(NlWahlart::isReferendum).collect(toSet());
  }

  public static NlWahlart valueOf(Wahl wahl) {
    if (wahl == null) {
      return UNBEKANNT;
    }

    return valueOf(wahl.getWahlart());
  }

  public static NlWahlart valueOf(Wahlart wahlart) {
    return stream(values()).filter(value -> value.equalsWahlart(wahlart)).findFirst().orElse(UNBEKANNT);
  }

  public static boolean isWasCSB(Wahl wahl, Customer customer) {
    return wahl != null && isWasCSB(wahl.getWahlart(), customer);
  }

  public static boolean isWasCSB(Wahlart wahlart, Customer customer) {
    return isWas(customer) && equalsWahlebene(wahlart, KUERZEL_WAHLEBENE_CSB);
  }

  public static boolean isWasHSB(Wahl wahl, Customer customer) {
    if (wahl == null) {
      return false;
    }

    return isWasHSB(wahl.getWahlart(), customer);
  }

  public static boolean isWasHSB(Wahlart wahlart, Customer customer) {
    return isWas(customer) && equalsWahlebene(wahlart, KUERZEL_WAHLEBENE_HSB);
  }

  /**
   * ELECT-15285 und ELECT-15712
   */
  public static boolean isWasHSBC(Wahl wahl, Customer customer) {
    return isWasHSB(wahl, customer) && wahl.getCustomData(NlHsbcCustomData.class).isHsbcSystem();
  }

  public static boolean isWasPSB(Wahl wahl, Customer customer) {
    return wahl != null && isWasPSB(wahl.getWahlart(), customer);
  }

  public static boolean isWasPSB(Wahlart wahlart, Customer customer) {
    return isWas(customer) && equalsWahlebene(wahlart, KUERZEL_WAHLEBENE_PSB);
  }

  public static boolean isWasNBSB(Wahl wahl, Customer customer) {
    return wahl != null && isWasNBSB(wahl.getWahlart(), customer);
  }

  public static boolean isWasNBSB(Wahlart wahlart, Customer customer) {
    return false; // TODO "Nationales Briefwahl Stimmbüro"-System...
  }

  public static boolean equalsWahlebene(Wahl wahl, String nameEbene) {
    return wahl != null && equalsWahlebene(wahl.getWahlart(), nameEbene);
  }

  public static boolean equalsWahlebene(Wahlart wahlart, String nameEbene) {
    if (wahlart == null || isBlank(wahlart.getName())) {
      return false;
    }

    return equalsWahlebene(wahlart.getName(), nameEbene);
  }

  public static boolean equalsWahlebene(String wahlartName, String nameEbene) {
    return isNotBlank(wahlartName) && wahlartName.toUpperCase().contains(nameEbene);
  }

  // vgl PS/PS1/PS2
  public boolean isWahlartFuerDurchfuehrbareWahl() {
    return true;
  }

  // ELECTVAPP-1493
  public boolean isWahlartMitWahlkreisen() {
    return true;
  }

  // ELECT-13360, EILANDSRAAD wie GEMEENTERAAD behandeln...
  // isFehltCsbHsbODerPSBInEinemSystemMitDemDieWahlartDurchgefuehrtWird
  public boolean isWahlartMitMehrerenWahlebenen() {
    return true;
  }

  @Nullable
  public static Date getNominationDate(Wahl wahl) {
    return new NlFristen(wahl).getWusNominierungstag().orElse(null);
  }

  protected int getNominierungAnzahlDerTageVorWahltag() {
    return 44; // ELECT-9642
  }

  public static boolean isWasGemeinde(Supplier<Gebiet> wurzelgebiet, Customer customer) {
    // nur für Gemeinden in VAPP-U (WAS) zulässig
    // https://support.elect-it.com/wiki/display/ELECT/Wahlarten+in+VAPP
    return isWas(customer) && NlGebietsart.from(wurzelgebiet.get()).isGemeinde();
  }

  public static boolean isWas(Customer customer) {
    return Customer.NL_WAS.equals(customer);
  }

  public static boolean isWus(Customer customer) {
    return Customer.NL_WUS.equals(customer) /* && isEbene(wahlart, "DEF") */;
  }

  public boolean isSupportedInMasterElectionTree() {
    return false; // einzeln freischalten, muss in Wahldefinition (nl-interfaces-met) unterstützt werden
  }

  public static boolean isZurAuswahlInWus(Wahlart wahlart) {
    return NlWahlart.valueOf(wahlart).isSupportedInMasterElectionTree() && isDefinitionIgnoreCustomer(wahlart);
  }

  public static boolean isDefinitionIgnoreCustomer(Wahlart wahlart) {
    return equalsWahlebene(wahlart, "DEF");
  }

  public static boolean matches(Wahlart wahlart, NlElectionLevel electionLevel, Customer customer) {
    return electionLevel.isCSB() == isWasCSB(wahlart, customer) && electionLevel.isHSB() == isWasHSB(wahlart, customer) && electionLevel.isPSB() == isWasPSB(
        wahlart, customer);
  }

  public abstract long getPreferencialBarrierNumerator(Long anzahlSitze);

  public static NlWahlart bestimmeWahlartProvincialeStaten(Wahl wahl, long anzahlWahlkreise) {
    return bestimmeWahlartProvincialeStaten(wahl, () -> anzahlWahlkreise);
  }

  public static NlWahlart bestimmeWahlartProvincialeStaten(Wahl wahl, Collection<? extends Gebiet> gebiete) {
    return bestimmeWahlartProvincialeStaten(wahl, () -> gebiete.stream().filter(gebiet -> NlGebietsart.from(gebiet).isKieskring()).count());
  }

  public static NlWahlart bestimmeWahlartProvincialeStaten(Wahl wahl, AbstractGebietDAO gebietDAO) {
    return bestimmeWahlartProvincialeStaten(wahl, () -> wahl.getWahlart().getGebietsarteigenschaften().stream() //
        .map(Gebietsarteigenschaft::getGebietsart) //
        .filter(gebietsart -> NlGebietsart.from(gebietsart).isKieskring()) //
        .findFirst() //
        .map(gebietsartKieskring -> gebietDAO.findByCountGebieteByWahlAndGebietsart(wahl, gebietsartKieskring)) //
        .orElse(0L));
  }

  private static NlWahlart bestimmeWahlartProvincialeStaten(Wahl wahl, LongSupplier kieskringCountSupplier) {
    if (PROVINCIALE_STATEN1.equalsWahlart(wahl)) {
      return PROVINCIALE_STATEN1;
    }

    if (PROVINCIALE_STATEN2.equalsWahlart(wahl)) {
      return PROVINCIALE_STATEN2;
    }

    if (PROVINCIALE_STATEN.equalsWahlart(wahl)) { // ELECT-13424
      long kieskringCount = kieskringCountSupplier.getAsLong();

      if (kieskringCount > 1) {
        return PROVINCIALE_STATEN2;
      }

      return PROVINCIALE_STATEN1;
    }

    return UNBEKANNT;
  }

  public String getElectionCategoryType() {
    return getKennzeichen();
  }

  public boolean isWahlkreiseBeiImportPruefen() {
    return true; // ELECT-13428 und verknüpfte
  }

  public boolean isOberstesGebietAnWahlKennzeichenHaengen() {
    return false;
  }

  @Nullable
  public String getEml510dIdMessageKey() {
    return "eml510d.reporting.unit.id";
  }

  @Nullable
  public String getEml510dNameMessageKey() {
    return "eml510d.reporting.unit.name";
  }

  @Nullable
  public String getEml510bIdMessageKey() {
    return "eml510b.reporting.unit.id.stimmbuero"; // wie bisher
  }

  public boolean isEml510bNameAusgaben() {
    return true; // wie bisher
  }

  public boolean isUseShortCodeForCandidateIdIn510dUnd520() {
    return false;
  }

  public boolean isContestidentifierAlleIn5xxCsb() {
    return false;
  }

  public boolean isElectionDomainImmerInEmlAusgeben() {
    return false;
  }

  public boolean isGruppenAendernErlaubt() {
    return false; // ELECT-14548
  }
}
