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

import org.apache.commons.math3.distribution.NormalDistribution;

import de.ivu.elect.business.ergebnis.entity.Stimmenart;
import de.ivu.elect.business.hochrechnung.entity.GebietDatenWahlbeteiligung;
import de.ivu.elect.business.hochrechnung.entity.ZufaelligeWahlbeteiligungGenerator;
import de.ivu.elect.business.hochrechnung.entity.ZufaelligeWahlbeteiligungNormalverteilungKonfiguration;

public class ZufaelligeWahlbeteiligungNormalverteilungGenerator extends ZufaelligeWahlbeteiligungGenerator {

  private ZufaelligeWahlbeteiligungNormalverteilungKonfiguration konfiguration;

  public ZufaelligeWahlbeteiligungNormalverteilungGenerator(ZufaelligeWahlbeteiligungNormalverteilungKonfiguration konfiguration) {
    this.konfiguration = konfiguration;
  }

  @Override
  public GebietDatenWahlbeteiligung getZufaelligeWahlbeteiligungFuerUrnengebiet() {
    double wahlbeteiligungOhneWahlschein = getWahlbeteiligungOhneWahlscheinUrnenwahl();
    double wahlbeteiligungMitWahlschein = getWahlbeteiligungMitWahlscheinUrnenwahl();
    double wahlbeteiligungNichtImWaehlerverzeichnis = getWahlbeteiligungNichtImWaehlerverzeichnisUrnenwahl();
    return getZufaelligeWahlbeteiligungFuerGebiet(wahlbeteiligungOhneWahlschein, wahlbeteiligungMitWahlschein, wahlbeteiligungNichtImWaehlerverzeichnis);
  }

  @Override
  public GebietDatenWahlbeteiligung getZufaelligeWahlbeteiligungFuerBriefgebiet() {
    double wahlbeteiligungOhneWahlschein = 0;
    double wahlbeteiligungMitWahlschein = getWahlbeteiligungMitWahlscheinBriefwahl();
    double wahlbeteiligungNichtImWaehlerverzeichnis = 0;
    return getZufaelligeWahlbeteiligungFuerGebiet(wahlbeteiligungOhneWahlschein, wahlbeteiligungMitWahlschein, wahlbeteiligungNichtImWaehlerverzeichnis);
  }

  @Override
  public GebietDatenWahlbeteiligung getZufaelligeWahlbeteiligungFuerUrnengebietMitBriefwahlInUrneErfassen() {
    double wahlbeteiligungOhneWahlschein = getWahlbeteiligungOhneWahlscheinUrnenwahl();
    double wahlbeteiligungMitWahlschein = getWahlbeteiligungMitWahlscheinBriefwahl();
    double wahlbeteiligungNichtImWaehlerverzeichnis = getWahlbeteiligungNichtImWaehlerverzeichnisUrnenwahl();
    return getZufaelligeWahlbeteiligungFuerGebiet(wahlbeteiligungOhneWahlschein, wahlbeteiligungMitWahlschein, wahlbeteiligungNichtImWaehlerverzeichnis);
  }

  private double getWahlbeteiligungOhneWahlscheinUrnenwahl() {
    NormalDistribution normalDistributionFuerWahlbeteiligungOhneWahlscheinUrnenwahl = konfiguration
        .getNormalDistributionFuerWahlbeteiligungOhneWahlscheinUrnenwahl();
    double min = konfiguration.getWahlbeteiligungOhneWahlscheinUrnenwahlMinPreventingZeros();
    return sampleFrom(normalDistributionFuerWahlbeteiligungOhneWahlscheinUrnenwahl, min);
  }

  private double getWahlbeteiligungMitWahlscheinUrnenwahl() {
    NormalDistribution normalDistributionFuerWahlbeteiligungMitWahlscheinUrnenwahl = konfiguration
        .getNormalDistributionFuerWahlbeteiligungMitWahlscheinUrnenwahl();
    double min = konfiguration.getWahlbeteiligungMitWahlscheinUrnenwahlMinPreventingZeros();
    return sampleFrom(normalDistributionFuerWahlbeteiligungMitWahlscheinUrnenwahl, min);
  }

  private double getWahlbeteiligungNichtImWaehlerverzeichnisUrnenwahl() {
    NormalDistribution normalDistributionFuerWahlbeteiligungNichtImWaehleverzeichnisUrnenwahl = konfiguration
        .getNormalDistributionFuerWahlbeteiligungNichtImWaehleverzeichnisUrnenwahl();
    double min = 0;
    return sampleFrom(normalDistributionFuerWahlbeteiligungNichtImWaehleverzeichnisUrnenwahl, min);
  }

  private double sampleFrom(NormalDistribution normalDistribution, double min) {
    double sample = normalDistribution.sample();
    return trimAnteil(sample, min);
  }

  private double getWahlbeteiligungMitWahlscheinBriefwahl() {
    NormalDistribution konfigurationNormalDistributionFuerWahlbeteiligungGesamtBriefwahl = konfiguration
        .getNormalDistributionFuerWahlbeteiligungGesamtBriefwahl();
    double min = konfiguration.getWahlbeteiligungGesamtBriefwahlMinPreventingZeros();
    return sampleFrom(konfigurationNormalDistributionFuerWahlbeteiligungGesamtBriefwahl, min);
  }

  private GebietDatenWahlbeteiligung getZufaelligeWahlbeteiligungFuerGebiet(double wahlbeteiligungOhneWahlschein, double wahlbeteiligungMitWahlschein, double wahlbeteiligungNichtImWaehlerverzeichnis) {
    GebietDatenWahlbeteiligung result = new GebietDatenWahlbeteiligung();
    result.setAnzahl(Stimmenart.OHNE_WAHLSCHEIN, wahlbeteiligungOhneWahlschein);
    result.setAnzahl(Stimmenart.MIT_WAHLSCHEIN, wahlbeteiligungMitWahlschein);
    result.setAnzahl(Stimmenart.NICHT_IM_WAEHLERVERZEICHNIS, wahlbeteiligungNichtImWaehlerverzeichnis);
    return result;
  }

  private double trimAnteil(double anteil, double min) {
    if (anteil >= min && anteil <= 1) {
      return anteil;
    }
    return anteil > 1? 1 : min;
  }
}
