
package de.ivu.elect.dokumente.ergebnistabelle;

import static de.ivu.elect.jee.entity.EntityUtil.castTo;
import static org.apache.commons.lang3.StringUtils.containsIgnoreCase;
import static org.apache.commons.lang3.StringUtils.trimToEmpty;

import java.util.Locale;

import javax.annotation.Nullable;

import de.ivu.elect.business.ergebnis.control.ErgebnisDaten;
import de.ivu.elect.business.gebietsbaum.control.CustomGebietInformationen;
import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
import de.ivu.elect.business.gebietsbaum.entity.Wahldurchfuehrungsgebiet;
import de.ivu.elect.business.internet.control.GebietUndStimmabgabeart;
import de.ivu.elect.business.stimmen.boundary.KopfzahlLabelGenerator;
import de.ivu.elect.jee.faces.Messages;

// TODO: Klasse lokalisieren
public abstract class AbstractErgebnistabelleDocumentColumn implements ErgebnistabelleDocumentColumn {

  private final Locale locale;

  private final Messages messages;

  protected AbstractErgebnistabelleDocumentColumn(Locale locale, Messages messages) {
    super();

    this.locale = locale;

    this.messages = messages;
  }

  @Override
  public boolean hasGruppierung() {
    return getGruppierung() != null;
  }

  @Override
  public String getGruppierung() {
    return null;
  }

  @Override
  public boolean hasUntergruppierung() {
    return getUntergruppierung() != null;
  }

  @Nullable
  @Override
  public String getUntergruppierung() {
    return null;
  }

  @Override
  public boolean hasKennung() {
    return getKennung() != null;
  }

  @Override
  public ErgebnistabelleSpreadsheetStyleTemplate getTextStyle(ErgebnistabelleCsvDTO dto) {
    if (dto.isSumme()) {
      return ErgebnistabelleSpreadsheetStyleTemplate.TEXT_STYLE_BOLD;
    }

    return ErgebnistabelleSpreadsheetStyleTemplate.TEXT_STYLE_DEFAULT;
  }

  @Override
  public int getRelativeSpaltenBreite() {
    return 1000;
  }

  public static abstract class AbstractLongErgebnistabelleDocumentColumn extends AbstractErgebnistabelleDocumentColumn {

    public AbstractLongErgebnistabelleDocumentColumn(Locale locale, Messages messages) {
      super(locale, messages);
    }

    @Override
    public String getValue(ErgebnistabelleCsvDTO dto) {
      return Long.toString(nonNull(getLongValue(dto)));
    }

    public abstract Long getLongValue(ErgebnistabelleCsvDTO dto);

    @Override
    public ErgebnistabelleSpreadsheetStyleTemplate getTextStyle(ErgebnistabelleCsvDTO dto) {
      if (dto.isSumme()) {
        return ErgebnistabelleSpreadsheetStyleTemplate.TEXT_STYLE_BOLD_NUMBER;
      }

      return ErgebnistabelleSpreadsheetStyleTemplate.TEXT_STYLE_NUMBER;
    }
  }

  public static abstract class AbstractKopfzahlErgebnistabelleDocumentColumn extends AbstractLongErgebnistabelleDocumentColumn {

    private final Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet;

    private final ErgebnisDaten ergebnisDaten;

    private final KopfzahlLabelGenerator kopfzahlLabelGenerator;

    public AbstractKopfzahlErgebnistabelleDocumentColumn(Locale locale, Messages messages, KopfzahlLabelGenerator kopfzahlLabelGenerator, Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet,
        ErgebnisDaten ergebnisDaten) {
      super(locale, messages);

      this.wahldurchfuehrungsgebiet = wahldurchfuehrungsgebiet;

      this.ergebnisDaten = ergebnisDaten;

      this.kopfzahlLabelGenerator = kopfzahlLabelGenerator;
    }

    protected Wahldurchfuehrungsgebiet getWahldurchfuehrungsgebiet() {
      return wahldurchfuehrungsgebiet;
    }

    protected ErgebnisDaten getErgebnisDaten() {
      return ergebnisDaten;
    }

    protected KopfzahlLabelGenerator getKopfzahlLabelGenerator() {
      return kopfzahlLabelGenerator;
    }

    protected GebietUndStimmabgabeart asGebietUndStimmabgabeart(ErgebnistabelleCsvDTO dto) {
      return new GebietUndStimmabgabeart(castTo(dto.getGebiet(), Wahldurchfuehrungsgebiet.class), dto.getStimmabgabeart());
    }
  }

  public static class ErgebnistabelleStatistischeGemeindekennzifferDocumentColumn extends AbstractErgebnistabelleDocumentColumn {

    private final CustomGebietInformationen customGebietInformationen;

    public ErgebnistabelleStatistischeGemeindekennzifferDocumentColumn(Locale locale, Messages messages, CustomGebietInformationen customGebietInformationen) {
      super(locale, messages);

      this.customGebietInformationen = customGebietInformationen;
    }

    @Override
    public String getGruppierung() {
      return null;
    }

    @Override
    public String getBezeichnung() {
      return "Statistische Gemeindekennziffer";
    }

    @Override
    public String getKennung() {
      return null;
    }

    @Nullable
    @Override
    public String getValue(ErgebnistabelleCsvDTO dto) {
      if (dto.isSumme()) {
        return customGebietInformationen.getGemeindekennzifferFuerErgebnisdokument(dto.getGebiet());
      }
      return null;
    }
  }

  public static class ErgebnistabelleErfassungsgebietDocumentColumn extends AbstractErgebnistabelleDocumentColumn {

    private final boolean gebietstextAusgeben;

    public ErgebnistabelleErfassungsgebietDocumentColumn(Locale locale, Messages messages, boolean gebietstextAusgeben) {
      super(locale, messages);

      this.gebietstextAusgeben = gebietstextAusgeben;
    }

    @Override
    public String getGruppierung() {
      return null;
    }

    @Override
    public String getBezeichnung() {
      return "Gliede\u00ADrung des Wahl\u00ADergebnisses";
    }

    @Override
    public String getKennung() {
      return null;
    }

    @Override
    public String getValue(ErgebnistabelleCsvDTO dto) {
      if (dto.isSumme()) {
        if (dto.isGesamt()) {
          return getMessage("ergebnistabelle.erfassungsgebiet.document.column.summe.gesamt.bezeichnung");
        }

        if (dto.isBriefwahl()) {
          return "Zwischen\u00ADsumme - Brief";
        }

        if (dto.isUrnenwahl()) {
          return "Zwischen\u00ADsumme - Urne";
        }
      }

      Gebiet wahlbezirk = dto.getGebiet();
      String wahlbezirkName = trimToEmpty(wahlbezirk.getName());
      String wahlbezirkNummer = trimToEmpty(wahlbezirk.getNummerBezirkFallsGesetzt());

      if (gebietstextAusgeben) {
        if (containsIgnoreCase(wahlbezirkName, wahlbezirkNummer)) {
          return wahlbezirkName;
        }

        return trimToEmpty(wahlbezirkNummer + " " + wahlbezirkName);
      }

      return wahlbezirkNummer;
    }

    @Override
    public ErgebnistabelleSpreadsheetStyleTemplate getTextStyle(ErgebnistabelleCsvDTO dto) {
      if (dto.isSumme() || dto.isCustom()) {
        return super.getTextStyle(dto);
      }

      return ErgebnistabelleSpreadsheetStyleTemplate.TEXT_STYLE_CELL_WITHOUT_LINE_BREAK;
    }

    @Override
    public int getRelativeSpaltenBreite() {
      if (gebietstextAusgeben) {
        // doppelte Breite, da Text statt schnöder Zahlen...
        return 2 * super.getRelativeSpaltenBreite();
      }
      return super.getRelativeSpaltenBreite() + 50;
    }
  }

  public static class ErgebnistabelleWahlberechtigteOhneWahlscheinDocumentColumn extends AbstractKopfzahlErgebnistabelleDocumentColumn {

    public ErgebnistabelleWahlberechtigteOhneWahlscheinDocumentColumn(Locale locale, Messages messages, KopfzahlLabelGenerator kopfzahlLabelGenerator,
        Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, ErgebnisDaten ergebnisDaten) {
      super(locale, messages, kopfzahlLabelGenerator, wahldurchfuehrungsgebiet, ergebnisDaten);
    }

    @Override
    public String getGruppierung() {
      return getKopfzahlLabelGenerator().getWahlberechtigteBezeichnungPlural(getWahldurchfuehrungsgebiet());
    }

    @Override
    public String getUntergruppierung() {
      return "laut Wählerverzeichnis";
    }

    @Override
    public String getBezeichnung() {
      return getMessage("ergebnistabelle.wahlberechtigte.ohne.wahlschein.document.column.bezeichnung");
    }

    @Override
    public String getKennung() {
      return getKopfzahlLabelGenerator().getAnzahlWahlberechtigteOhneWahlscheinKennung(getWahldurchfuehrungsgebiet());
    }

    @Override
    public Long getLongValue(ErgebnistabelleCsvDTO dto) {
      return getErgebnisDaten().getWahlberechtigteOhneWahlscheinFuerErfassungsgebietUnterhalbVonAnzahl(asGebietUndStimmabgabeart(dto));
    }
  }

  public static class ErgebnistabelleWahlberechtigteMitWahlscheinDocumentColumn extends AbstractKopfzahlErgebnistabelleDocumentColumn {

    public ErgebnistabelleWahlberechtigteMitWahlscheinDocumentColumn(Locale locale, Messages messages, KopfzahlLabelGenerator kopfzahlLabelGenerator, Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet,
        ErgebnisDaten ergebnisDaten) {
      super(locale, messages, kopfzahlLabelGenerator, wahldurchfuehrungsgebiet, ergebnisDaten);
    }

    @Override
    public String getGruppierung() {
      return getKopfzahlLabelGenerator().getWahlberechtigteBezeichnungPlural(getWahldurchfuehrungsgebiet());
    }

    @Override
    public String getUntergruppierung() {
      return "laut Wählerverzeichnis";
    }

    @Override
    public String getBezeichnung() {
      return getMessage("ergebnistabelle.wahlberechtigte.mit.wahlschein.document.column.bezeichnung");
    }

    @Override
    public String getKennung() {
      return getKopfzahlLabelGenerator().getAnzahlWahlberechtigteMitWahlscheinKennung(getWahldurchfuehrungsgebiet());
    }

    @Override
    public Long getLongValue(ErgebnistabelleCsvDTO dto) {
      return getErgebnisDaten().getWahlberechtigteMitWahlscheinFuerErfassungsgebietUnterhalbVonAnzahl(asGebietUndStimmabgabeart(dto));
    }
  }

  public static class ErgebnistabelleWahlberechtigteNichtImWaehlerverzeichnisDocumentColumn extends AbstractKopfzahlErgebnistabelleDocumentColumn {

    public ErgebnistabelleWahlberechtigteNichtImWaehlerverzeichnisDocumentColumn(Locale locale, Messages messages, KopfzahlLabelGenerator kopfzahlLabelGenerator,
        Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, ErgebnisDaten ergebnisDaten) {
      super(locale, messages, kopfzahlLabelGenerator, wahldurchfuehrungsgebiet, ergebnisDaten);
    }

    @Override
    public String getGruppierung() {
      return getKopfzahlLabelGenerator().getWahlberechtigteBezeichnungPlural(getWahldurchfuehrungsgebiet());
    }

    @Override
    public String getBezeichnung() {
      String msgKey = "ergebnistabelle.wahlberechtigte.nicht.im.waehlerverzeichnis.document.column.bezeichnung";
      String msgKeyWahlartSuffix = "." + getWahldurchfuehrungsgebiet().getWahl().getWahlart().getWahlartNameNormalized();

      if (existsMessage(msgKey + msgKeyWahlartSuffix)) {
        return getMessage(msgKey + msgKeyWahlartSuffix);
      }

      return getMessage(msgKey);
    }

    @Override
    public String getKennung() {
      return getKopfzahlLabelGenerator().getAnzahlWahlberechtigteNichtImWaehlerverzeichnisKennung(getWahldurchfuehrungsgebiet());
    }

    @Override
    public int getRelativeSpaltenBreite() {
      return super.getRelativeSpaltenBreite() + 50; // etwas breiter, weil viel Text...
    }

    @Override
    public Long getLongValue(ErgebnistabelleCsvDTO dto) {
      return getErgebnisDaten().getWahlberechtigteNichtImWaehlerverzeichnisFuerErfassungsgebietUnterhalbVonAnzahl(asGebietUndStimmabgabeart(dto));
    }
  }

  public static class ErgebnistabelleWahlberechtigteGesamtDocumentColumn extends AbstractKopfzahlErgebnistabelleDocumentColumn {

    public ErgebnistabelleWahlberechtigteGesamtDocumentColumn(Locale locale, Messages messages, KopfzahlLabelGenerator kopfzahlLabelGenerator, Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet,
        ErgebnisDaten ergebnisDaten) {
      super(locale, messages, kopfzahlLabelGenerator, wahldurchfuehrungsgebiet, ergebnisDaten);
    }

    @Override
    public String getGruppierung() {
      return getKopfzahlLabelGenerator().getWahlberechtigteBezeichnungPlural(getWahldurchfuehrungsgebiet());
    }

    @Override
    public String getBezeichnung() {
      return "insgesamt (" + getKopfzahlLabelGenerator().getAnzahlWahlberechtigteGesamtOhneWahlscheinPlusMitWahlscheinPlusNichtImWaehlerverzeichnisSummanden(getWahldurchfuehrungsgebiet()) + ")";
    }

    @Override
    public String getKennung() {
      return getKopfzahlLabelGenerator().getAnzahlWahlberechtigteGesamtKennung(getWahldurchfuehrungsgebiet());
    }

    @Override
    public Long getLongValue(ErgebnistabelleCsvDTO dto) {
      return getErgebnisDaten().getWahlberechtigteGesamtFuerErfassungsgebietUnterhalbVonAnzahl(asGebietUndStimmabgabeart(dto));
    }
  }

  public static class ErgebnistabelleWaehlerMitWahlscheinDocumentColumn extends AbstractKopfzahlErgebnistabelleDocumentColumn {

    public ErgebnistabelleWaehlerMitWahlscheinDocumentColumn(Locale locale, Messages messages, KopfzahlLabelGenerator kopfzahlLabelGenerator, Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet,
        ErgebnisDaten ergebnisDaten) {
      super(locale, messages, kopfzahlLabelGenerator, wahldurchfuehrungsgebiet, ergebnisDaten);
    }

    @Override
    public String getGruppierung() {
      return getKopfzahlLabelGenerator().getWaehlerBezeichnungPlural(getWahldurchfuehrungsgebiet());
    }

    @Override
    public String getBezeichnung() {
      return getMessage("ergebnistabelle.waehler.mit.wahlschein.document.column.bezeichnung");
    }

    @Override
    public String getKennung() {
      return getKopfzahlLabelGenerator().getAnzahlWaehlerMitWahlscheinKennung(getWahldurchfuehrungsgebiet());
    }

    @Override
    public Long getLongValue(ErgebnistabelleCsvDTO dto) {
      return getErgebnisDaten().getErgebnisDatenWaehler().getSummeWaehlerMitWahlschein(asGebietUndStimmabgabeart(dto));
    }
  }

  public static class ErgebnistabelleWaehlerMitWahlscheinBriefwahlInUrneErfassbarDocumentColumn extends AbstractKopfzahlErgebnistabelleDocumentColumn {

    public ErgebnistabelleWaehlerMitWahlscheinBriefwahlInUrneErfassbarDocumentColumn(Locale locale, Messages messages, KopfzahlLabelGenerator kopfzahlLabelGenerator,
        Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, ErgebnisDaten ergebnisDaten) {
      super(locale, messages, kopfzahlLabelGenerator, wahldurchfuehrungsgebiet, ergebnisDaten);
    }

    @Override
    public String getGruppierung() {
      return getKopfzahlLabelGenerator().getWaehlerBezeichnungPlural(getWahldurchfuehrungsgebiet());
    }

    @Nullable
    @Override
    public String getUntergruppierung() {
      return "mit Wahlschein";
    }

    @Override
    public String getBezeichnung() {
      return "alle erfassten";
    }

    @Override
    public String getKennung() {
      return getKopfzahlLabelGenerator().getAnzahlWaehlerMitWahlscheinKennung(getWahldurchfuehrungsgebiet());
    }

    @Override
    public Long getLongValue(ErgebnistabelleCsvDTO dto) {
      return getErgebnisDaten().getErgebnisDatenWaehler().getSummeWaehlerMitWahlschein(asGebietUndStimmabgabeart(dto));
    }
  }

  public static class ErgebnistabelleWaehlerMitWahlscheinInUrnenwahlAnzahlAusUrnenwahlDocumentColumn extends AbstractKopfzahlErgebnistabelleDocumentColumn {

    public ErgebnistabelleWaehlerMitWahlscheinInUrnenwahlAnzahlAusUrnenwahlDocumentColumn(Locale locale, Messages messages, KopfzahlLabelGenerator kopfzahlLabelGenerator,
        Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, ErgebnisDaten ergebnisDaten) {
      super(locale, messages, kopfzahlLabelGenerator, wahldurchfuehrungsgebiet, ergebnisDaten);
    }

    @Override
    public String getGruppierung() {
      return getKopfzahlLabelGenerator().getWaehlerBezeichnungPlural(getWahldurchfuehrungsgebiet());
    }

    @Nullable
    @Override
    public String getUntergruppierung() {
      return "mit Wahlschein";
    }

    @Override
    public String getBezeichnung() {
      return "Urnenwähler";
    }

    @Override
    public String getKennung() {
      return getKopfzahlLabelGenerator().getAnzahlWaehlerMitWahlscheinInUrnenwahlAnzahlAusUrnenwahlKennung(getWahldurchfuehrungsgebiet());
    }

    @Override
    public Long getLongValue(ErgebnistabelleCsvDTO dto) {
      return getErgebnisDaten().getErgebnisDatenWaehler().getSummeWaehlerMitWahlscheinInUrnenwahlAnzahlAusUrnenwahl(asGebietUndStimmabgabeart(dto));
    }
  }

  public static class ErgebnistabelleWaehlerMitWahlscheinInUrnenwahlAnzahlAusBriefwahlDocumentColumn extends AbstractKopfzahlErgebnistabelleDocumentColumn {

    public ErgebnistabelleWaehlerMitWahlscheinInUrnenwahlAnzahlAusBriefwahlDocumentColumn(Locale locale, Messages messages, KopfzahlLabelGenerator kopfzahlLabelGenerator,
        Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, ErgebnisDaten ergebnisDaten) {
      super(locale, messages, kopfzahlLabelGenerator, wahldurchfuehrungsgebiet, ergebnisDaten);
    }

    @Override
    public String getGruppierung() {
      return getKopfzahlLabelGenerator().getWaehlerBezeichnungPlural(getWahldurchfuehrungsgebiet());
    }

    @Nullable
    @Override
    public String getUntergruppierung() {
      return "mit Wahlschein";
    }

    @Override
    public String getBezeichnung() {
      return "Briefwähler (in Urnenwahllokal erfasst)";
    }

    @Override
    public String getKennung() {
      return getKopfzahlLabelGenerator().getAnzahlWaehlerMitWahlscheinInUrnenwahlAnzahlAusBriefwahlKennung(getWahldurchfuehrungsgebiet());
    }

    @Override
    public Long getLongValue(ErgebnistabelleCsvDTO dto) {
      return getErgebnisDaten().getErgebnisDatenWaehler().getSummeWaehlerMitWahlscheinInUrnenwahlAnzahlAusBriefwahl(asGebietUndStimmabgabeart(dto));
    }
  }

  public static class ErgebnistabelleWaehlerGesamtDocumentColumn extends AbstractKopfzahlErgebnistabelleDocumentColumn {

    public ErgebnistabelleWaehlerGesamtDocumentColumn(Locale locale, Messages messages, KopfzahlLabelGenerator kopfzahlLabelGenerator, Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet,
        ErgebnisDaten ergebnisDaten) {
      super(locale, messages, kopfzahlLabelGenerator, wahldurchfuehrungsgebiet, ergebnisDaten);
    }

    @Override
    public String getGruppierung() {
      return getKopfzahlLabelGenerator().getWaehlerBezeichnungPlural(getWahldurchfuehrungsgebiet());
    }

    @Override
    public String getBezeichnung() {
      return "insgesamt";
    }

    @Override
    public String getKennung() {
      return getKopfzahlLabelGenerator().getAnzahlWaehlerGesamtKennung(getWahldurchfuehrungsgebiet());
    }

    @Override
    public Long getLongValue(ErgebnistabelleCsvDTO dto) {
      return getErgebnisDaten().getWaehlerAktuellGesamt(asGebietUndStimmabgabeart(dto));
    }
  }

  protected boolean existsMessage(String msgKey) {
    return getMessages().isMessageKeyVorhanden(msgKey);
  }

  protected String getMessage(String msgKey, Object... params) {
    return getMessages().getMessageForMessageKey(msgKey, params);
  }

  private Messages getMessages() {
    return messages;
  }

  protected static long nonNull(Long value) {
    return value == null ? 0L : value;
  }
}
