
package de.ivu.elect.business.stimmen.boundary;

import static java.util.Objects.requireNonNull;
import static org.apache.commons.lang3.StringUtils.defaultIfBlank;

import java.io.Serializable;
import java.util.Collection;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.inject.Inject;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import de.ivu.elect.business.ergebnis.entity.Ergebnisart;
import de.ivu.elect.business.ergebnis.entity.Stimmenart;
import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
import de.ivu.elect.business.gruppe.entity.Gruppe;
import de.ivu.elect.business.gruppe.entity.SystemGruppe;
import de.ivu.elect.business.gruppeimgebiet.entity.GruppeImGebiet;
import de.ivu.elect.business.kandidatur.entity.WaehlerBenannteKandidaturen;
import de.ivu.elect.business.stimmzettel.entity.AbstractEntityAufStimmzettelStimmzettelPosition;
import de.ivu.elect.business.stimmzettel.entity.EntityAufStimmzettelDaten;
import de.ivu.elect.business.wahl.entity.UmgangGueltigeWerte;
import de.ivu.elect.business.wahl.entity.WahlartErgebnisart;
import de.ivu.elect.business.wahlvorschlag.entity.WahlvorschlagStimmzettelPosition;

public abstract class AbstractStimmeLabelGenerator implements Serializable {

  private static final long serialVersionUID = 1L;

  protected static final Logger LOG = LoggerFactory.getLogger(AbstractStimmeLabelGenerator.class);

  protected static final String MSG_KEY_INFIX_WAHLBERECHTIGTE = SystemGruppe.WAHLBERECHTIGTE.name();

  protected static final String MSG_KEY_INFIX_WAEHLER = SystemGruppe.WAEHLER.name();

  protected static final String MSG_KEY_INFIX_GUELTIGE = SystemGruppe.GUELTIGE.name();

  protected static final String MSG_KEY_INFIX_UNGUELTIGE = SystemGruppe.UNGUELTIGE.name();

  protected static final String MSG_KEY_INFIX_UNBESTIMMTE = "UNBESTIMMTE";

  protected static final String MSG_KEY_INFIX_UEBRIGE = SystemGruppe.UEBRIGE.name();

  protected static final String MSG_KEY_INFIX_ABGEGEBENE = "ABGEGEBENE";

  protected static final String MSG_KEY_INFIX_STIMMZETTEL = "STIMMZETTEL";

  protected static final String MSG_KEY_INFIX_GESAMT = Stimmenart.GESAMT.name();

  @Inject
  private transient StimmeLabelMessages stimmeLabelMessages;

  protected AbstractStimmeLabelGenerator() {
    super();
  }

  protected AbstractStimmeLabelGenerator(StimmeLabelMessages stimmeLabelMessages) {
    super();

    this.stimmeLabelMessages = stimmeLabelMessages;
  }

  protected String getSingularOrDefault(Gebiet gebiet, WahlartErgebnisart ergebnisart, String msgKeyInfix, String defaultValue, Object... params) {
    return stimmeLabelMessages.getSingularOrDefault(gebiet, ergebnisart, msgKeyInfix, defaultValue, params);
  }

  protected String getSingularOrDefault(Gebiet gebiet, String msgKeyInfix, String defaultValue, Object... params) {
    return stimmeLabelMessages.getSingularOrDefault(gebiet, msgKeyInfix, defaultValue, params);
  }

  protected String getPluralOrDefault(Gebiet gebiet, WahlartErgebnisart ergebnisart, String msgKeyInfix, String defaultValue, Object... params) {
    return stimmeLabelMessages.getPluralOrDefault(gebiet, ergebnisart, msgKeyInfix, defaultValue, params);
  }

  protected String getPluralOrDefault(Gebiet gebiet, String msgKeyInfix, String defaultValue, Object... params) {
    return stimmeLabelMessages.getPluralOrDefault(gebiet, msgKeyInfix, defaultValue, params);
  }

  protected String getBezeichnungOrDefault(Gebiet gebiet, WahlartErgebnisart ergebnisart, String msgKeyInfix, String defaultValue, Object... params) {
    return stimmeLabelMessages.getBezeichnungOrDefault(gebiet, ergebnisart, msgKeyInfix, defaultValue, params);
  }

  protected String getBezeichnungOrDefault(Gebiet gebiet, String msgKeyInfix, String defaultValue, Object... params) {
    return stimmeLabelMessages.getBezeichnungOrDefault(gebiet, msgKeyInfix, defaultValue, params);
  }

  protected String getKennungFortlaufendOrDefault(Gebiet gebiet, String msgKeyInfix, String defaultValue, Object... params) {
    return stimmeLabelMessages.getKennungFortlaufendOrDefault(gebiet, msgKeyInfix, defaultValue, params);
  }

  protected String getKennungOrDefault(Gebiet gebiet, WahlartErgebnisart ergebnisart, String msgKeyInfix, String defaultValue, Object... params) {
    return stimmeLabelMessages.getKennungOrDefault(gebiet, ergebnisart, msgKeyInfix, defaultValue, params);
  }

  protected String getKennungOrDefault(Gebiet gebiet, String msgKeyInfix, String defaultValue, Object... params) {
    return stimmeLabelMessages.getKennungOrDefault(gebiet, msgKeyInfix, defaultValue, params);
  }

  protected String formatAnzahlGueltigeStimmenartKennung(GruppeImGebiet gruppeImGebiet, Stimmenart stimmenart) {
    return formatAnzahlGueltigeStimmenartKennung(gruppeImGebiet.getGebiet(), stimmenart);
  }

  protected String formatAnzahlGueltigeStimmenartKennung(Gebiet gebiet, Stimmenart stimmenart) {
    return formatAnzahlGueltigeStimmenartKennung(gebiet, stimmenart, "");
  }

  protected String formatAnzahlGueltigeStimmenartKennung(Gebiet gebiet, Stimmenart stimmenart, String defaulValue) {
    String msgKeyInfix = MSG_KEY_INFIX_GUELTIGE + ".ANZAHL." + stimmenart.name();

    return getKennungOrDefault(gebiet, msgKeyInfix, defaulValue);
  }

  protected String formatStimmenAnzahlGueltigeStimmenartBezeichnung(GruppeImGebiet gruppeImGebiet, Stimmenart stimmenart) {
    return formatAnzahlGueltigeStimmenartBezeichnung(gruppeImGebiet.getGebiet(), stimmenart);
  }

  protected String formatAnzahlGueltigeStimmenartBezeichnung(Gebiet gebiet, Stimmenart stimmenart) {
    return formatAnzahlGueltigeStimmenartBezeichnung(gebiet, stimmenart, "");
  }

  protected String formatAnzahlGueltigeStimmenartBezeichnung(Gebiet gebiet, Stimmenart stimmenart, String defaultValue) {
    String msgKeyInfix = MSG_KEY_INFIX_GUELTIGE + ".ANZAHL." + stimmenart.name();

    return getBezeichnungOrDefault(gebiet, msgKeyInfix, defaultValue);
  }

  protected String formatStimmenAnzahlUngueltigeStimmenartKennung(GruppeImGebiet gruppeImGebiet, Stimmenart stimmenart) {
    return formatAnzahlUngueltigeStimmenartKennung(gruppeImGebiet.getGebiet(), stimmenart);
  }

  protected String formatAnzahlUngueltigeStimmenartKennung(Gebiet gebiet, Stimmenart stimmenart) {
    return formatAnzahlUngueltigeStimmenartKennung(gebiet, stimmenart, "");
  }

  protected String formatAnzahlUngueltigeStimmenartKennung(Gebiet gebiet, Stimmenart stimmenart, String defaultValue) {
    String msgKeyInfix = MSG_KEY_INFIX_UNGUELTIGE + ".ANZAHL." + stimmenart.name();

    return getKennungOrDefault(gebiet, msgKeyInfix, defaultValue);
  }

  protected String formatStimmenAnzahlUngueltigeStimmenartBezeichnung(GruppeImGebiet gruppeImGebiet, Stimmenart stimmenart) {
    return formatAnzahlUngueltigeStimmenartBezeichnung(gruppeImGebiet.getGebiet(), stimmenart);
  }

  protected String formatAnzahlUngueltigeStimmenartBezeichnung(Gebiet gebiet, Stimmenart stimmenart) {
    return formatAnzahlUngueltigeStimmenartBezeichnung(gebiet, stimmenart, "");
  }

  protected String formatAnzahlUngueltigeStimmenartBezeichnung(Gebiet gebiet, Stimmenart stimmenart, String defaultValue) {
    String msgKeyInfix = MSG_KEY_INFIX_UNGUELTIGE + ".ANZAHL." + stimmenart.name();

    return getBezeichnungOrDefault(gebiet, msgKeyInfix, defaultValue);
  }

  protected String formatStimmenAnzahlUnbestimmteStimmenartKennung(GruppeImGebiet gruppeImGebiet, Stimmenart stimmenart) {
    return formatAnzahlUnbestimmteStimmenartKennung(gruppeImGebiet.getGebiet(), stimmenart);
  }

  protected String formatAnzahlUnbestimmteStimmenartKennung(Gebiet gebiet, Stimmenart stimmenart) {
    return formatAnzahlUnbestimmteStimmenartKennung(gebiet, stimmenart, "");
  }

  protected String formatAnzahlUnbestimmteStimmenartKennung(Gebiet gebiet, Stimmenart stimmenart, String defaultValue) {
    String msgKeyInfix = MSG_KEY_INFIX_UNBESTIMMTE + ".ANZAHL." + stimmenart.name();

    return getKennungOrDefault(gebiet, msgKeyInfix, defaultValue);
  }

  protected String formatStimmenAnzahlUnbestimmteStimmenartBezeichnung(GruppeImGebiet gruppeImGebiet, Stimmenart stimmenart) {
    return formatAnzahlUnbestimmteStimmenartBezeichnung(gruppeImGebiet.getGebiet(), stimmenart);
  }

  protected String formatAnzahlUnbestimmteStimmenartBezeichnung(Gebiet gebiet, Stimmenart stimmenart) {
    return formatAnzahlUnbestimmteStimmenartBezeichnung(gebiet, stimmenart, "");
  }

  protected String formatAnzahlUnbestimmteStimmenartBezeichnung(Gebiet gebiet, Stimmenart stimmenart, String defaultValue) {
    String msgKeyInfix = MSG_KEY_INFIX_UNBESTIMMTE + ".ANZAHL." + stimmenart.name();

    return getBezeichnungOrDefault(gebiet, msgKeyInfix, defaultValue);
  }

  protected String formatAnzahlUebrigeStimmenartKennung(GruppeImGebiet gruppeImGebiet, Stimmenart stimmenart) {
    return formatAnzahlUebrigeStimmenartKennung(gruppeImGebiet.getGebiet(), stimmenart);
  }

  protected String formatAnzahlUebrigeStimmenartKennung(Gebiet gebiet, Stimmenart stimmenart) {
    return formatAnzahlUebrigeStimmenartKennung(gebiet, stimmenart, "");
  }

  protected String formatAnzahlUebrigeStimmenartKennung(Gebiet gebiet, Stimmenart stimmenart, String defaultValue) {
    String msgKeyInfix = MSG_KEY_INFIX_UEBRIGE + ".ANZAHL." + stimmenart.name();

    return getKennungOrDefault(gebiet, msgKeyInfix, defaultValue);
  }

  protected String formatAnzahlUebrigeStimmenartBezeichnung(GruppeImGebiet gruppeImGebiet, Stimmenart stimmenart) {
    return formatAnzahlUebrigeStimmenartBezeichnung(gruppeImGebiet.getGebiet(), stimmenart);
  }

  protected String formatAnzahlUebrigeStimmenartBezeichnung(Gebiet gebiet, Stimmenart stimmenart) {
    return formatAnzahlUebrigeStimmenartBezeichnung(gebiet, stimmenart, "");
  }

  protected String formatAnzahlUebrigeStimmenartBezeichnung(Gebiet gebiet, Stimmenart stimmenart, String defaultValue) {
    String msgKeyInfix = MSG_KEY_INFIX_UEBRIGE + ".ANZAHL." + stimmenart.name();

    return getBezeichnungOrDefault(gebiet, msgKeyInfix, defaultValue);
  }

  @Nullable
  public WahlvorschlagStimmzettelPosition getWahlvorschlagStimmzettelPosition(EntityAufStimmzettelDaten entityAufStimmzettelDaten, @Nullable GruppeImGebiet gruppeImGebiet,
      Collection<GruppeImGebiet> gruppenImGebiet) {
    if (gruppeImGebiet != null && gruppenImGebiet.contains(gruppeImGebiet) && !isGruppeImGebietFuerWaehlerBenannteKandidaturen(gruppeImGebiet)) {
      return bestimmeStimmzettelPosition(entityAufStimmzettelDaten, gruppeImGebiet);
    }

    LOG.warn("Zur GruppeImGebiet [{}] kann keine Stimmzettelposition generiert werden", gruppeImGebiet);

    return null;
  }

  protected WahlvorschlagStimmzettelPosition bestimmeStimmzettelPosition(EntityAufStimmzettelDaten entityAufStimmzettelDaten, @Nonnull GruppeImGebiet gruppeImGebiet) {
    return gruppeImGebiet.getStimmzettelPosition(entityAufStimmzettelDaten);
  }

  public String getWahlvorschlagStimmzettelKurzname(EntityAufStimmzettelDaten entityAufStimmzettelDaten, GruppeImGebiet gruppeImGebiet) {
    return defaultIfBlank(gruppeImGebiet.getKurznameAufGebiet(), gruppeImGebiet.getNameAufGebiet());
  }

  protected boolean isGruppeImGebietFuerWaehlerBenannteKandidaturen(GruppeImGebiet gruppeImGebiet) {
    if (gruppeImGebiet != null) {
      Gruppe gruppe = gruppeImGebiet.getGruppe();

      return WaehlerBenannteKandidaturen.isWaehlerBenannterDirektkandidatGruppe(gruppe) || WaehlerBenannteKandidaturen.isWaehlerBenannteListeGruppe(gruppe);
    }

    return false;
  }

  protected boolean isSystemGruppeGueltigeErfasstAlsStimmzettel(Gebiet gebiet) {
    return isSystemGruppeGueltigeErfasstAlsStimmzettel(gebiet, Ergebnisart.VORLAEUFIG) && isSystemGruppeGueltigeErfasstAlsStimmzettel(gebiet, Ergebnisart.ENDGUELTIG);
  }

  // ELECT-8887
  protected boolean isSystemGruppeGueltigeErfasstAlsStimmzettel(Gebiet gebiet, WahlartErgebnisart ergebnisart) {
    return getUmgangGueltige(gebiet, ergebnisart).isErfassenAlsStimmzettel();
  }

  private UmgangGueltigeWerte getUmgangGueltige(Gebiet gebiet, WahlartErgebnisart ergebnisart) {
    requireNonNullWahlart(gebiet);

    return UmgangGueltigeWerte.valueOfGueltige(gebiet.getWahl(), ergebnisart);
  }

  protected boolean isSystemGruppeUnbestimmteErfasstAlsStimmzettel(Gebiet gebiet) {
    return isSystemGruppeUnbestimmteErfasstAlsStimmzettel(gebiet, Ergebnisart.VORLAEUFIG) && isSystemGruppeUnbestimmteErfasstAlsStimmzettel(gebiet, Ergebnisart.ENDGUELTIG);
  }

  protected boolean isSystemGruppeUnbestimmteErfasstAlsStimmzettel(Gebiet gebiet, WahlartErgebnisart ergebnisart) {
    return isSystemGruppeUngueltigeErfasstAlsStimmzettel(gebiet, ergebnisart); // ELECT-13792, erstmal an der Konfiguration für UNGUELTIGE festmachen...
  }

  protected boolean isSystemGruppeUngueltigeErfasstAlsStimmzettel(Gebiet gebiet) {
    return isSystemGruppeUngueltigeErfasstAlsStimmzettel(gebiet, Ergebnisart.VORLAEUFIG) && isSystemGruppeUngueltigeErfasstAlsStimmzettel(gebiet, Ergebnisart.ENDGUELTIG);
  }

  // ELECT-8887
  protected boolean isSystemGruppeUngueltigeErfasstAlsStimmzettel(Gebiet gebiet, WahlartErgebnisart ergebnisart) {
    return getUmgangUngueltige(gebiet, ergebnisart).isErfassenAlsStimmzettel();
  }

  private UmgangGueltigeWerte getUmgangUngueltige(Gebiet gebiet, WahlartErgebnisart ergebnisart) {
    requireNonNullWahlart(gebiet);

    return UmgangGueltigeWerte.valueOfUngueltige(gebiet.getWahl(), ergebnisart);
  }

  protected String getMessage(Enum< ? > enumValue, Object... params) {
    return stimmeLabelMessages.getMessage(enumValue, params);
  }

  private void requireNonNullWahlart(Gebiet gebiet) {
    requireNonNull(gebiet, "Gebiet darf nicht null sein");
    requireNonNull(gebiet.getWahl(), "Wahl darf nicht null sein");
    requireNonNull(gebiet.getWahl().getWahlart(), "Wahlart darf nicht null sein");
  }

  protected String fixNullToEmpty(Object value) {
    if (value == null) {
      return "";
    }

    return value.toString();
  }

  protected long fixNullToZero(AbstractEntityAufStimmzettelStimmzettelPosition value) {
    if (value != null) {
      return fixNullToZero(value.getPosition());
    }

    return 0L;
  }

  protected long fixNullToZero(Long value) {
    if (value != null) {
      return value;
    }

    return 0L;
  }
}
