
package de.ivu.elect.business.stimmeingabe.control.fiktive;

import java.util.Collection;
import java.util.Map;
import java.util.Optional;
import java.util.Random;

import de.ivu.elect.business.ergebnis.control.ErgebnisDaten;
import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
import de.ivu.elect.business.gebietsbaum.entity.Wahldurchfuehrungsgebiet;
import de.ivu.elect.business.hochrechnung.entity.AnzahlStimmenProWaehlerNachStimmenartBestimmer;
import de.ivu.elect.business.hochrechnung.entity.StimmenhochrechnerGebietAusgabedaten;
import de.ivu.elect.business.kandidatur.entity.Listenkandidatur;
import de.ivu.elect.business.stimmeingabe.entity.Ergebniseingang;
import de.ivu.elect.business.stimmeingabe.entity.Stimmeingabe;


import static de.ivu.elect.jee.entity.EntityUtil.castTo;

public abstract class AbstractStimmeingabenAusFiktivwertenSetzer {

  public AbstractStimmeingabenAusFiktivwertenSetzer() {
    super();
  }

  public abstract boolean isVomLetztenErgebniseingangUebernommeneStimmenZuUeberschreiben(Ergebniseingang ergebniseingangAktuell, Collection<Stimmeingabe> uebernommeneStimmeingaben);

  public abstract void setzeErgebniseingangStimmeingabenAnzahlAusHochgerechnetenWerten(Ergebniseingang ergebniseingang, StimmenhochrechnerGebietAusgabedaten hochrechnerAusgabedaten, AnzahlStimmenProWaehlerNachStimmenartBestimmer anzahlStimmenProWaehlerBestimmer);

  protected void setzeStimmenEingabeAnzahl(Long anzahl, Stimmeingabe stimmeingabe) {
    if (stimmeingabe != null && anzahl != null) {
      stimmeingabe.setAnzahl(anzahl);
    }
  }

  protected void setzeStimmenEingabeAnzahl(Long anzahl, Optional<Stimmeingabe> stimmeingabe) {
    if (stimmeingabe.isPresent() && anzahl != null) {
      stimmeingabe.get().setAnzahl(anzahl);
    }
  }

  protected void addListenkandidaturStimmenEingabeAnzahlAufVorhandeneAnzahl(Map<Listenkandidatur, Long> listenkandidaturenStimmen, Stimmeingabe stimmeingabe) {
    if (stimmeingabe != null) {
      Listenkandidatur listenkandidatur = stimmeingabe.getListenkandidatur();

      if (listenkandidatur != null) {
        var vorher = listenkandidaturenStimmen.get(listenkandidatur);
        if (vorher == null/*ELECTKOM-2579*/) {
          setzeStimmenEingabeAnzahl(stimmeingabe.getAnzahl(), stimmeingabe);
        } else {
          setzeStimmenEingabeAnzahl(vorher + stimmeingabe.getAnzahl(), stimmeingabe);
        }
      }
    }
  }

  protected long getRandomValueFromMinToMaxAnteil(Random random, Double anteilMin, Double anteilMax, long ganz) {
    long min = Math.round(ganz * anteilMin);
    long max = Math.round(ganz * anteilMax);

    return getRandomValueFromMinToMax(random, min, max);
  }

  protected long getRandomValueFrom1ToN(Random random, long n) {
    return getRandomValueFromMinToMax(random, 1, n);
  }

  protected long getRandomValueFromMinToMax(Random random, long min, long max) {
    return getRandomValueFrom0ToN(random, max - min) + min;
  }

  protected long getRandomValueFrom0ToN(Random random, long n) {
    return Math.abs(random.nextLong()) % (n + 1);
  }

  protected double getRandomValueFromMinToMax(Random random, double min, double max) {
    return getRandomValueFrom0ToN(random, max - min) + min;
  }

  protected double getRandomValueFrom0ToN(Random random, double n) {
    return random.nextDouble() * n;
  }

  protected boolean isErgebnisNachbargebieteVorhanden(ErgebnisDaten ergebnisDaten, Gebiet gebiet) {
    if (gebiet == null) {
      return false;
    }
    return isErgebnisFuerGebietVorhanden(ergebnisDaten, gebiet.getUebergeordnetesGebiet());
  }

  protected boolean isErgebnisFuerGebietVorhanden(ErgebnisDaten ergebnisDaten, Gebiet gebiet) {
    if (ergebnisDaten == null || gebiet == null) {
      return false;
    }

    Long anzahlWaehlerGesamt = ergebnisDaten.getWaehlerGesamt(castTo(gebiet, Wahldurchfuehrungsgebiet.class));

    return anzahlWaehlerGesamt != null && anzahlWaehlerGesamt > 0L;
  }

  protected void assertErgebnisVorhanden(Ergebniseingang ergebniseingang, ErgebnisDaten ergebnisDaten, Gebiet obergebiet) {
    if (!isErgebnisFuerGebietVorhanden(ergebnisDaten, obergebiet)) {
      throw new IllegalArgumentException("Für Ergebniseingang [" + ergebniseingang
          + "] wurde die Fiktivwertberechnung anhand eines Ergebnisses angefragt; das Ergebnis enthält jedoch keine Daten für das Obergebiet [" + obergebiet + "]");
    }
  }
}
