package de.ivu.elect.business.hochrechnung.entity;

import java.util.HashSet;
import java.util.Set;
import java.util.function.BiFunction;

import org.apache.commons.math3.random.RandomGenerator;
import org.apache.commons.math3.random.Well19937c;

import de.ivu.elect.business.hochrechnung.boundary.HochrechnungsalgorithmusKonfiguration;
import de.ivu.elect.business.hochrechnung.control.ZufaelligeAnzahlWahlberechtigteGenerator;
import de.ivu.elect.jee.presentation.dynamicform.FormBuilder;
import de.ivu.elect.jee.presentation.dynamicform.LambdaEnumProperty;
import de.ivu.elect.jee.presentation.dynamicform.LambdaLongProperty;
import de.ivu.elect.util.ElectRandom;


import static java.util.Arrays.asList;

public class ZufaelligerStimmenHochrechnerKonfiguration implements HochrechnungsalgorithmusKonfiguration {

  private RandomGenerator randomGenerator;
  private long seedFuerRandomGenerator;

  private ZufaelligeAnzahlWahlberechtigteGeneratorArt zufaelligeAnzahlWahlberechtigteGeneratorArt;
  private ZufaelligeWahlbeteiligungGeneratorArt wahlbeteiligungGeneratorArt;
  private ZufaelligeGueltigeStimmenanteileGeneratorArt gueltigeStimmenanteileGeneratorArt;
  private ZufaelligeParteiStimmenGeneratorArt parteiStimmenGeneratorArt;
  private ZufaelligeReferendumFrageStimmenanteileArt zufaelligeReferendumFrageStimmenanteileArt;

  private ZufaelligeAnzahlWahlberechtigteGenerator anzahlWahlberechtigteGenerator;
  private ZufaelligeWahlbeteiligungGenerator wahlbeteiligungGenerator;
  private ZufaelligeGueltigeStimmenanteileGenerator gueltigeStimmenanteileGenerator;
  private ZufaelligeParteiStimmenGenerator parteiStimmenGenerator;
  private ZufaelligeReferendumFrageStimmenanteile zufaelligeReferendumFrageStimmenanteile;

  public ZufaelligerStimmenHochrechnerKonfiguration() {
    // if seed is not given, generate it randomly
    this(new Well19937c(), ElectRandom.getRandom().nextInt());
  }

  public ZufaelligerStimmenHochrechnerKonfiguration(RandomGenerator randomGenerator, long seedFuerRandomGenerator) {
    setRandomGenerator(randomGenerator, seedFuerRandomGenerator);
    setZufaelligeGenaratoren();
  }

  private void setZufaelligeGenaratoren() {
    setZufaelligeAnzahlWahlberechtigteGeneratorArt(ZufaelligeAnzahlWahlberechtigteGeneratorArt.NACH_NORMAL_VERTEILUNG);
    setWahlbeteiligungGeneratorArt(ZufaelligeWahlbeteiligungGeneratorArt.NACH_NORMAL_VERTEILUNG);
    setGueltigeStimmenanteileGeneratorArt(ZufaelligeGueltigeStimmenanteileGeneratorArt.NACH_NORMAL_VERTEILUNG);
    setParteiStimmenGeneratorArt(ZufaelligeParteiStimmenGeneratorArt.QUADRATISCH_WACHSEND_MIT_DER_RELATIVEN_POSITION);
    setZufaelligeReferendumFrageStimmenanteileArt(ZufaelligeReferendumFrageStimmenanteileArt.NACH_NORMAL_VERTEILUNG);
  }

  public RandomGenerator getRandomGenerator() {
    return randomGenerator;
  }

  public void setRandomGenerator(RandomGenerator randomGenerator, long seedFuerRandomGenerator) {
    this.seedFuerRandomGenerator = seedFuerRandomGenerator;
    this.randomGenerator = randomGenerator;
    this.randomGenerator.setSeed(seedFuerRandomGenerator);
  }

  public long getSeedFuerRandomGenerator() {
    return seedFuerRandomGenerator;
  }

  public void setSeedFuerRandomGenerator(long seedFuerRandomGenerator) {
    this.seedFuerRandomGenerator = seedFuerRandomGenerator;
    this.randomGenerator.setSeed(seedFuerRandomGenerator);
  }

  public ZufaelligeAnzahlWahlberechtigteGeneratorArt getZufaelligeAnzahlWahlberechtigteGeneratorArt() {
    return zufaelligeAnzahlWahlberechtigteGeneratorArt;
  }

  public void setZufaelligeAnzahlWahlberechtigteGeneratorArt(ZufaelligeAnzahlWahlberechtigteGeneratorArt zufaelligeAnzahlWahlberechtigteGeneratorArt) {
    this.zufaelligeAnzahlWahlberechtigteGeneratorArt = zufaelligeAnzahlWahlberechtigteGeneratorArt;
    this.anzahlWahlberechtigteGenerator = this.zufaelligeAnzahlWahlberechtigteGeneratorArt.getZufaelligeAnzahlWahlberechtigteGenerator(randomGenerator);
  }

  public ZufaelligeWahlbeteiligungGeneratorArt getWahlbeteiligungGeneratorArt() {
    return wahlbeteiligungGeneratorArt;
  }

  public void setWahlbeteiligungGeneratorArt(ZufaelligeWahlbeteiligungGeneratorArt wahlbeteiligungGeneratorArt) {
    this.wahlbeteiligungGeneratorArt = wahlbeteiligungGeneratorArt;
    this.wahlbeteiligungGenerator = this.wahlbeteiligungGeneratorArt.getZufaelligeWahlbeteiligungGenerator(randomGenerator);
  }

  public ZufaelligeGueltigeStimmenanteileGeneratorArt getGueltigeStimmenanteileGeneratorArt() {
    return gueltigeStimmenanteileGeneratorArt;
  }

  public void setGueltigeStimmenanteileGeneratorArt(ZufaelligeGueltigeStimmenanteileGeneratorArt gueltigeStimmenanteileGeneratorArt) {
    this.gueltigeStimmenanteileGeneratorArt = gueltigeStimmenanteileGeneratorArt;
    this.gueltigeStimmenanteileGenerator = this.gueltigeStimmenanteileGeneratorArt.getZufaelligeGueltigeStimmenanteileGenerator(randomGenerator);
  }

  public ZufaelligeParteiStimmenGeneratorArt getParteiStimmenGeneratorArt() {
    return parteiStimmenGeneratorArt;
  }

  public void setParteiStimmenGeneratorArt(ZufaelligeParteiStimmenGeneratorArt parteiStimmenGeneratorArt) {
    this.parteiStimmenGeneratorArt = parteiStimmenGeneratorArt;
    this.parteiStimmenGenerator = this.parteiStimmenGeneratorArt.getZufaelligeParteiStimmenGenerator(randomGenerator);
  }

  public ZufaelligeReferendumFrageStimmenanteileArt getZufaelligeReferendumFrageStimmenanteileArt() {
    return zufaelligeReferendumFrageStimmenanteileArt;
  }

  public void setZufaelligeReferendumFrageStimmenanteileArt(ZufaelligeReferendumFrageStimmenanteileArt zufaelligeReferendumFrageStimmenanteileArt) {
    this.zufaelligeReferendumFrageStimmenanteileArt = zufaelligeReferendumFrageStimmenanteileArt;
    this.zufaelligeReferendumFrageStimmenanteile = this.zufaelligeReferendumFrageStimmenanteileArt.getZufaelligeReferendumFrageStimmenanteile(randomGenerator);
  }

  public ZufaelligeAnzahlWahlberechtigteGenerator getAnzahlWahlberechtigteGenerator() {
    return anzahlWahlberechtigteGenerator;
  }

  public ZufaelligeWahlbeteiligungGenerator getWahlbeteiligungGenerator() {
    return wahlbeteiligungGenerator;
  }

  public ZufaelligeGueltigeStimmenanteileGenerator getGueltigeStimmenanteileGenerator() {
    return gueltigeStimmenanteileGenerator;
  }

  public ZufaelligeParteiStimmenGenerator getParteiStimmenGenerator() {
    return parteiStimmenGenerator;
  }

  public ZufaelligeReferendumFrageStimmenanteile getReferendumFrageStimmenGenerator() {
    return zufaelligeReferendumFrageStimmenanteile;
  }

  @Override
  public void addObservingProperties(FormBuilder formBuilder, LambdaEnumProperty observedProperty, BiFunction<String, Object[], String> messageGetter) {
    // do nothing, aber erweiterbar um die Konfiguration zu aendern bei Wunsche

    LambdaLongProperty lambdaLongPropertySeed = new LambdaLongProperty(
        messageGetter.apply("stimmeingabe.auffaelligkeit.hochrechnungsalgorithmus.zufaellig.seed.label", new Object[0]), true,
        this::setSeedFuerRandomGenerator,
        this::getSeedFuerRandomGenerator);
    lambdaLongPropertySeed.setTooltip(messageGetter.apply("stimmeingabe.auffaelligkeit.hochrechnungsalgorithmus.zufaellig.seed" + ".tooltip", new Object[0]));


    LambdaEnumProperty<ZufaelligeParteiStimmenGeneratorArt> lambdaZufaelligeParteiStimmenGeneratorArtEnumProperty = new LambdaEnumProperty<>(
        messageGetter.apply("stimmeingabe.auffaelligkeit.hochrechnungsalgorithmus.zufaellig.parteistimmen.generation", new Object[0]), true,
        this::setParteiStimmenGeneratorArt, this::getParteiStimmenGeneratorArt,
        ZufaelligeParteiStimmenGeneratorArt.class, asList(ZufaelligeParteiStimmenGeneratorArt.values()), true);

    Set<HochrechnungsalgorithmusArt> showByHochrechnungsalgorithmusArt = new HashSet<>();
    showByHochrechnungsalgorithmusArt.add(HochrechnungsalgorithmusArt.ZUFAELLIG);
    addAbstractObservingLambdaProperty(formBuilder, observedProperty, lambdaLongPropertySeed,
        showByHochrechnungsalgorithmusArt, true);
    addAbstractObservingLambdaProperty(formBuilder, observedProperty, lambdaZufaelligeParteiStimmenGeneratorArtEnumProperty,
        showByHochrechnungsalgorithmusArt, true);
  }

  @Override
  public HochrechnungsalgorithmusArt getHochrechnungsalgorithmusArt() {
    return HochrechnungsalgorithmusArt.ZUFAELLIG;
  }

  @Override
  public void adjustDefaultSelectionNachKoordinatenInformationen(KoordinatenInformationen koordinatenInformationen) {
    // do nothing (for now)
  }
}
