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

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;

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;

/**
 * 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> getGsbs() {
      return EnumSet.of(NlGebietsart.PROVINCIE);
    }

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

    @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 isPublicKeySupported() {
      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> getGsbs() {
      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> getGsbs() {
      return EnumSet.of(NlGebietsart.GEMEENTE, NlGebietsart.GEMEENTE_FRYSK /* ELECTVAPP-1203 */);
    }

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

    @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> getGsbs() {
      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"; // elect: "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 getGsb() {
  //      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> getGsbs() {
      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> getGsbs() {
      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> getGsbs() {
      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> getGsbs() {
      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> getGsbs() {
      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> getGsbs() {
      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> getGsbs() {
      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> getGsbs() {
      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 isPublicKeySupported() {
      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)
   */

  /**
   * Centraal StemBureau
   */
  public static final String KUERZEL_WAHLEBENE_CSB = NlElectionLevel.CSB.name(); // Wahlgebiet - Sitzberechnung

  /**
   * HoofdStemBureau
   */
  public static final String KUERZEL_WAHLEBENE_HSB = NlElectionLevel.HSB.name(); // Aggregationsebene

  /**
   * Gemeentelijk StemBureau
   */
  public static final String KUERZEL_WAHLEBENE_GSB = NlElectionLevel.PSB.name(); // 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> getGsbs();

  @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 isPublicKeySupported() {
    return true; // legacy
  }

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

  public boolean isPS_1_2() {
    return false;
  }

  public static boolean isUrnenwahlUnterstuetzt(Gebiet gebietImGebietsbaum) {
    return gebietImGebietsbaum != null && isUrnenwahlUnterstuetzt(gebietImGebietsbaum.getWahl());
  }

  public static boolean isUrnenwahlUnterstuetzt(Wahl wahl) {
    return !isNurBriefwahlAuslandUnterstuetzt(wahl);
  }

  public static boolean isBriefwahlGemeindeUnterstuetzt(Gebiet gebietImGebietsbaum) {
    if (gebietImGebietsbaum != null) {
      boolean briefwahlGemeindeUnterstuetzt = valueOf(gebietImGebietsbaum.getWahl()).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 (isNurBriefwahlAuslandUnterstuetzt(wahl)) {
        return true; // ELECT-16655
      }

      boolean briefwahlAuslandUnterstuetzt = valueOf(wahl).isBriefwahlAuslandUnterstuetzt();

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

    return false;
  }

  // ELECT-16655
  public static boolean isNurBriefwahlAuslandUnterstuetzt(Wahl wahl) {
    if (NlWahlart.isNBSB(wahl) || NlWahlart.isNBSBC(wahl)) {
      return valueOf(wahl.getWahlart()).isBriefwahlAuslandUnterstuetzt();
    }

    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);
  }

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

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

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

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

  /**
   * HoofdStemBureau met nationaal Briefstembureau
   */
  public static boolean isWasHSBB(Wahl wahl, Customer customer) {
    return isWasHSB(wahl, customer) && isHSBB(wahl);
  }

  /**
   * HoofdStemBureau met nationaal Briefstembureau
   */
  public static boolean isHSBB(Wahl wahl) {
    return wahl != null && wahl.getCustomData(NlWahlartCustomData.class).isHsbbSystem();
  }

  /**
   * HoofdStemBureau voor Correcties
   */
  public static boolean isWasHSBC(Wahl wahl, Customer customer) {
    return isWasHSB(wahl, customer) && isHSBC(wahl);
  }

  /**
   * HoofdStemBureau voor Correcties
   */
  public static boolean isHSBC(Wahl wahl) {
    return wahl != null && wahl.getCustomData(NlWahlartCustomData.class).isHsbcSystem();
  }

  /**
   * Gemeentelijk StemBureau
   */
  public static boolean isWasGSB(Wahl wahl, Customer customer) {
    return wahl != null && isWasGSB(wahl.getWahlart(), customer);
  }

  /**
   * Gemeentelijk StemBureau
   */
  public static boolean isWasGSB(Wahlart wahlart, Customer customer) {
    return isWas(customer) && equalsWahlebene(wahlart, KUERZEL_WAHLEBENE_GSB);
  }

  /**
   * Nationaal BriefStemBureau
   */
  public static boolean isWasNBSB(Wahl wahl, Customer customer) {
    return isWasGSB(wahl, customer) && isNBSB(wahl);
  }

  /**
   * Nationaal BriefStemBureau
   */
  public static boolean isNBSB(Wahl wahl) {
    return wahl != null && wahl.getCustomData(NlWahlartCustomData.class).isNbsbSystem();
  }

  /**
   * Nationaal BriefStemBureau voor Correcties
   */
  public static boolean isWasNBSBC(Wahl wahl, Customer customer) {
    return isWasGSB(wahl, customer) && isNBSBC(wahl);
  }

  /**
   * Nationaal BriefStemBureau voor Correcties
   */
  public static boolean isNBSBC(Wahl wahl) {
    return wahl != null && wahl.getCustomData(NlWahlartCustomData.class).isNbsbcSystem();
  }

  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...
  public boolean isWahlartMitMehrerenWahlebenen() {
    return true;
  }

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

  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.isGSB() == isWasGSB(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
  }
}
