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

import static java.util.Objects.nonNull;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
import de.ivu.elect.business.gruppeimgebiet.entity.GruppeImGebiet;
import de.ivu.elect.business.kandidatur.boundary.WaehlerBenannteKandidaturKonfiguration;
import de.ivu.elect.business.stimmzettel.entity.EntityAufStimmzettelDaten;
import de.ivu.elect.business.wahlvorschlag.control.ReferendumOptionByPositionComparator;
import de.ivu.elect.business.wahlvorschlag.entity.ReferendumOption;
import de.ivu.elect.business.wahlvorschlag.entity.WahlvorschlagStimmzettelPosition;

public class ReferendumStimmeLabelGenerator extends AbstractStimmeLabelGenerator {

  private static final long serialVersionUID = 1L;

  // Cf. Stimmenart#REFERENDUM_STIMME
  protected static final String MSG_KEY_INFIX_REFERENDUM_STIMME = "REFERENDUM_OPTION_STIMME";

  public ReferendumStimmeLabelGenerator() {
    super();
  }

  public ReferendumStimmeLabelGenerator(StimmeLabelMessages stimmeLabelMessages, WaehlerBenannteKandidaturKonfiguration waehlerBenannteKandidaturKonfiguration) {
    super(stimmeLabelMessages, waehlerBenannteKandidaturKonfiguration);
  }

  public String formatReferendumVorlageKennung(EntityAufStimmzettelDaten entityAufStimmzettelDaten, GruppeImGebiet gruppeImGebiet, Collection<GruppeImGebiet> gruppenImGebiet) {
    return format(getReferendumVorlageKennung(gruppeImGebiet.getGebiet()), entityAufStimmzettelDaten, gruppeImGebiet, gruppenImGebiet);
  }

  protected String getReferendumVorlageKennung(Gebiet gebiet) {
    String msgKeyInfix = "REFERENDUM_VORLAGE";

    return getKennungOrDefault(gebiet, msgKeyInfix, "{0}");
  }

  public String formatReferendumVorlageBezeichnung(EntityAufStimmzettelDaten entityAufStimmzettelDaten, GruppeImGebiet gruppeImGebiet, Collection<GruppeImGebiet> gruppenImGebiet) {
    return format(getReferendumVorlageBezeichnung(gruppeImGebiet.getGebiet()), entityAufStimmzettelDaten, gruppeImGebiet, gruppenImGebiet);
  }

  protected String getReferendumVorlageBezeichnung(Gebiet gebiet) {
    String msgKeyInfix = "REFERENDUM_VORLAGE";

    return getBezeichnungOrDefault(gebiet, msgKeyInfix, "{1}");
  }

  public String formatReferendumOptionStimmeAnzahlUngueltigeGesamtKennung(EntityAufStimmzettelDaten entityAufStimmzettelDaten, GruppeImGebiet gruppeImGebiet,
      Collection<GruppeImGebiet> gruppenImGebiet) {
    String pattern = getReferendumOptionStimmeAnzahlUngueltigeGesamtKennung(gruppeImGebiet.getGebiet());

    // NOTE ELECTDD-369, dass die Kennung ohne Nummer angezeigt werden soll.
    if (containsSingleReferendumVorlage(gruppenImGebiet)) {
      return format(pattern, null, null, null, null, null);
    }

    return format(pattern, entityAufStimmzettelDaten, gruppeImGebiet, gruppenImGebiet);
  }

  protected String getReferendumOptionStimmeAnzahlUngueltigeGesamtKennung(Gebiet gebiet) {
    String msgKeyInfix = MSG_KEY_INFIX_REFERENDUM_STIMME + ".ANZAHL." + MSG_KEY_INFIX_UNGUELTIGE + "." + MSG_KEY_INFIX_GESAMT;

    return getKennungOrDefault(gebiet, msgKeyInfix, "C{0}");
  }

  public String formatReferendumOptionStimmeAnzahlUngueltigeGesamtBezeichnung(EntityAufStimmzettelDaten entityAufStimmzettelDaten, GruppeImGebiet gruppeImGebiet,
      Collection<GruppeImGebiet> gruppenImGebiet) {
    return format(getReferendumOptionStimmeAnzahlUngueltigeGesamtBezeichnung(gruppeImGebiet.getGebiet()), entityAufStimmzettelDaten, gruppeImGebiet, gruppenImGebiet);
  }

  protected String getReferendumOptionStimmeAnzahlUngueltigeGesamtBezeichnung(Gebiet gebiet) {
    String msgKeyInfix = MSG_KEY_INFIX_REFERENDUM_STIMME + ".ANZAHL." + MSG_KEY_INFIX_UNGUELTIGE + "." + MSG_KEY_INFIX_GESAMT;

    return getBezeichnungOrDefault(gebiet, msgKeyInfix, "Ungültige Stimmen");
  }

  public String formatReferendumOptionStimmeAnzahlGueltigeGesamtKennung(EntityAufStimmzettelDaten entityAufStimmzettelDaten, GruppeImGebiet gruppeImGebiet,
      Collection<GruppeImGebiet> gruppenImGebiet) {
    String pattern = getReferendumOptionStimmeAnzahlGueltigeGesamtKennung(gruppeImGebiet.getGebiet());

    // NOTE ELECTDD-369, dass die Kennung ohne Nummer angezeigt werden soll.
    if (containsSingleReferendumVorlage(gruppenImGebiet)) {
      return format(pattern, null, null, null, null, null);
    }

    return format(pattern, entityAufStimmzettelDaten, gruppeImGebiet, gruppenImGebiet);
  }

  protected String getReferendumOptionStimmeAnzahlGueltigeGesamtKennung(Gebiet gebiet) {
    String msgKeyInfix = MSG_KEY_INFIX_REFERENDUM_STIMME + ".ANZAHL." + MSG_KEY_INFIX_GUELTIGE + "." + MSG_KEY_INFIX_GESAMT;

    return getKennungOrDefault(gebiet, msgKeyInfix, "D{0}");
  }

  public String formatReferendumOptionStimmeAnzahlGueltigeGesamtBezeichnung(EntityAufStimmzettelDaten entityAufStimmzettelDaten, GruppeImGebiet gruppeImGebiet,
      Collection<GruppeImGebiet> gruppenImGebiet) {
    return format(getReferendumOptionStimmeAnzahlGueltigeGesamtBezeichnung(gruppeImGebiet.getGebiet()), entityAufStimmzettelDaten, gruppeImGebiet, gruppenImGebiet);
  }

  protected String getReferendumOptionStimmeAnzahlGueltigeGesamtBezeichnung(Gebiet gebiet) {
    String msgKeyInfix = MSG_KEY_INFIX_REFERENDUM_STIMME + ".ANZAHL." + MSG_KEY_INFIX_GUELTIGE + "." + MSG_KEY_INFIX_GESAMT;

    return getBezeichnungOrDefault(gebiet, msgKeyInfix, "Gültige Stimmen");
  }

  public String formatReferendumOptionStimmeAnzahlGueltigeKennung(EntityAufStimmzettelDaten entityAufStimmzettelDaten, GruppeImGebiet gruppeImGebiet, Collection<GruppeImGebiet> gruppenImGebiet,
      ReferendumOption referendumOption) {
    String result = getReferendumOptionStimmeAnzahlGueltigeKennung(gruppeImGebiet.getGebiet());

    // ELECT-8093
    if (containsSingleReferendumVorlage(gruppenImGebiet)) {
      result = result.replace("{0}", "");
    }

    return format(result, entityAufStimmzettelDaten, gruppeImGebiet, gruppenImGebiet, referendumOption);
  }

  protected String getReferendumOptionStimmeAnzahlGueltigeKennung(Gebiet gebiet) {
    String msgKeyInfix = MSG_KEY_INFIX_REFERENDUM_STIMME + ".ANZAHL." + MSG_KEY_INFIX_GUELTIGE;

    return getKennungOrDefault(gebiet, msgKeyInfix, "D{0}-{3}");
  }

  public String formatReferendumOptionStimmeAnzahlGueltigeBezeichnung(EntityAufStimmzettelDaten entityAufStimmzettelDaten, GruppeImGebiet gruppeImGebiet, Collection<GruppeImGebiet> gruppenImGebiet,
      ReferendumOption referendumOption) {
    return format(getReferendumOptionStimmeAnzahlGueltigeBezeichnung(gruppeImGebiet.getGebiet()), entityAufStimmzettelDaten, gruppeImGebiet, gruppenImGebiet, referendumOption);
  }

  protected String getReferendumOptionStimmeAnzahlGueltigeBezeichnung(Gebiet gebiet) {
    String msgKeyInfix = MSG_KEY_INFIX_REFERENDUM_STIMME + ".ANZAHL." + MSG_KEY_INFIX_GUELTIGE;

    return getBezeichnungOrDefault(gebiet, msgKeyInfix, "davon gültige Stimmen für „{3}“");
  }

  public String formatReferendumOptionStimmeAnzahlAbgegebeneKennung(EntityAufStimmzettelDaten entityAufStimmzettelDaten, GruppeImGebiet gruppeImGebiet, Collection<GruppeImGebiet> gruppenImGebiet) {
    String pattern = getReferendumOptionStimmeAnzahlAbgegebeneKennung(gruppeImGebiet.getGebiet());

    // NOTE ELECTDD-369, dass die Kennung ohne Nummer angezeigt werden soll.
    if (containsSingleReferendumVorlage(gruppenImGebiet)) {
      return format(pattern, null, null, null, null, null);
    }

    return format(pattern, entityAufStimmzettelDaten, gruppeImGebiet, gruppenImGebiet);
  }

  public String formatReferendumOptionStimmeAnzahlUebrigeGesamtKennung(EntityAufStimmzettelDaten entityAufStimmzettelDaten, GruppeImGebiet gruppeImGebiet, Collection<GruppeImGebiet> gruppenImGebiet) {
    String pattern = getReferendumOptionStimmeAnzahlUebrigeGesamtKennung(gruppeImGebiet.getGebiet());

    // NOTE ELECTDD-369, dass die Kennung ohne Nummer angezeigt werden soll.
    if (containsSingleReferendumVorlage(gruppenImGebiet)) {
      return format(pattern, null, null, null, null, null);
    }

    return format(pattern, entityAufStimmzettelDaten, gruppeImGebiet, gruppenImGebiet);
  }

  protected String getReferendumOptionStimmeAnzahlUebrigeGesamtKennung(Gebiet gebiet) {
    String msgKeyInfix = MSG_KEY_INFIX_REFERENDUM_STIMME + ".ANZAHL." + MSG_KEY_INFIX_UEBRIGE + "." + MSG_KEY_INFIX_GESAMT;

    return getKennungOrDefault(gebiet, msgKeyInfix, "C{0}");
  }

  public String formatReferendumOptionStimmeAnzahlUebrigeGesamtBezeichnung(EntityAufStimmzettelDaten entityAufStimmzettelDaten, GruppeImGebiet gruppeImGebiet,
      Collection<GruppeImGebiet> gruppenImGebiet) {
    return format(getReferendumOptionStimmeAnzahlUebrigeGesamtBezeichnung(gruppeImGebiet.getGebiet()), entityAufStimmzettelDaten, gruppeImGebiet, gruppenImGebiet);
  }

  protected String getReferendumOptionStimmeAnzahlUebrigeGesamtBezeichnung(Gebiet gebiet) {
    String msgKeyInfix = MSG_KEY_INFIX_REFERENDUM_STIMME + ".ANZAHL." + MSG_KEY_INFIX_UEBRIGE + "." + MSG_KEY_INFIX_GESAMT;

    return getBezeichnungOrDefault(gebiet, msgKeyInfix, "");
  }

  protected String getReferendumOptionStimmeAnzahlAbgegebeneKennung(Gebiet gebiet) {
    String msgKeyInfix = MSG_KEY_INFIX_REFERENDUM_STIMME + ".ANZAHL." + MSG_KEY_INFIX_ABGEGEBENE;

    return getKennungOrDefault(gebiet, msgKeyInfix, "E{0}");
  }

  protected String getReferendumOptionStimmeAnzahlAbgegebeneBezeichnung(Gebiet gebiet) {
    String msgKeyInfix = MSG_KEY_INFIX_REFERENDUM_STIMME + ".ANZAHL." + MSG_KEY_INFIX_ABGEGEBENE;

    return getBezeichnungOrDefault(gebiet, msgKeyInfix, "Abgegebene Stimmen insgesamt");
  }

  public String formatReferendumOptionStimmeAnzahlAbgegebeneGesamtKennung(EntityAufStimmzettelDaten entityAufStimmzettelDaten, GruppeImGebiet gruppeImGebiet,
      Collection<GruppeImGebiet> gruppenImGebiet) {
    String pattern = getReferendumOptionStimmeAnzahlAbgegebeneGesamtKennung(gruppeImGebiet.getGebiet());

    // NOTE ELECTDD-369, dass die Kennung ohne Nummer angezeigt werden soll.
    if (containsSingleReferendumVorlage(gruppenImGebiet)) {
      return format(pattern, null, null, null, null, null);
    }

    return format(pattern, entityAufStimmzettelDaten, gruppeImGebiet, gruppenImGebiet);
  }

  protected String getReferendumOptionStimmeAnzahlAbgegebeneGesamtKennung(Gebiet gebiet) {
    String msgKeyInfix = MSG_KEY_INFIX_REFERENDUM_STIMME + ".ANZAHL." + MSG_KEY_INFIX_ABGEGEBENE + "." + MSG_KEY_INFIX_GESAMT;

    return getKennungOrDefault(gebiet, msgKeyInfix, "C{0} + D{0}");
  }

  public String formatReferendumOptionStimmeAnzahlGesamtAbgegebenBezeichnung(EntityAufStimmzettelDaten entityAufStimmzettelDaten, GruppeImGebiet gruppeImGebiet,
      Collection<GruppeImGebiet> gruppenImGebiet) {
    return format(getReferendumOptionStimmeAnzahlAbgegebeneGesamtBezeichnung(gruppeImGebiet.getGebiet()), entityAufStimmzettelDaten, gruppeImGebiet, gruppenImGebiet);
  }

  protected String getReferendumOptionStimmeAnzahlAbgegebeneGesamtBezeichnung(Gebiet gebiet) {
    String msgKeyInfix = MSG_KEY_INFIX_REFERENDUM_STIMME + ".ANZAHL." + MSG_KEY_INFIX_ABGEGEBENE + "." + MSG_KEY_INFIX_GESAMT;

    return getBezeichnungOrDefault(gebiet, msgKeyInfix, "Abgegebene Stimmen insgesamt");
  }

  public Long getReferendumOptionNummer(ReferendumOption referendumOption) {
    if (nonNull(referendumOption)) {
      List<ReferendumOption> referendumOptionen = new ArrayList<>(referendumOption.getGruppeImGebiet().getReferendumOptionen());

      Collections.sort(referendumOptionen, new ReferendumOptionByPositionComparator());

      if (referendumOptionen.contains(referendumOption)) {
        return Long.valueOf(referendumOptionen.indexOf(referendumOption) + 1);
      }
    }

    return null;
  }

  public String getReferendumOptionKurzbezeichnung(ReferendumOption referendumOption) {
    if (nonNull(referendumOption)) {
      return referendumOption.getKurzbezeichnungWennGesetztSonstLabel();
    }

    return null;
  }

  private String format(String pattern, EntityAufStimmzettelDaten entityAufStimmzettelDaten, GruppeImGebiet gruppeImGebiet, Collection<GruppeImGebiet> gruppenImGebiet) {
    return format(pattern, entityAufStimmzettelDaten, gruppeImGebiet, gruppenImGebiet, null);
  }

  private String format(String pattern, EntityAufStimmzettelDaten entityAufStimmzettelDaten, GruppeImGebiet gruppeImGebiet, Collection<GruppeImGebiet> gruppenImGebiet,
      ReferendumOption referendumOption) {
    Long referendumVorlagePosition = null;

    WahlvorschlagStimmzettelPosition referendumVorlageStimmzettelPosition = getWahlvorschlagStimmzettelPosition(entityAufStimmzettelDaten, gruppeImGebiet, gruppenImGebiet);

    if (referendumVorlageStimmzettelPosition != null && referendumVorlageStimmzettelPosition.hasPosition()) {
      referendumVorlagePosition = referendumVorlageStimmzettelPosition.getPosition();
    }

    Long referendumVorlagePositionOderReferendumOptionNummer = referendumVorlagePosition;
    Long referendumOptionNummer = getReferendumOptionNummer(referendumOption);

    if (containsSingleReferendumVorlage(gruppenImGebiet)) {
      referendumVorlagePositionOderReferendumOptionNummer = referendumOptionNummer;
    }

    return format(pattern, referendumVorlageStimmzettelPosition, getWahlvorschlagStimmzettelKurzname(entityAufStimmzettelDaten, gruppeImGebiet), referendumOptionNummer,
        getReferendumOptionKurzbezeichnung(referendumOption), referendumVorlagePositionOderReferendumOptionNummer);
  }

  protected String format(String pattern, WahlvorschlagStimmzettelPosition wahlvorschlagStimmzettelPosition, String wahlvorschlagStimmzettelKurzname, Long referendumOptionNummer,
      String referendumOptionKurzbezeichnung, Long wahlvorschlagStimmzettelPositionOderReferendumOptionNummer) {
    return MessageFormat.format(pattern, fixNullToEmpty(wahlvorschlagStimmzettelPosition), fixNullToEmpty(wahlvorschlagStimmzettelKurzname), fixNullToEmpty(referendumOptionNummer),
        fixNullToEmpty(referendumOptionKurzbezeichnung), fixNullToEmpty(wahlvorschlagStimmzettelPositionOderReferendumOptionNummer));
  }

  private boolean containsSingleReferendumVorlage(Collection<GruppeImGebiet> gruppenImGebiet) {
    int referendumVorlagenCount = 0;

    for (GruppeImGebiet gruppeImGebiet : gruppenImGebiet) {
      if (gruppeImGebiet.isReferendumVorlage()) {
        referendumVorlagenCount++;

        if (referendumVorlagenCount > 1) {
          return false;
        }
      }
    }

    return true;
  }
}
