
package de.ivu.elect.business.ergebnis.control;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import de.ivu.elect.business.ergebnis.entity.Stimmabgabeart;
import de.ivu.elect.business.ergebnis.entity.Stimmenart;
import de.ivu.elect.business.gebietsbaum.entity.Wahldurchfuehrungsgebiet;
import de.ivu.elect.business.gruppe.entity.Gruppe;
import de.ivu.elect.business.gruppe.entity.NlSystemGruppe;
import de.ivu.elect.business.internet.control.GebietUndStimmabgabeart;

public class NlErgebnisDatenGueltige extends AbstractErgebnisDaten {

  private static final NlSystemGruppe BLANKO = NlSystemGruppe.BLANKO;

  private final NlErgebnisDaten nlErgebnisDaten;

  private final ErgebnisDatenGueltige ergebnisDatenGueltige;

  private final StimmenPool stimmen;

  private final StimmenKumulierer stimmenKumulierer;

  private final Optional<Gruppe> blankoGruppe;

  private final Map<GebietUndStimmabgabeart, Map<Stimmenart, Long>> blankoResult;

  public NlErgebnisDatenGueltige(NlErgebnisDaten nlErgebnisDaten, ErgebnisDatenGueltige ergebnisDatenGueltige) {
    super(nlErgebnisDaten.collectGebieteUntergeordnetFct(), nlErgebnisDaten.collectGebieteErfasstFct());

    this.nlErgebnisDaten = nlErgebnisDaten;

    this.ergebnisDatenGueltige = ergebnisDatenGueltige;

    this.stimmen = nlErgebnisDaten.getStimmen();
    this.stimmenKumulierer = nlErgebnisDaten.getStimmenKumulierer();

    this.blankoGruppe = nlErgebnisDaten.findSystemGruppe(BLANKO);
    this.blankoResult = new HashMap<>();
  }

  BestimmeStimmen<Stimmenart, GebietUndStimmabgabeart> blankoBestimmeStimmen = new BestimmeStimmen<>() {

    @Override
    public Long ermittleStimmen(Stimmenart stimmenart, Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet) {
      return blankoGruppe.map(gruppe -> stimmenKumulierer.getSummeFuerSystemGruppeAufGebietUndDarunter(gruppe, wahldurchfuehrungsgebiet, stimmen, stimmenart)).orElse(0L);
    }

    @Override
    public Map<Stimmenart, Long> getErgebnisUntergebiet(GebietUndStimmabgabeart gebietUndStimmabgabeart) {
      return getSummeBlanko(gebietUndStimmabgabeart);
    }

    @Override
    public List<Stimmenart> getTargetAufGebiet(GebietUndStimmabgabeart gebietUndStimmabgabeart) {
      return new ArrayList<>(BLANKO.getStimmenarten());
    }
  };

  /**
   * @return Summe der gültigen Listenstimmen auf dem Gebiet. BEACHTE, dass dies je nach Wahlart und Wahlebene entweder "gültige Stimmzettel" (GSB) oder "gültige Stimmen" (HSB, CSB) sein können!!!
   *
   * @see NlErgebnisDaten#getSummeGebietListenstimmen
   */
  public long getSummeGueltigeListenstimmen(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, Stimmabgabeart stimmabgabeart) {
    return getSummeGueltigeListenstimmen(new GebietUndStimmabgabeart(wahldurchfuehrungsgebiet, stimmabgabeart));
  }

  /**
   * @return Summe der gültigen Listenstimmen auf dem Gebiet gewichtet. BEACHTE, dass dies IMMER "gültige Stimmen" (== Summe der Listenstimmen aller Gruppen gewichtet) und NIE "gültige Stimmzettel"
   *         sind!!!
   *
   * @see NlErgebnisDaten#getSummeGebietListenstimmenGewichtetMitVoteValues
   */
  public long getSummeGueltigeListenstimmenGewichtetMitVoteValues(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, Stimmabgabeart stimmabgabeart) {
    return getSummeGueltigeListenstimmenGewichtetMitVoteValues(new GebietUndStimmabgabeart(wahldurchfuehrungsgebiet, stimmabgabeart));
  }

  /**
   * @return Summe der gültigen Listenstimmen auf dem Gebiet. BEACHTE, dass dies je nach Wahlart und Wahlebene entweder "gültige Stimmzettel" (GSB) oder "gültige Stimmen" (HSB, CSB) sein können!!!
   *
   * @see NlErgebnisDaten#getSummeGebietListenstimmen
   */
  public long getSummeGueltigeListenstimmenNurGesamtGemeinde(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet) {
    return getSummeGueltigeListenstimmenNurUrnenwahlGemeinde(wahldurchfuehrungsgebiet) + getSummeGueltigeListenstimmenNurBriefwahlGemeinde(wahldurchfuehrungsgebiet);
  }

  /**
   * @return Summe der gültigen Listenstimmen auf dem Gebiet gewichtet. BEACHTE, dass dies IMMER "gültige Stimmen" (== Summe der Listenstimmen aller Gruppen gewichtet) und NIE "gültige Stimmzettel"
   *         sind!!!
   *
   * @see NlErgebnisDaten#getSummeGebietListenstimmenGewichtetMitVoteValues
   */
  public long getSummeGueltigeListenstimmenNurGesamtGemeindeGewichtetMitVoteValues(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet) {
    return getSummeGueltigeListenstimmenNurUrnenwahlGemeindeGewichtetMitVoteValues(wahldurchfuehrungsgebiet)
        + getSummeGueltigeListenstimmenNurBriefwahlGemeindeGewichtetMitVoteValues(wahldurchfuehrungsgebiet);
  }

  /**
   * @return Summe der gültigen Listenstimmen auf dem Gebiet. BEACHTE, dass dies je nach Wahlart und Wahlebene entweder "gültige Stimmzettel" (GSB) oder "gültige Stimmen" (HSB, CSB) sein können!!!
   *
   * @see NlErgebnisDaten#getSummeGebietListenstimmen
   */
  public long getSummeGueltigeListenstimmenNurUrnenwahlGemeinde(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet) {
    return getSummeGueltigeListenstimmen(NlErgebnisDaten.createGebietUndStimmabgabeartNurUrnenwahlGemeinde(wahldurchfuehrungsgebiet));
  }

  /**
   * @return Summe der gültigen Listenstimmen auf dem Gebiet gewichtet. BEACHTE, dass dies IMMER "gültige Stimmen" (== Summe der Listenstimmen aller Gruppen gewichtet) und NIE "gültige Stimmzettel"
   *         sind!!!
   *
   * @see NlErgebnisDaten#getSummeGebietListenstimmenGewichtetMitVoteValues
   */
  public long getSummeGueltigeListenstimmenNurUrnenwahlGemeindeGewichtetMitVoteValues(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet) {
    return getSummeGueltigeListenstimmenGewichtetMitVoteValues(NlErgebnisDaten.createGebietUndStimmabgabeartNurUrnenwahlGemeinde(wahldurchfuehrungsgebiet));
  }

  /**
   * @return Summe der gültigen Listenstimmen auf dem Gebiet. BEACHTE, dass dies je nach Wahlart und Wahlebene entweder "gültige Stimmzettel" (GSB) oder "gültige Stimmen" (HSB, CSB) sein können!!!
   *
   * @see NlErgebnisDaten#getSummeGebietListenstimmen
   */
  public long getSummeGueltigeListenstimmenNurBriefwahlGemeinde(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet) {
    return getSummeGueltigeListenstimmen(NlErgebnisDaten.createGebietUndStimmabgabeartNurBriefwahlGemeinde(wahldurchfuehrungsgebiet));
  }

  /**
   * @return Summe der gültigen Listenstimmen auf dem Gebiet gewichtet. BEACHTE, dass dies IMMER "gültige Stimmen" (== Summe der Listenstimmen aller Gruppen gewichtet) und NIE "gültige Stimmzettel"
   *         sind!!!
   *
   * @see NlErgebnisDaten#getSummeGebietListenstimmenGewichtetMitVoteValues
   */
  public long getSummeGueltigeListenstimmenNurBriefwahlGemeindeGewichtetMitVoteValues(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet) {
    return getSummeGueltigeListenstimmenGewichtetMitVoteValues(NlErgebnisDaten.createGebietUndStimmabgabeartNurBriefwahlGemeinde(wahldurchfuehrungsgebiet));
  }

  /**
   * @return Summe der gültigen Listenstimmen auf dem Gebiet. BEACHTE, dass dies je nach Wahlart und Wahlebene entweder "gültige Stimmzettel" (GSB) oder "gültige Stimmen" (HSB, CSB) sein können!!!
   *
   * @see NlErgebnisDaten#getSummeGebietListenstimmen
   */
  public long getSummeGueltigeListenstimmenNurBriefwahlAusland(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet) {
    return getSummeGueltigeListenstimmen(NlErgebnisDaten.createGebietUndStimmabgabeartNurBriefwahlAusland(wahldurchfuehrungsgebiet));
  }

  /**
   * @return Summe der gültigen Listenstimmen auf dem Gebiet gewichtet. BEACHTE, dass dies IMMER "gültige Stimmen" (== Summe der Listenstimmen aller Gruppen gewichtet) und NIE "gültige Stimmzettel"
   *         sind!!!
   *
   * @see NlErgebnisDaten#getSummeGebietListenstimmenGewichtetMitVoteValues
   */
  public long getSummeGueltigeListenstimmenNurBriefwahlAuslandGewichtetMitVoteValues(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet) {
    return getSummeGueltigeListenstimmenGewichtetMitVoteValues(NlErgebnisDaten.createGebietUndStimmabgabeartNurBriefwahlAusland(wahldurchfuehrungsgebiet));
  }

  /**
   * @return Summe der gültigen Listenstimmen auf dem Gebiet. BEACHTE, dass dies je nach Wahlart und Wahlebene entweder "gültige Stimmzettel" (GSB) oder "gültige Stimmen" (HSB, CSB) sein können!!!
   *
   * @see NlErgebnisDaten#getSummeGebietListenstimmen
   */
  public long getSummeGueltigeListenstimmen(GebietUndStimmabgabeart gebietUndStimmabgabeart) {
    return notNull(ergebnisDatenGueltige.getZweitstimmenAktuellGueltig(gebietUndStimmabgabeart));
  }

  /**
   * @return Summe der gültigen Listenstimmen auf dem Gebiet gewichtet. BEACHTE, dass dies IMMER "gültige Stimmen" (== Summe der Listenstimmen aller Gruppen gewichtet) und NIE "gültige Stimmzettel"
   *         sind!!!
   *
   * @see NlErgebnisDaten#getSummeGebietListenstimmenGewichtetMitVoteValues
   */
  public long getSummeGueltigeListenstimmenGewichtetMitVoteValues(GebietUndStimmabgabeart gebietUndStimmabgabeart) {
    return nlErgebnisDaten.getSummeGebietListenstimmenGewichtetMitVoteValues(gebietUndStimmabgabeart);
  }

  /**
   * @return Summe der ungültigen Listenstimmen auf dem Gebiet. BEACHTE, dass dies je nach Wahlart und Wahlebene entweder "ungültige Stimmzettel" (GSB) oder "ungültige Stimmen" (HSB, CSB) sein
   *         können!!!
   */
  public long getSummeUngueltigeListenstimmen(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, Stimmabgabeart stimmabgabeart) {
    return getSummeUngueltigeListenstimmen(new GebietUndStimmabgabeart(wahldurchfuehrungsgebiet, stimmabgabeart));
  }

  /**
   * @return Summe der ungültigen Listenstimmen auf dem Gebiet. BEACHTE, dass dies je nach Wahlart und Wahlebene entweder "ungültige Stimmzettel" (GSB) oder "ungültige Stimmen" (HSB, CSB) sein
   *         können!!!
   */
  public long getSummeUngueltigeListenstimmenNurGesamtGemeinde(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet) {
    return getSummeUngueltigeListenstimmenNurUrnenwahlGemeinde(wahldurchfuehrungsgebiet) + getSummeUngueltigeListenstimmenNurBriefwahlGemeinde(wahldurchfuehrungsgebiet);
  }

  /**
   * @return Summe der ungültigen Listenstimmen auf dem Gebiet. BEACHTE, dass dies je nach Wahlart und Wahlebene entweder "ungültige Stimmzettel" (GSB) oder "ungültige Stimmen" (HSB, CSB) sein
   *         können!!!
   */
  public long getSummeUngueltigeListenstimmenNurUrnenwahlGemeinde(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet) {
    return getSummeUngueltigeListenstimmen(NlErgebnisDaten.createGebietUndStimmabgabeartNurUrnenwahlGemeinde(wahldurchfuehrungsgebiet));
  }

  /**
   * @return Summe der ungültigen Listenstimmen auf dem Gebiet. BEACHTE, dass dies je nach Wahlart und Wahlebene entweder "ungültige Stimmzettel" (GSB) oder "ungültige Stimmen" (HSB, CSB) sein
   *         können!!!
   */
  public long getSummeUngueltigeListenstimmenNurBriefwahlGemeinde(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet) {
    return getSummeUngueltigeListenstimmen(NlErgebnisDaten.createGebietUndStimmabgabeartNurBriefwahlGemeinde(wahldurchfuehrungsgebiet));
  }

  /**
   * @return Summe der ungültigen Listenstimmen auf dem Gebiet. BEACHTE, dass dies je nach Wahlart und Wahlebene entweder "ungültige Stimmzettel" (GSB) oder "ungültige Stimmen" (HSB, CSB) sein
   *         können!!!
   */
  public long getSummeUngueltigeListenstimmenNurBriefwahlAusland(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet) {
    return getSummeUngueltigeListenstimmen(NlErgebnisDaten.createGebietUndStimmabgabeartNurBriefwahlAusland(wahldurchfuehrungsgebiet));
  }

  /**
   * @return Summe der ungültigen Listenstimmen auf dem Gebiet. BEACHTE, dass dies je nach Wahlart und Wahlebene entweder "ungültige Stimmzettel" (GSB) oder "ungültige Stimmen" (HSB, CSB) sein
   *         können!!!
   */
  public long getSummeUngueltigeListenstimmen(GebietUndStimmabgabeart gebietUndStimmabgabeart) {
    return notNull(ergebnisDatenGueltige.getZweitstimmenAktuellUngueltig(gebietUndStimmabgabeart));
  }

  /**
   * @return Summe der leeren Listenstimmen auf dem Gebiet. BEACHTE, dass dies je nach Wahlart und Wahlebene entweder "leere Stimmzettel" (GSB) oder "leere Stimmen" (HSB, CSB) sein können!!!
   */
  public long getSummeBlankoListenstimmen(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, Stimmabgabeart stimmabgabeart) {
    return getSummeBlankoListenstimmen(new GebietUndStimmabgabeart(wahldurchfuehrungsgebiet, stimmabgabeart));
  }

  /**
   * @return Summe der leeren Listenstimmen auf dem Gebiet. BEACHTE, dass dies je nach Wahlart und Wahlebene entweder "leere Stimmzettel" (GSB) oder "leere Stimmen" (HSB, CSB) sein können!!!
   */
  public long getSummeBlankoListenstimmenNurGesamtGemeinde(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet) {
    return getSummeBlankoListenstimmenNurUrnenwahlGemeinde(wahldurchfuehrungsgebiet) + getSummeBlankoListenstimmenNurBriefwahlGemeinde(wahldurchfuehrungsgebiet);
  }

  /**
   * @return Summe der leeren Listenstimmen auf dem Gebiet. BEACHTE, dass dies je nach Wahlart und Wahlebene entweder "leere Stimmzettel" (GSB) oder "leere Stimmen" (HSB, CSB) sein können!!!
   */
  public long getSummeBlankoListenstimmenNurUrnenwahlGemeinde(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet) {
    return getSummeBlankoListenstimmen(NlErgebnisDaten.createGebietUndStimmabgabeartNurUrnenwahlGemeinde(wahldurchfuehrungsgebiet));
  }

  /**
   * @return Summe der leeren Listenstimmen auf dem Gebiet. BEACHTE, dass dies je nach Wahlart und Wahlebene entweder "leere Stimmzettel" (GSB) oder "leere Stimmen" (HSB, CSB) sein können!!!
   */
  public long getSummeBlankoListenstimmenNurBriefwahlGemeinde(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet) {
    return getSummeBlankoListenstimmen(NlErgebnisDaten.createGebietUndStimmabgabeartNurBriefwahlGemeinde(wahldurchfuehrungsgebiet));
  }

  /**
   * @return Summe der leeren Listenstimmen auf dem Gebiet. BEACHTE, dass dies je nach Wahlart und Wahlebene entweder "leere Stimmzettel" (GSB) oder "leere Stimmen" (HSB, CSB) sein können!!!
   */
  public long getSummeBlankoListenstimmenNurBriefwahlAusland(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet) {
    return getSummeBlankoListenstimmen(NlErgebnisDaten.createGebietUndStimmabgabeartNurBriefwahlAusland(wahldurchfuehrungsgebiet));
  }

  /**
   * @return Summe der leeren Listenstimmen auf dem Gebiet. BEACHTE, dass dies je nach Wahlart und Wahlebene entweder "leere Stimmzettel" (GSB) oder "leere Stimmen" (HSB, CSB) sein können!!!
   */
  public long getSummeBlankoListenstimmen(GebietUndStimmabgabeart gebietUndStimmabgabeart) {
    return getSummeBlanko(gebietUndStimmabgabeart, Stimmenart.LISTENSTIMME_LEER);
  }

  private long getSummeBlanko(GebietUndStimmabgabeart gebietUndStimmabgabeart, Stimmenart stimmenart) {
    return notNull(getSummeBlanko(gebietUndStimmabgabeart).get(stimmenart));
  }

  private Map<Stimmenart, Long> getSummeBlanko(GebietUndStimmabgabeart gebietUndStimmabgabeart) {
    return bestimmeStimmenOderSummiereAufUntergebieten(gebietUndStimmabgabeart, blankoResult, blankoBestimmeStimmen);
  }
}
