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

import java.util.ArrayList;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import de.ivu.elect.business.stimmeingabe.entity.Stimmeingabe;
import de.ivu.elect.business.stimmeingabe.entity.StimmeingabePlausiKonfiguration;
import de.ivu.elect.jee.faces.Messages;

public abstract class AbstractWaehlerGesamtAnzahlComparedToTheoretischeAnzahlPlausi extends AbstractStimmeingabeAnzahlComparedToPlausi {

  private static final boolean VERBOSE = false;

  private final Long anzahlStimmenProWaehlerMaximal;

  private final long anzahlStimmenProWaehlerFuerListeMinimal;

  private final long anzahlStimmenProWaehlerFuerListeMaximal;

  private final Stimmeingabe stimmzetteleingabeUngueltigeListenstimmen;

  private final KommunalListenwahlStimmeingabenGueltigeListenstimmen stimmeingabenGueltigeListenstimmen;

  private final Logger log;

  protected AbstractWaehlerGesamtAnzahlComparedToTheoretischeAnzahlPlausi(Long anzahlStimmenProWaehlerMaximal, Long anzahlStimmenProWaehlerFuerListeMinimal,
      Long anzahlStimmenProWaehlerFuerListeMaximal, StimmeingabeAnzahlComparedToOperator comparedToOperator, Stimmeingabe stimmzetteleingabeUngueltigeListenstimmen,
      KommunalListenwahlStimmeingabenGueltigeListenstimmen stimmeingabenGueltigeListenstimmen, StimmeingabePlausiKonfiguration stimmeingabePlausiKonfiguration, Messages messages) {
    super(new StimmeingabeAnzahlComparedToStimmeingabeAnzahlenPlausiDefinition(StimmeingabeAnzahlReference.SYSTEM_GRUPPE_WAEHLER_GESAMT, comparedToOperator,
        StimmeingabeAnzahlReference.SYSTEM_GRUPPE_UNGUELTIGE_LISTENSTIMMEN, StimmeingabeAnzahlReference.SYSTEM_GRUPPE_GUELTIGE_LISTENSTIMMEN_NUR_EINZELNER_WAHLVORSCHLAG_UNVERAENDERT,
        StimmeingabeAnzahlReference.SYSTEM_GRUPPE_GUELTIGE_LISTENSTIMMEN_NUR_EINZELNER_WAHLVORSCHLAG_VERAENDERT,
        StimmeingabeAnzahlReference.SYSTEM_GRUPPE_GUELTIGE_LISTENSTIMMEN_LISTENKANDIDATUREN_GESAMT), stimmeingabePlausiKonfiguration, messages);
    this.anzahlStimmenProWaehlerMaximal = fixNullToZero(anzahlStimmenProWaehlerMaximal);

    this.anzahlStimmenProWaehlerFuerListeMinimal = fixNullToZero(anzahlStimmenProWaehlerFuerListeMinimal);
    this.anzahlStimmenProWaehlerFuerListeMaximal = fixNullToZero(anzahlStimmenProWaehlerFuerListeMaximal);

    this.stimmzetteleingabeUngueltigeListenstimmen = stimmzetteleingabeUngueltigeListenstimmen;

    this.stimmeingabenGueltigeListenstimmen = stimmeingabenGueltigeListenstimmen;

    this.log = LoggerFactory.getLogger(getClass());
  }

  @Override
  protected boolean isPlausibilisierenZulaessig(Stimmeingabe stimmeingabe) {
    return super.isPlausibilisierenZulaessig(stimmeingabe) && getAnzahlStimmenProWaehlerFuerListeMinimal() > 0L && getAnzahlStimmenProWaehlerFuerListeMaximal() > 0L;
  }

  @Override
  protected StimmeingabePlausiResult calculatePlausiResult(Stimmeingabe stimmeingabe, Double value) {
    log("Wähler verglichen [ist = {}, soll = {}, operator = {}]", calculateStimmeingabeAnzahl(stimmeingabe), value, getComparedToOperator());

    return super.calculatePlausiResult(stimmeingabe, value);
  }

  @Override
  protected Long getComparedToValue() {
    long anzahlStimmzettelErfasst = getAnzahlStimmzettelErfasst();
    long anzahlStimmzettelBerechnet = getAnzahlStimmzettelBerechnetIncludingAnzahlStimmen();

    long result = Math.max(anzahlStimmzettelErfasst, anzahlStimmzettelBerechnet);

    log("\t Wähler berechnet -> {} = max(Stimmzettel erfasst, Stimmzettel berechnet (mit Stimmen)) ->  max({}, {})", result, anzahlStimmzettelErfasst, anzahlStimmzettelBerechnet);

    return result;
  }

  protected boolean isErgebnismeldungKomplett() {
    return stimmeingabenGueltigeListenstimmen.isErgebnismeldungKomplett();
  }

  protected long getAnzahlStimmenProWaehlerMaximal() {
    return anzahlStimmenProWaehlerMaximal;
  }

  private long getAnzahlStimmenProWaehlerFuerListeMinimal() {
    return anzahlStimmenProWaehlerFuerListeMinimal;
  }

  private long getAnzahlStimmenProWaehlerFuerListeMaximal() {
    return anzahlStimmenProWaehlerFuerListeMaximal;
  }

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

    long anzahlStimmzettelErfasstNurEinzelnerWahlvorschlag = getAnzahlStimmzettelErfasstNurEinzelnerWahlvorschlag();
    long anzahlStimmzettelErfasstUngueltig = getAnzahlStimmzettelErfasstUngueltig();

    result += anzahlStimmzettelErfasstNurEinzelnerWahlvorschlag;
    result += anzahlStimmzettelErfasstUngueltig;

    log("\t\t Stimmzettel erfasst -> {} = Stimmzettel nur einzelner Wahlvorschlag + Stimmzettel ungültig -> {} + {}", result, anzahlStimmzettelErfasstNurEinzelnerWahlvorschlag,
        anzahlStimmzettelErfasstUngueltig);

    return result;
  }

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

    long anzahlStimmzettelErfasstNurEinzelnerWahlvorschlagUnveraendert = getAnzahlStimmzettelErfasstNurEinzelnerWahlvorschlagUnveraendert();
    long anzahlStimmzettelErfasstUngueltig = getAnzahlStimmzettelErfasstUngueltig();

    result += anzahlStimmzettelErfasstNurEinzelnerWahlvorschlagUnveraendert;
    result += anzahlStimmzettelErfasstUngueltig;

    long anzahlStimmzettelErfasstNurEinzelnerWahlvorschlagVeraendert = getAnzahlStimmzettelErfasstNurEinzelnerWahlvorschlagVeraendert();
    long anzahlStimmzettelBerechnetWahlvorschlagVeraendert = getAnzahlStimmzettelBerechnetWahlvorschlagVeraendert();

    result += Math.max(anzahlStimmzettelErfasstNurEinzelnerWahlvorschlagVeraendert, anzahlStimmzettelBerechnetWahlvorschlagVeraendert);

    log("\t\t Stimmzettel berechnet (mit Stimmen) -> {} = Stimmzettel nur einzelner Wahlvorschlag unverändert + Stimmzettel ungültig + max(Stimmzettel nur einzelner Wahlvorschlag verändert, Stimmzettel berechnet Wahlvorschlag verändert) -> {} + {} + max({}, {})",
        result, anzahlStimmzettelErfasstNurEinzelnerWahlvorschlagUnveraendert, anzahlStimmzettelErfasstUngueltig, anzahlStimmzettelErfasstNurEinzelnerWahlvorschlagVeraendert,
        anzahlStimmzettelBerechnetWahlvorschlagVeraendert);

    return result;
  }

  private long getAnzahlStimmzettelErfasstNurEinzelnerWahlvorschlag() {
    return stimmeingabenGueltigeListenstimmen.getAnzahlStimmzettelNurEinzelnerWahlvorschlag();
  }

  private long getAnzahlStimmzettelErfasstNurEinzelnerWahlvorschlagUnveraendert() {
    return stimmeingabenGueltigeListenstimmen.getAnzahlStimmzettelNurEinzelnerWahlvorschlagUnveraendert();
  }

  private long getAnzahlStimmzettelErfasstNurEinzelnerWahlvorschlagVeraendert() {
    return stimmeingabenGueltigeListenstimmen.getAnzahlStimmzettelNurEinzelnerWahlvorschlagVeraendert();
  }

  private long getAnzahlStimmzettelErfasstUngueltig() {
    return fixNullToZero(stimmzetteleingabeUngueltigeListenstimmen);
  }

  protected abstract long getAnzahlStimmzettelBerechnetWahlvorschlagVeraendert();

  protected long getAnzahlStimmenErfasstWahlvorschlagVeraendertMinimal() {
    return getAnzahlStimmenErfasstWahlvorschlagVeraendert(getAnzahlStimmenProWaehlerFuerListeMaximal());
  }

  protected long getAnzahlStimmenErfasstWahlvorschlagVeraendertMaximal() {
    return getAnzahlStimmenErfasstWahlvorschlagVeraendert(getAnzahlStimmenProWaehlerFuerListeMinimal());
  }

  private long getAnzahlStimmenErfasstWahlvorschlagVeraendert(long anzahlStimmenProWaehlerFuerListe) {
    return stimmeingabenGueltigeListenstimmen.getAnzahlStimmenErfasstExcludingStimmzettelNurEinzelnerWahlvorschlagUnveraendert(anzahlStimmenProWaehlerFuerListe);
  }

  private long getAnzahlStimmenErfasst() {
    return stimmeingabenGueltigeListenstimmen.getAnzahlStimmenErfasst();
  }

  @Override
  protected List<Object> getStimmeingabefehlerBeschreibungParameters(Stimmeingabe stimmeingabe) {
    List<Object> result = new ArrayList<>(super.getStimmeingabefehlerBeschreibungParameters(stimmeingabe));
    result.add(getAnzahlStimmzettelErfasst());
    result.add(getAnzahlStimmenErfasst());

    return result;
  }

  protected void log(String format, Object... params) {
    if (VERBOSE) {
      log.info(format, params);
    }
  }
}
