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

import static java.util.Collections.sort;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import de.ivu.elect.business.stimmeingabe.control.StimmeingabeByAnzahlComparator;
import de.ivu.elect.business.stimmeingabe.entity.Stimmeingabe;

/**
 * Verändert die Summanden, bis sie zusammen die gewünschte Summe ergeben
 */
public class SummenRueckwaertsKorrigierenderFehlerBeheber implements StimmeingabePlausiFiktiveWerteFehlerBeheber {

  private static final Comparator<Stimmeingabe> STIMMEINGABE_GROESSERE_ANZAHL_ZUERST_COMPARATOR = Collections.reverseOrder(new StimmeingabeByAnzahlComparator());

  private final Collection<Stimmeingabe> stimmeingabenSummand;

  public SummenRueckwaertsKorrigierenderFehlerBeheber(Collection<Stimmeingabe> stimmeingabenSummand) {
    super();

    this.stimmeingabenSummand = stimmeingabenSummand;
  }

  @Override
  public void behebeFehler(Stimmeingabe stimmeingabeSumme) {
    long summeIst = getSummeAusStimmeingabenSummand();
    long summeSoll = stimmeingabeSumme.getAnzahl();

    for (Stimmeingabe stimmeingabeSummand : sortiereStimmeingabenGroessereAnzahlZuerst(stimmeingabenSummand)) {
      long summandIst = stimmeingabeSummand.getAnzahl();
      long summandSoll = berechneSummandSoll(summandIst, summeIst, summeSoll);

      stimmeingabeSummand.setAnzahl(summandSoll);

      // Ziel schon erreicht?
      if (getSummeAusStimmeingabenSummand() == summeSoll) {
        break;
      }
    }
  }

  private long berechneSummandSoll(long summandIst, long summeIst, long summeSoll) {
    long differenzSumme = summeIst - summeSoll;

    long differenzAnteiligFuerSummand = Math.round((double) summandIst * differenzSumme / summeIst);
    if (differenzAnteiligFuerSummand == 0) {
      // HINT: da die Summanden schon der Größe nach geordnet sind, können wir jetzt nur noch etwas retten, indem wir die gesamte Restdifferenz aufschlagen.
      differenzAnteiligFuerSummand = getSummeAusStimmeingabenSummand() - summeSoll;
    }

    return summandIst - differenzAnteiligFuerSummand;
  }

  private long getSummeAusStimmeingabenSummand() {
    long result = 0L;

    for (Stimmeingabe stimmeingabeFuerSumme : stimmeingabenSummand) {
      result = result + stimmeingabeFuerSumme.getAnzahl();
    }
    return result;
  }

  private static List<Stimmeingabe> sortiereStimmeingabenGroessereAnzahlZuerst(Collection<Stimmeingabe> stimmeingaben) {
    List<Stimmeingabe> result = new ArrayList<>(stimmeingaben);
    sort(result, STIMMEINGABE_GROESSERE_ANZAHL_ZUERST_COMPARATOR);
    return result;
  }

}
