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

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

import de.ivu.elect.business.ergebnis.entity.Stimmenart;
import de.ivu.elect.business.gebietsbaum.entity.Wahldurchfuehrungsgebiet;
import de.ivu.elect.business.internet.control.GebietUndStimmabgabeart;
import de.ivu.elect.business.wahl.entity.UmgangGueltigeWerte;

public class ErgebnisDatenGueltige extends AbstractErgebnisDatenGueltige {

  public ErgebnisDatenGueltige(StimmergebnisDaten stimmergebnisDaten) {
    super(stimmergebnisDaten);
  }

  public Long getErststimmenAktuellGueltig(GebietUndStimmabgabeart gebietUndStimmabgabeart) {
    return berechneErststimmen(gebietUndStimmabgabeart, cacheStimmenAktuellGueltig, bestimmeStimmenAktuellGueltig);
  }

  public Long getErststimmenAktuellUngueltig(GebietUndStimmabgabeart gebietUndStimmabgabeart) {
    return berechneErststimmen(gebietUndStimmabgabeart, cacheStimmenAktuellUngueltig, bestimmeStimmenAktuellUngueltig);
  }

  public Long getErststimmenAktuellGesamt(GebietUndStimmabgabeart gebietUndStimmabgabeart) {
    return sum(getErststimmenAktuellGueltig(gebietUndStimmabgabeart), getErststimmenAktuellUngueltig(gebietUndStimmabgabeart));
  }

  public Long getErststimmenVorperiodeGueltig(GebietUndStimmabgabeart gebietUndStimmabgabeart) {
    return berechneErststimmen(gebietUndStimmabgabeart, cacheStimmenVorperiodeGueltig, bestimmeStimmenVorperiodeGueltig);
  }

  public Long getErststimmenVorperiodeUngueltig(GebietUndStimmabgabeart gebietUndStimmabgabeart) {
    return berechneErststimmen(gebietUndStimmabgabeart, cacheStimmenVorperiodeUngueltig, bestimmeStimmenVorperiodeUngueltig);
  }

  public Long getErststimmenVorperiodeGesamt(GebietUndStimmabgabeart gebietUndStimmabgabeart) {
    return sum(getErststimmenVorperiodeGueltig(gebietUndStimmabgabeart), getErststimmenVorperiodeUngueltig(gebietUndStimmabgabeart));
  }

  public Long getZweitstimmenAktuellGueltig(GebietUndStimmabgabeart gebietUndStimmabgabeart) {
    return berechneZweitstimmen(gebietUndStimmabgabeart, cacheStimmenAktuellGueltig, bestimmeStimmenAktuellGueltig);
  }

  public Long getZweitstimmenAktuellUngueltig(GebietUndStimmabgabeart gebietUndStimmabgabeart) {
    return berechneZweitstimmen(gebietUndStimmabgabeart, cacheStimmenAktuellUngueltig, bestimmeStimmenAktuellUngueltig);
  }

  public Long getZweitstimmenAktuellGesamt(GebietUndStimmabgabeart gebietUndStimmabgabeart) {
    return sum(getZweitstimmenAktuellGueltig(gebietUndStimmabgabeart), getZweitstimmenAktuellUngueltig(gebietUndStimmabgabeart));
  }

  public Long getZweitstimmenVorperiodeGueltig(GebietUndStimmabgabeart gebietUndStimmabgabeart) {
    return berechneZweitstimmen(gebietUndStimmabgabeart, cacheStimmenVorperiodeGueltig, bestimmeStimmenVorperiodeGueltig);
  }

  public Long getZweitstimmenVorperiodeUngueltig(GebietUndStimmabgabeart gebietUndStimmabgabeart) {
    return berechneZweitstimmen(gebietUndStimmabgabeart, cacheStimmenVorperiodeUngueltig, bestimmeStimmenVorperiodeUngueltig);
  }

  public Long getZweitstimmenVorperiodeGesamt(GebietUndStimmabgabeart gebietUndStimmabgabeart) {
    return sum(getZweitstimmenVorperiodeGueltig(gebietUndStimmabgabeart), getZweitstimmenVorperiodeUngueltig(gebietUndStimmabgabeart));
  }

  public Long getGesamtstimmenAktuellGueltig(GebietUndStimmabgabeart gebietUndStimmabgabeart) {
    return berechneGesamtstimmen(gebietUndStimmabgabeart, cacheGesamtstimmenAktuellGueltig, this::getErststimmenAktuellGueltig, this::getZweitstimmenAktuellGueltig);
  }

  public Long getGesamtstimmenAktuellUngueltig(GebietUndStimmabgabeart gebietUndStimmabgabeart) {
    return berechneGesamtstimmen(gebietUndStimmabgabeart, cacheGesamtstimmenAktuellUngueltig, this::getErststimmenAktuellUngueltig, this::getZweitstimmenAktuellUngueltig);
  }

  public Long getGesamtstimmenAktuellGesamt(GebietUndStimmabgabeart gebietUndStimmabgabeart) {
    return sum(getGesamtstimmenAktuellGueltig(gebietUndStimmabgabeart), getGesamtstimmenAktuellUngueltig(gebietUndStimmabgabeart));
  }

  public Long getGesamtstimmenVorperiodeGueltig(GebietUndStimmabgabeart gebietUndStimmabgabeart) {
    return berechneGesamtstimmen(gebietUndStimmabgabeart, cacheGesamtstimmenVorperiodeGueltig, this::getErststimmenVorperiodeGueltig, this::getZweitstimmenVorperiodeGueltig);
  }

  public Long getGesamtstimmenVorperiodeUngueltig(GebietUndStimmabgabeart gebietUndStimmabgabeart) {
    return berechneGesamtstimmen(gebietUndStimmabgabeart, cacheGesamtstimmenVorperiodeUngueltig, this::getErststimmenVorperiodeUngueltig, this::getZweitstimmenVorperiodeUngueltig);
  }

  public Long getGesamtstimmenVorperiodeGesamt(GebietUndStimmabgabeart gebietUndStimmabgabeart) {
    return sum(getGesamtstimmenVorperiodeGueltig(gebietUndStimmabgabeart), getGesamtstimmenVorperiodeUngueltig(gebietUndStimmabgabeart));
  }

  private Long berechneErststimmen(GebietUndStimmabgabeart gebietUndStimmabgabeart, Map<GebietUndStimmabgabeart, Map<Stimmenart, Long>> cache,
      BestimmeStimmen<Stimmenart, GebietUndStimmabgabeart> bestimmeStimmen) {
    return berechneStimmen(gebietUndStimmabgabeart, Stimmenart.DIREKTSTIMME, cache, bestimmeStimmen);
  }

  private Long berechneZweitstimmen(GebietUndStimmabgabeart gebietUndStimmabgabeart, Map<GebietUndStimmabgabeart, Map<Stimmenart, Long>> cache,
      BestimmeStimmen<Stimmenart, GebietUndStimmabgabeart> bestimmeStimmen) {
    return berechneStimmen(gebietUndStimmabgabeart, Stimmenart.LISTENSTIMME, cache, bestimmeStimmen);
  }

  private Long berechneGesamtstimmen(GebietUndStimmabgabeart gebietUndStimmabgabeart, Map<GebietUndStimmabgabeart, Long> cache, Function<GebietUndStimmabgabeart, Long> anzahlDirektstimmenFct,
      Function<GebietUndStimmabgabeart, Long> anzahlListenstimmenFct) {
    if (!cache.containsKey(gebietUndStimmabgabeart)) {
      cache.put(gebietUndStimmabgabeart, sum(anzahlDirektstimmenFct.apply(gebietUndStimmabgabeart), anzahlListenstimmenFct.apply(gebietUndStimmabgabeart)));
    }

    return cache.get(gebietUndStimmabgabeart);
  }

  private Long berechneStimmen(GebietUndStimmabgabeart gebietUndStimmabgabeart, Stimmenart stimmenart, Map<GebietUndStimmabgabeart, Map<Stimmenart, Long>> cache,
      BestimmeStimmen<Stimmenart, GebietUndStimmabgabeart> bestimmeStimmen) {
    return berechnePerStimmenart(gebietUndStimmabgabeart, stimmenart, cache, bestimmeStimmen);
  }

  private Long ermittleStimmenGueltig(StimmenPool stimmenPool, Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, Stimmenart stimmenart) {
    return UmgangGueltigeWerte.berechneStimmenGueltig(wahl, ergebnisart, wahldurchfuehrungsgebiet, berechneSummeWaehlerGesamt(stimmenPool), berechneSummeGueltige(stimmenPool, stimmenart),
        berechneSummeUngueltige(stimmenPool, stimmenart));
  }

  private Long ermittleStimmenUngueltig(StimmenPool stimmenPool, Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, Stimmenart stimmenart) {
    return UmgangGueltigeWerte.berechneStimmenUngueltig(wahl, ergebnisart, wahldurchfuehrungsgebiet, berechneSummeWaehlerGesamt(stimmenPool), berechneSummeGueltige(stimmenPool, stimmenart),
        berechneSummeUngueltige(stimmenPool, stimmenart));
  }

  private final Map<GebietUndStimmabgabeart, Long> cacheGesamtstimmenAktuellGueltig = new HashMap<>();

  private final Map<GebietUndStimmabgabeart, Map<Stimmenart, Long>> cacheStimmenAktuellGueltig = new HashMap<>();

  private final BestimmeStimmen<Stimmenart, GebietUndStimmabgabeart> bestimmeStimmenAktuellGueltig = new BestimmeStimmen<Stimmenart, GebietUndStimmabgabeart>() {

    @Override
    public Long ermittleStimmen(Stimmenart stimmenart, Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet) {
      return ermittleStimmenGueltig(stimmenPoolAktuell, wahldurchfuehrungsgebiet, stimmenart);
    }

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

    @Override
    public List<Stimmenart> getTargetAufGebiet(GebietUndStimmabgabeart gebietUndStimmabgabeart) {
      return TARGET_STIMMENARTEN_GUELTIGE;
    }
  };

  private final Map<GebietUndStimmabgabeart, Long> cacheGesamtstimmenAktuellUngueltig = new HashMap<>();

  private final Map<GebietUndStimmabgabeart, Map<Stimmenart, Long>> cacheStimmenAktuellUngueltig = new HashMap<>();

  private final BestimmeStimmen<Stimmenart, GebietUndStimmabgabeart> bestimmeStimmenAktuellUngueltig = new BestimmeStimmen<Stimmenart, GebietUndStimmabgabeart>() {

    @Override
    public Long ermittleStimmen(Stimmenart stimmenart, Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet) {
      return ermittleStimmenUngueltig(stimmenPoolAktuell, wahldurchfuehrungsgebiet, stimmenart);
    }

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

    @Override
    public List<Stimmenart> getTargetAufGebiet(GebietUndStimmabgabeart gebietUndStimmabgabeart) {
      return TARGET_STIMMENARTEN_GUELTIGE;
    }
  };

  private final Map<GebietUndStimmabgabeart, Long> cacheGesamtstimmenVorperiodeGueltig = new HashMap<>();

  private final Map<GebietUndStimmabgabeart, Map<Stimmenart, Long>> cacheStimmenVorperiodeGueltig = new HashMap<>();

  private final BestimmeStimmen<Stimmenart, GebietUndStimmabgabeart> bestimmeStimmenVorperiodeGueltig = new BestimmeStimmen<Stimmenart, GebietUndStimmabgabeart>() {

    @Override
    public Long ermittleStimmen(Stimmenart stimmenart, Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet) {
      return ermittleStimmenGueltig(stimmenPoolVorperiode, wahldurchfuehrungsgebiet, stimmenart);
    }

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

    @Override
    public List<Stimmenart> getTargetAufGebiet(GebietUndStimmabgabeart gebietUndStimmabgabeart) {
      return TARGET_STIMMENARTEN_GUELTIGE;
    }
  };

  private final Map<GebietUndStimmabgabeart, Long> cacheGesamtstimmenVorperiodeUngueltig = new HashMap<>();

  private final Map<GebietUndStimmabgabeart, Map<Stimmenart, Long>> cacheStimmenVorperiodeUngueltig = new HashMap<>();

  private final BestimmeStimmen<Stimmenart, GebietUndStimmabgabeart> bestimmeStimmenVorperiodeUngueltig = new BestimmeStimmen<Stimmenart, GebietUndStimmabgabeart>() {

    @Override
    public Long ermittleStimmen(Stimmenart stimmenart, Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet) {
      return ermittleStimmenUngueltig(stimmenPoolVorperiode, wahldurchfuehrungsgebiet, stimmenart);
    }

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

    @Override
    public List<Stimmenart> getTargetAufGebiet(GebietUndStimmabgabeart gebietUndStimmabgabeart) {
      return TARGET_STIMMENARTEN_GUELTIGE;
    }
  };
}
