
package de.ivu.elect.presentation.ergebnis.dokumente.gesamtergebnis;

import java.math.BigDecimal;
import java.util.Objects;

import javax.annotation.Nullable;

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

import de.ivu.document.generator.template.annotations.DocumentComplexField;
import de.ivu.document.generator.template.annotations.DocumentSimpleField;
import de.ivu.elect.business.ergebnis.control.ErgebnisDaten;
import de.ivu.elect.business.ergebnis.entity.Stimmabgabeart;
import de.ivu.elect.business.gebietsbaum.entity.Gebietsgruppierung;
import de.ivu.elect.business.gebietsbaum.entity.Wahldurchfuehrungsgebiet;
import de.ivu.elect.business.gruppe.entity.PolitischeGruppe;
import de.ivu.elect.business.internet.control.GebietUndStimmabgabeart;
import de.ivu.elect.dokumente.BaseElectContext;
import de.ivu.elect.dokumente.DokumentFormatter;
import de.ivu.elect.dokumente.gebiet.GebietDokument;
import de.ivu.elect.presentation.ergebnis.dokumente.gesamtergebnis.berechner.GueltigeErstUndZweitstimmenAktuellMitVorperiodeBerechner;
import de.ivu.elect.presentation.ergebnis.dokumente.gesamtergebnis.berechner.UngueltigeErstUndZweitstimmenAktuellMitVorperiodeBerechner;
import de.ivu.elect.presentation.system.PercentUtil;
import de.ivu.elect.util.NotNull;

/**
 * on 13.02.2017.
 */
public class AbstractGebietMitErgebnisdatenInklVorperiodeDokument extends GebietDokument {

  private static final Logger LOG = LoggerFactory.getLogger(AbstractGebietMitErgebnisdatenInklVorperiodeDokument.class);

  @Nullable
  private final Gebietsgruppierung gebietsgruppierung;

  @DocumentSimpleField("GebietsgruppierungName")
  public final String gebietsgruppierungName;

  @DocumentSimpleField("GebietsgruppierungNummer")
  public final String gebietsgruppierungNummer;

  @DocumentSimpleField("GebietsgruppierungArt")
  public final String gebietsgruppierungArt;

  @DocumentSimpleField("GebietsgruppierungVerwaltungsart")
  public final String gebietsgruppierungVerwaltungart;

  @DocumentComplexField("Wahlberechtigte")
  public final WahlberechtigteMitVorperiodeAufGebiet wahlberechtigte;

  @DocumentComplexField("Wähler")
  public final WaehlerUndWahlbeteiligungMitVorperiodeAufGebiet waehler;

  @DocumentComplexField("AbgegebeneStimmen")
  public final StimmenMitVorperiodeAufGebiet abgegebeneStimmen;

  /**
   * Umfasst alle Wahlvorschläge, die sich nicht um Zweitstimmen bewerben, also insbes. Einzelbewerber, Wählergruppen
   * und in der Vorperiode die Parteien, die nicht mehr antreten.
   */
  @DocumentComplexField("Stimmen von Gruppen mit ausschließlich Erststimmen")
  public final StimmenMitVorperiodeAufGebiet gruppenMitAusschlErststimmenStimmen;

  public AbstractGebietMitErgebnisdatenInklVorperiodeDokument(WahlbuchParameter parameter,
                                                              Wahldurchfuehrungsgebiet gebiet, @Nullable Gebietsgruppierung gebietsgruppierung) {

    super(parameter.getContext(), gebiet);

    LOG.info("create AbstractGebietMitErgebnisdatenInklVorperiodeDokument");

    this.gebietsgruppierung = gebietsgruppierung;
    this.gebietsgruppierungName = getGebietsgruppierungName(gebietsgruppierung);
    this.gebietsgruppierungArt = getGebietsgruppierungArt(gebietsgruppierung);
    this.gebietsgruppierungNummer = getGebietsgruppierungNummer(gebietsgruppierung);
    this.gebietsgruppierungVerwaltungart = getGebietsgruppierungVerwaltungart(gebietsgruppierung, parameter.getContext());

    GebietUndStimmabgabeart gebietUndStimmabgabeart = new GebietUndStimmabgabeart(gebiet, gebietsgruppierung,
        Stimmabgabeart.GESAMT);

    wahlberechtigte = createWahlberechtigteMitVorperiodeAufGebiet(parameter, gebietUndStimmabgabeart);
    waehler = createWaehlerUndWahlbeteiligungMitVorperiodeAufGebiet(parameter, gebietUndStimmabgabeart);
    abgegebeneStimmen = erstelleAbgegebeneStimmen(parameter, gebietUndStimmabgabeart);

    gruppenMitAusschlErststimmenStimmen = bestimmeSummeGruppenMitAusschlErststimmenAufGebiet(parameter, gebietUndStimmabgabeart);

    LOG.info("create AbstractGebietMitErgebnisdatenInklVorperiodeDokument - done");
  }

  public boolean equalsGebietsgruppierung(Gebietsgruppierung gebietsgruppierung) {
    return Objects.equals(this.gebietsgruppierung, gebietsgruppierung);
  }

  public long getAnzahlWaehler() {
    return NotNull.notNull(this.waehler.waehlerGesamt.anzahl);
  }

  public long getAnzahlWahlberechtigte() {
    return NotNull.notNull(this.wahlberechtigte.wahlberechtigteGesamt.anzahl);
  }

  public String getWahlbeteiligungProzentFormatiert() {
    return waehler.waehlerGesamt.prozentFormatiert;
  }

  public String getWahlbeteiligungDifferenzVorperiodeFormatiert(DokumentFormatter formatter) {
    BigDecimal differenz = PercentUtil.percentDifference(wahlberechtigte.wahlberechtigteGesamt.anzahl,
        waehler.waehlerGesamt.anzahl, wahlberechtigte.wahlberechtigteGesamtVorperiode.anzahl,
        waehler.waehlerGesamtVorperiode.anzahl);

    // Hier wird noch das Vorzeichen des Prozent-Werts ermittelt, damit dies ggf. bei der Darstellung berücksichtigt werden kann
    int differenzVorzeichen = PercentUtil.percentDifferenceSignum(wahlberechtigte.wahlberechtigteGesamt.anzahl,
        waehler.waehlerGesamt.anzahl, wahlberechtigte.wahlberechtigteGesamtVorperiode.anzahl,
        waehler.waehlerGesamtVorperiode.anzahl);

    return formatter.formatStimmanteilDifferenz(differenz, Long.valueOf(differenzVorzeichen));
  }

  private String getGebietsgruppierungArt(@Nullable Gebietsgruppierung gebietsgruppierung) {
    return gebietsgruppierung == null ? "" : gebietsgruppierung.getGebietsgruppierungArt();
  }

  private String getGebietsgruppierungName(@Nullable Gebietsgruppierung gebietsgruppierung) {
    return gebietsgruppierung == null ? "" : gebietsgruppierung.getName();
  }

  private String getGebietsgruppierungNummer(@Nullable Gebietsgruppierung gebietsgruppierung) {
    return gebietsgruppierung == null ? "" : gebietsgruppierung.getName();
  }

  private String getGebietsgruppierungVerwaltungart(Gebietsgruppierung gebietsgruppierung, BaseElectContext context) {
    return gebietsgruppierung == null ? "" : context.getMessage(gebietsgruppierung.getVerwaltungsart());
  }

  public WahlberechtigteMitVorperiodeAufGebiet createWahlberechtigteMitVorperiodeAufGebiet(WahlbuchParameter parameter,
                                                                                           GebietUndStimmabgabeart gebietUndStimmabgabeart) {
    return new WahlberechtigteMitVorperiodeAufGebiet(parameter, gebietUndStimmabgabeart);
  }

  public WaehlerUndWahlbeteiligungMitVorperiodeAufGebiet createWaehlerUndWahlbeteiligungMitVorperiodeAufGebiet(
      WahlbuchParameter parameter, GebietUndStimmabgabeart gebietUndStimmabgabeart) {
    return new WaehlerUndWahlbeteiligungMitVorperiodeAufGebiet(parameter.getErgebnisDaten(), gebietUndStimmabgabeart,
        parameter.getFormatter());
  }

  public StimmenMitVorperiodeAufGebiet erstelleAbgegebeneStimmen(WahlbuchParameter parameter,
                                                                 GebietUndStimmabgabeart g) {
    return new StimmenMitVorperiodeAufGebiet(createAbgegebeneStimmen(parameter.getErgebnisDaten(), g).ohneBezug(),
        parameter.getFormatter());
  }

  protected StimmenAktuellMitVorperiode createAbgegebeneStimmen(ErgebnisDaten ergebnisDaten,
                                                                GebietUndStimmabgabeart gebietUndStimmabgabeart) {
    StimmenAktuellMitVorperiode ungueltige = createUngueltigeStimmen(ergebnisDaten, gebietUndStimmabgabeart);
    StimmenAktuellMitVorperiode gueltige = createGueltigeStimmen(ergebnisDaten, gebietUndStimmabgabeart);
    return ungueltige.plus(gueltige);
  }

  protected StimmenAktuellMitVorperiode createUngueltigeStimmen(ErgebnisDaten ergebnisDaten,
                                                                GebietUndStimmabgabeart gebietUndStimmabgabeart) {
    return new UngueltigeErstUndZweitstimmenAktuellMitVorperiodeBerechner(ergebnisDaten, gebietUndStimmabgabeart)
        .getStimmenAktuellMitVorperiode();
  }

  protected StimmenAktuellMitVorperiode createGueltigeStimmen(ErgebnisDaten ergebnisDaten,
                                                              GebietUndStimmabgabeart gebietUndStimmabgabeart) {
    return new GueltigeErstUndZweitstimmenAktuellMitVorperiodeBerechner(ergebnisDaten, gebietUndStimmabgabeart)
        .getStimmenAktuellMitVorperiode();
  }

  protected StimmenMitVorperiodeAufGebiet bestimmeSummeGruppenMitAusschlErststimmenAufGebiet(
      WahlbuchParameter parameter, GebietUndStimmabgabeart gebietUndStimmabgabeart) {
    StimmenAktuellMitVorperiode gruppenStimmen = summiereStimmenGruppenMitAusschlErststimmen(parameter,
        gebietUndStimmabgabeart);

    // Die folgenden 4 Werte dienen als Bezugswerte
    StimmenAktuellMitVorperiode gueltigeGesamt = createGueltigeStimmen(parameter.getErgebnisDaten(),
        gebietUndStimmabgabeart);

    return new StimmenMitVorperiodeAufGebiet(gruppenStimmen, gueltigeGesamt, parameter.getFormatter());
  }

  protected StimmenAktuellMitVorperiode summiereStimmenGruppenMitAusschlErststimmen(WahlbuchParameter parameter,
                                                                                    GebietUndStimmabgabeart gebietUndStimmabgabeart) {

    ErgebnisDaten ergebnisDaten = parameter.getErgebnisDaten();
    Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet = gebietUndStimmabgabeart.getWahldurchfuehrungsgebiet();

    // Summiere diese vier Werte über alle Parteien, die zugelassene Direktkandidaten haben, aber keine Liste
    // WASDE-2084: Die Ergebnisse der Parteien, die nur mit Erststimmen angetreten sind, sollten im Heft auch einzeln aufgeführt und nicht zu Übrige gezählt werden.
    StimmenAktuellMitVorperiode gruppenStimmen = new StimmenAktuellMitVorperiode();
    for (PolitischeGruppe politischeGruppe : ergebnisDaten
        .getPolitischeGruppeMitZugelassenenDirektkandidatenInGebietshierarchie(wahldurchfuehrungsgebiet)) {
      if (!politischeGruppe.isPartei() && !ergebnisDaten.hatListeInGebietshierarchie(politischeGruppe, wahldurchfuehrungsgebiet)) {
        addGruppenStimmen(ergebnisDaten, gruppenStimmen, gebietUndStimmabgabeart, politischeGruppe);
      }
    }
    return gruppenStimmen;
  }

  protected void addGruppenStimmen(ErgebnisDaten ergebnisDaten, StimmenAktuellMitVorperiode gruppenStimmen,
                                   GebietUndStimmabgabeart gebietUndStimmabgabeart, PolitischeGruppe politischeGruppe) {
    addGruppenStimmen(ergebnisDaten, gruppenStimmen, gebietUndStimmabgabeart, politischeGruppe, true, true);
  }

  protected void addGruppenStimmen(ErgebnisDaten ergebnisDaten, StimmenAktuellMitVorperiode gruppenStimmen,
                                   GebietUndStimmabgabeart gebietUndStimmabgabeart, PolitischeGruppe politischeGruppe, boolean mitAktuellenStimmen,
                                   boolean mitVorperiodeStimmen) {

    if (mitAktuellenStimmen) {
      gruppenStimmen.addErststimmen(ergebnisDaten.getErststimmenAktuell(gebietUndStimmabgabeart).get(politischeGruppe));
      gruppenStimmen.addZweitstimmen(ergebnisDaten.getZweitstimmenAktuell(gebietUndStimmabgabeart).get(politischeGruppe));
    }
    if (mitVorperiodeStimmen) {
      gruppenStimmen.addErststimmenVorperiode(
          ergebnisDaten.getErststimmenVorperiode(gebietUndStimmabgabeart).get(politischeGruppe));
      gruppenStimmen.addZweitstimmenVorperiode(
          ergebnisDaten.getZweitstimmenVorperiode(gebietUndStimmabgabeart).get(politischeGruppe));
    }
  }

}
