
package de.ivu.elect.dokumente;

import static org.apache.commons.lang3.StringUtils.isBlank;

import java.math.BigDecimal;
import java.util.ArrayDeque;
import java.util.Date;
import java.util.Deque;
import java.util.HashMap;
import java.util.Map;

import de.ivu.document.generator.template.BaseDocument;
import de.ivu.document.generator.template.annotations.DocumentComplexField;
import de.ivu.document.generator.template.annotations.DocumentSimpleField;
import de.ivu.elect.business.benutzerverwaltung.entity.Benutzer;
import de.ivu.elect.business.ergebnis.entity.Ergebnisart;
import de.ivu.elect.business.wahl.entity.Wahl;
import de.ivu.elect.business.wahl.entity.Wahlart;
import de.ivu.elect.business.wahl.entity.Wahlgang;
import de.ivu.elect.business.wahl.entity.Wahltermin;
import de.ivu.elect.dokumente.wahl.UmgangWaehlerDocument;
import de.ivu.elect.dokumente.wahl.UmgangWahlberechtigteDocument;

public class ElectDocument extends BaseDocument {

  private static final long serialVersionUID = 1L;

  @DocumentSimpleField("Aktuelles-Datum (Date-Objekt)")
  public final Date aktuellesDatumAlsDate;

  @DocumentSimpleField("Aktuelles-Datum-Formatiert (Standardformat)")
  public final String aktuellesDatum;

  @DocumentSimpleField("Aktuelles-Datum-Formatiert (Kurzformat)")
  public final String aktuellesDatumKurzFormat;

  @DocumentSimpleField("Aktuelles-Datum-Formatiert (Langformat)")
  public final String aktuellesDatumLangFormat;

  @DocumentSimpleField("Aktuelle-Uhrzeit")
  public final String aktuelleUhrzeit;

  @DocumentSimpleField("Aktuelle-Uhrzeit (mit Sekunden)")
  public final String aktuelleUhrzeitSekunden;

  @DocumentSimpleField("Aktueller-Wochentag (Name)")
  public final String aktuellerWochentagName;

  @DocumentSimpleField("Aktueller-Wochentag (Kürzel)")
  public final String aktuellerWochentagKuerzel;

  @DocumentSimpleField("Wahl-Name")
  public final String wahlName;

  @DocumentSimpleField("Wahl-Jahr")
  public final String wahlJahr;

  @DocumentSimpleField("Wahlart")
  public final String wahlart;

  @DocumentSimpleField("Wahlart-Schluessel)")
  public final String wahlartSchluessel;

  @DocumentSimpleField("Wahlart-Kurzname")
  public final String wahlartKurzname; // ELECT-8840

  @DocumentSimpleField("Wahlart-Kennzeichen")
  public final String wahlartKennzeichen; // ELECT-9731

  @DocumentComplexField("Wahlart-Umgang-Wahlberechtigte-Vorlaeufig")
  public final UmgangWahlberechtigteDocument wahlartUmgangWahlberechtigteVorlaeufig;

  @DocumentComplexField("Wahlart-Umgang-Wahlberechtigte-Endgueltig")
  public final UmgangWahlberechtigteDocument wahlartUmgangWahlberechtigteEndgueltig;

  @DocumentComplexField("Wahlart-Umgang-Waehler-Vorlaeufig")
  public final UmgangWaehlerDocument wahlartUmgangWaehlerVorlaeufig;

  @DocumentComplexField("Wahlart-Umgang-Waehler-Endgueltig")
  public final UmgangWaehlerDocument wahlartUmgangWaehlerEndgueltig;

  @DocumentSimpleField("Quorum/Prozenthuerde")
  public final BigDecimal quorum;

  @DocumentSimpleField("Wahltermin (Date-Objekt)")
  public final Date wahlterminAlsDate;

  @DocumentSimpleField("Wahltermin-Formatiert (Standardformat)")
  public final String wahltermin;

  @DocumentSimpleField("Wahltermin-Formatiert (Kurzformat)")
  public final String wahlterminKurzFormat;

  @DocumentSimpleField("Wahltermin-Formatiert (Langformat)")
  public final String wahlterminLangFormat;

  // Soll dazu dienen, dass man in Dokumenten einen Platzhalter einfügen kann, der dann durch nichts ersetzt wird. Als Text für den Platzhalter kann man einen Kommentar in das Dokument schreiben.
  @DocumentSimpleField("Entwicklerkommentar: Dies ersetzen")
  public final String entwicklerkommentar = "";

  @DocumentComplexField("Anwender")
  public final BenutzerDokument anwender;

  @DocumentSimpleField("format")
  public final DokumentFormatter format;

  @DocumentSimpleField("counter")
  public final DokumentCounter counter;

  @DocumentSimpleField("Erster Wahlgang")
  public final boolean ersterWahlgang;

  @DocumentSimpleField("Stichwahl")
  public final boolean stichwahl;

  // ELECT-9097
  @DocumentSimpleField("Stichwahl-Moeglich")
  public final boolean stichwahlMoeglich;

  @DocumentSimpleField("Wiederholungswahl")
  public final boolean wiederholungswahl;

  @DocumentSimpleField("Verbundene Wahl")
  public final boolean verbundeneWahl;

  @DocumentSimpleField("erzeuge kleines Dokument zum Testen")
  public final boolean generateSmallDocumentForTest;

  @DocumentComplexField("Parameter")
  public final CustomParameterDokument parameter;

  @DocumentSimpleField("ParameterFormat")
  public final CustomParameterDokumentFormatter parameterFormat;

  @DocumentSimpleField("stichtag.tageVorWahl(1)")
  public final ElectDocumentStichtag stichtag;

  @DocumentComplexField("Systemgebietsebene")
  public final SystemGebietsebeneDokument systemGebietsebene;

  private final Map<String, String> map = new HashMap<>(); // ELECT-12760

  private final Deque<String> stack = new ArrayDeque<>(); // ELECT-12760

  public ElectDocument(BaseElectContext baseElectContext) {
    super();

    this.parameter = baseElectContext.getCustomParameterDokument();
    this.parameterFormat = new CustomParameterDokumentFormatter(this.parameter);

    Date now = new Date();
    this.aktuellesDatumAlsDate = now;
    this.aktuellesDatum = baseElectContext.formatDateMedium(now);
    this.aktuellesDatumKurzFormat = baseElectContext.formatDateShort(now);
    this.aktuellesDatumLangFormat = baseElectContext.formatDateLong(now);

    this.aktuelleUhrzeit = baseElectContext.formatTimeHHmm(now);
    this.aktuelleUhrzeitSekunden = baseElectContext.formatTimeHHmmss(now);

    this.aktuellerWochentagName = baseElectContext.formatDatePattern(now, "EEEE");
    this.aktuellerWochentagKuerzel = baseElectContext.formatDatePattern(now, "EEE");

    Wahl wahl = baseElectContext.getWahl();
    Date wahlterminDatum;

    if (wahl != null) {
      Wahlart wahlart = wahl.getWahlart();

      this.wahlName = wahl.getName();
      this.wahlJahr = String.valueOf(wahl.getWahljahr());

      this.wahlart = wahlart.getNameGui();
      this.wahlartSchluessel = wahlart.getName();
      this.wahlartKurzname = wahlart.getKurzname();
      this.wahlartKennzeichen = wahlart.getKennzeichen();

      this.quorum = wahl.getQuorumProzenthuerde();

      Wahltermin wahltermin = wahl.getWahltermin();
      Wahlgang wahlgang = wahl.getWahlgang();

      wahlterminDatum = wahltermin.getWahldatum();

      this.verbundeneWahl = wahltermin.getWahlsByWahltermin().size() > 1;
      this.ersterWahlgang = wahlgang.isErsterWahlgang();
      this.stichwahl = wahlgang.isStichwahl();
      this.stichwahlMoeglich = wahltermin.isStichwahlMoeglich();
      this.wiederholungswahl = wahlgang.isWiederholungswahl();
    } else {
      Wahltermin wahltermin = baseElectContext.getWahltermin();

      if (wahltermin != null) {
        this.wahlName = wahltermin.getName();
        this.wahlJahr = String.valueOf(wahltermin.getWahljahr());

        this.wahlart = "";
        this.wahlartSchluessel = wahltermin.getName();
        this.wahlartKurzname = wahltermin.getName();
        this.wahlartKennzeichen = "";

        this.quorum = null;

        wahlterminDatum = wahltermin.getWahldatum();

        this.verbundeneWahl = wahltermin.getWahlsByWahltermin().size() > 1;
        this.ersterWahlgang = !wahltermin.isStichwahltermin();
        this.stichwahl = wahltermin.isStichwahltermin();
        this.stichwahlMoeglich = wahltermin.isStichwahlMoeglich();
        this.wiederholungswahl = false;
      } else {
        this.wahlName = "";
        this.wahlJahr = "";

        this.wahlart = "";
        this.wahlartSchluessel = "";
        this.wahlartKurzname = "";
        this.wahlartKennzeichen = "";

        this.quorum = null;

        wahlterminDatum = now;

        this.verbundeneWahl = false;
        this.ersterWahlgang = false;
        this.stichwahl = false;
        this.stichwahlMoeglich = false;
        this.wiederholungswahl = false;
      }
    }

    // ELECT-13015
    this.wahlartUmgangWahlberechtigteVorlaeufig = new UmgangWahlberechtigteDocument(baseElectContext, Ergebnisart.VORLAEUFIG);
    this.wahlartUmgangWahlberechtigteEndgueltig = new UmgangWahlberechtigteDocument(baseElectContext, Ergebnisart.ENDGUELTIG);

    // ELECT-13015
    this.wahlartUmgangWaehlerVorlaeufig = new UmgangWaehlerDocument(baseElectContext, Ergebnisart.VORLAEUFIG);
    this.wahlartUmgangWaehlerEndgueltig = new UmgangWaehlerDocument(baseElectContext, Ergebnisart.ENDGUELTIG);

    this.wahlterminAlsDate = wahlterminDatum;
    this.wahltermin = baseElectContext.formatDateMedium(wahlterminDatum);
    this.wahlterminKurzFormat = baseElectContext.formatDateShort(wahlterminDatum);
    this.wahlterminLangFormat = baseElectContext.formatDateLong(wahlterminDatum);

    this.stichtag = new ElectDocumentStichtag(baseElectContext, wahlterminDatum);

    Benutzer benutzer = baseElectContext.getAnwender();

    if (benutzer == null) {
      this.anwender = BenutzerDokument.EMPTY_OBJECT;
    } else {
      this.anwender = new BenutzerDokument(benutzer);
    }

    this.format = new DokumentFormatter(baseElectContext.getLocale());

    this.counter = new DokumentCounter();

    this.generateSmallDocumentForTest = baseElectContext.isGenerateSmallDocumentForTest();

    this.systemGebietsebene = new SystemGebietsebeneDokument(baseElectContext);
  }

  protected Long sumNullToZero(Long one, Long another) {
    return fixNullToZero(one) + fixNullToZero(another);
  }

  protected Long fixNullToZero(Long value) {
    if (value == null) {
      return 0L;
    }

    return value;
  }

  public Map<String, String> getMap() {
    return map;
  }

  public Deque<String> getStack() {
    return stack;
  }

  public String addMap(String key, String value) {
    if (key != null) {
      map.put(key, value);
    }

    return "";
  }

  public String getMap(String key) {
    if (key == null) {
      return "";
    }

    String value = map.get(key);

    if (isBlank(value)) {
      return "";
    }

    return value;
  }

  public String pushStack(String value) {
    stack.addFirst(value);

    return "";
  }

  public String pollStack() {
    try {
      String result = stack.removeFirst();

      if (isBlank(result)) {
        return "";
      }

      return result;
    } catch (Exception ignored) {
    }

    return "";
  }

  public String peekStack() {
    try {
      String result = stack.peekFirst();

      if (isBlank(result)) {
        return "";
      }

      return result;
    } catch (Exception ignored) {
    }

    return "";
  }
}
