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

import static java.util.Arrays.asList;

import java.util.List;
import java.util.Map;
import java.util.function.Function;

import javax.annotation.Nullable;

import de.ivu.elect.business.ergebnis.entity.Ergebnis;
import de.ivu.elect.business.ergebnis.entity.Stimmenart;
import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
import de.ivu.elect.business.internet.control.GebietUndStimmabgabeart;
import de.ivu.elect.business.internet.control.PolitischeGruppeUndGebietUndStimmabgabeart;
import de.ivu.elect.business.stimmeingabe.boundary.StimmeingabeErfassenDescription;
import de.ivu.elect.business.wahl.entity.Wahl;
import de.ivu.elect.business.wahl.entity.WahlartErgebnisart;
import de.ivu.elect.business.wahlvorschlag.entity.Wahlvorschlag;

public abstract class AbstractErgebnisDatenGueltige extends AbstractErgebnisDaten {

  protected static final List<Stimmenart> TARGET_STIMMENARTEN_GUELTIGE = asList(Stimmenart.DIREKTSTIMME, Stimmenart.LISTENSTIMME, Stimmenart.REFERENDUM_STIMME);

  protected final StimmergebnisDaten stimmergebnisDaten;

  protected final StimmenKumulierer stimmenKumulierer;

  protected final StimmenPool stimmenPoolAktuell;

  protected final StimmenPool stimmenPoolVorperiode;

  protected final StimmeingabeErfassenDescription stimmeingabeErfassenDescription;

  protected final Wahl wahl;

  protected final WahlartErgebnisart ergebnisart;

  public AbstractErgebnisDatenGueltige(StimmergebnisDaten stimmergebnisDaten) {
    super(stimmergebnisDaten::getUntergeordneteGebiete, stimmergebnisDaten::getErfassteGebiete);

    this.stimmergebnisDaten = stimmergebnisDaten;

    this.stimmenKumulierer = stimmergebnisDaten.getStimmenKumulierer();

    this.stimmenPoolAktuell = stimmergebnisDaten.getStimmen();
    this.stimmenPoolVorperiode = stimmergebnisDaten.getStimmenVorperiodeAlsPool();

    this.stimmeingabeErfassenDescription = stimmergebnisDaten.getStimmeingabeErfassenDescription();

    Ergebnis ergebnis = stimmergebnisDaten.getErgebnis();

    this.wahl = ergebnis.getWahl();
    this.ergebnisart = ergebnis.getErgebnisart();
  }

  protected Long berechnePerStimmenart(GebietUndStimmabgabeart gebietUndStimmabgabeart, Stimmenart stimmenart, Map<GebietUndStimmabgabeart, Map<Stimmenart, Long>> cache,
      BestimmeStimmen<Stimmenart, GebietUndStimmabgabeart> bestimme) {
    return berechnePerStimmenart(gebietUndStimmabgabeart, cache, bestimme).get(stimmenart);
  }

  protected Map<Stimmenart, Long> berechnePerStimmenart(GebietUndStimmabgabeart gebietUndStimmabgabeart, Map<GebietUndStimmabgabeart, Map<Stimmenart, Long>> cache,
      BestimmeStimmen<Stimmenart, GebietUndStimmabgabeart> bestimme) {
    return bestimmeStimmenOderSummiereAufUntergebieten(gebietUndStimmabgabeart, cache, bestimme);
  }

  protected Long berechnePerWahlvorschlag(GebietUndStimmabgabeart gebietUndStimmabgabeart, Wahlvorschlag wahlvorschlag, Map<PolitischeGruppeUndGebietUndStimmabgabeart, Map<Wahlvorschlag, Long>> cache,
      BestimmeStimmen<Wahlvorschlag, PolitischeGruppeUndGebietUndStimmabgabeart> bestimme) {
    return berechnePerWahlvorschlag(new PolitischeGruppeUndGebietUndStimmabgabeart(wahlvorschlag.getGruppe(), gebietUndStimmabgabeart), cache, bestimme).get(wahlvorschlag);
  }

  protected Map<Wahlvorschlag, Long> berechnePerWahlvorschlag(PolitischeGruppeUndGebietUndStimmabgabeart politischeGruppeUndGebietUndStimmabgabeart,
      Map<PolitischeGruppeUndGebietUndStimmabgabeart, Map<Wahlvorschlag, Long>> cache, BestimmeStimmen<Wahlvorschlag, PolitischeGruppeUndGebietUndStimmabgabeart> bestimme) {
    return bestimmeStimmenOderSummiereAufUntergebieten(politischeGruppeUndGebietUndStimmabgabeart, cache, bestimme);
  }

  protected Long sum(@Nullable Long one, @Nullable Long another) {
    return sumOrDefault(one, another, null);
  }

  protected Long sumOrDefault(@Nullable Long one, @Nullable Long another, Long defaultValue) {
    if (one == null && another == null) {
      return defaultValue;
    }

    if (one == null) {
      return another;
    }

    if (another == null) {
      return one;
    }

    return one + another;
  }

  protected Function<Gebiet, Long> berechneSummeWaehlerGesamt(StimmenPool stimmenPool) {
    return gebiet -> stimmenKumulierer.getSummeWaehlerGesamtStimmenAufGebiet(stimmenPool, gebiet);
  }

  protected Function<Gebiet, Long> berechneSummeGueltige(StimmenPool stimmenPool, Stimmenart stimmenart) {
    return gebiet -> stimmenKumulierer.getSummeGueltigeStimmenAufGebiet(stimmenPool, gebiet, stimmenart);
  }

  protected Function<Gebiet, Long> berechneSummeUngueltige(StimmenPool stimmenPool, Stimmenart stimmenart) {
    return gebiet -> stimmenKumulierer.getSummeUngueltigeStimmenAufGebiet(stimmenPool, gebiet, stimmenart);
  }
}
