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

import java.util.Collection;

import javax.inject.Inject;

import de.ivu.elect.business.ergebnis.entity.Stimmenart;
import de.ivu.elect.business.stimmeingabe.boundary.StimmeingabeErfassenDescriptionResolver;
import de.ivu.elect.business.stimmeingabe.control.StimmeingabePlausiBuilder;
import de.ivu.elect.business.stimmeingabe.entity.Ergebniseingang;
import de.ivu.elect.business.stimmeingabe.entity.Stimmeingabe;
import de.ivu.elect.business.stimmeingabe.entity.StimmeingabeGruppeStimmen;
import de.ivu.elect.business.stimmeingabe.entity.StimmeingabeGruppeStimmenErfassenModel;
import de.ivu.elect.business.stimmeingabe.entity.StimmeingabeGruppeStimmenListenstimmenAbzuziehen;
import de.ivu.elect.business.stimmeingabe.entity.StimmeingabeKopfzahl;
import de.ivu.elect.business.stimmeingabe.entity.StimmeingabeKopfzahlErfassenModel;
import de.ivu.elect.business.stimmeingabe.entity.StimmeingabeListenkandidaturStimmenErfassenModel;
import de.ivu.elect.business.stimmeingabe.entity.StimmeingabePlausiFehlerart;
import de.ivu.elect.business.stimmeingabe.entity.StimmeingabePlausiTyp;
import de.ivu.elect.business.wahl.entity.CommonWahlart;
import de.ivu.elect.business.wahl.entity.Wahl;
import de.ivu.elect.jee.faces.Messages;

public class BtwStimmeingabePlausiBuilder extends AbstractErgebniseingangPlausiBuilder implements StimmeingabePlausiBuilder {

  private static final long serialVersionUID = 1L;

  @Inject
  private transient BtwKopfzahlPlausiBuilder btwKopfzahlPlausiBuilder;

  public BtwStimmeingabePlausiBuilder() {
    super();
  }

  public BtwStimmeingabePlausiBuilder(BtwKopfzahlPlausiBuilder btwKopfzahlPlausiBuilder, Messages messages, StimmeingabeErfassenDescriptionResolver stimmeingabeErfassenDescriptionResolver) {
    super(messages, stimmeingabeErfassenDescriptionResolver, btwKopfzahlPlausiBuilder.getStimmeingabeKopfzahlKonfiguration());

    this.btwKopfzahlPlausiBuilder = btwKopfzahlPlausiBuilder;
  }

  @Override
  public boolean isApplicable(Wahl wahl) {
    return CommonWahlart.BUNDESTAGSWAHL.equalsWahlart(wahl);
  }

  @Override
  protected AbstractStimmeingabePlausiDefinitions getStimmeingabePlausiDefinitions(Ergebniseingang ergebniseingang) {
    return btwKopfzahlPlausiBuilder.getStimmeingabePlausiDefinitions(ergebniseingang);
  }

  @Override
  protected void fillStimmeingabePlausis(StimmeingabePlausiBuilderContext context, StimmeingabeKopfzahlErfassenModel kopfzahlErfassenModel,
      StimmeingabeGruppeStimmenErfassenModel gruppeStimmenErfassenModel, Collection<StimmeingabeListenkandidaturStimmenErfassenModel> listenkandidaturStimmenErfassenModels) {
    fillAllgemeinePlausis(context);

    fillKopfzahlPlausis(kopfzahlErfassenModel, context);

    fillGruppeStimmenPlausis(gruppeStimmenErfassenModel, kopfzahlErfassenModel, context);
  }

  protected void fillKopfzahlPlausis(StimmeingabeKopfzahlErfassenModel model, StimmeingabePlausiBuilderContext context) {
    btwKopfzahlPlausiBuilder.fillKopfzahlPlausis(model, context);
  }

  protected void fillGruppeStimmenPlausis(StimmeingabeGruppeStimmenErfassenModel model, StimmeingabeKopfzahlErfassenModel kopfzahlModel, StimmeingabePlausiBuilderContext context) {
    fillGruppeStimmenKopfzahlenPlausis(model, kopfzahlModel, context);

    fillGruppeStimmenGueltigeUndUngueltigePlausis(model, context);

    fillGruppeStimmenVergleichVorlaeufigEndgueltigPlausis(model, context);

    fillGruppeStimmenVergleichDirektstimmenListenstimmenPlausi(model, kopfzahlModel, context);
  }

  protected void fillGruppeStimmenKopfzahlenPlausis(StimmeingabeGruppeStimmenErfassenModel model, StimmeingabeKopfzahlErfassenModel kopfzahlModel, StimmeingabePlausiBuilderContext context) {
    // Gültige + Ungültige = Wähler
    if (isWaehlerGesamtZuPlausibilisieren(kopfzahlModel)) {
      StimmeingabeKopfzahl waehlerGesamt = kopfzahlModel.getWaehlerGesamt();

      Stimmeingabe stimmeingabeGueltigeDirektstimmen = model.getStimmeingabeGruppeGueltigeDirektstimmen().orElse(null);
      Stimmeingabe stimmeingabeUngueltigeDirektstimmen = model.getStimmeingabeGruppeUngueltigeDirektstimmen().orElse(null);

      if (stimmeingabeGueltigeDirektstimmen != null && stimmeingabeUngueltigeDirektstimmen != null) {
        addPlausiKopfzahl(waehlerGesamt,
            new WaehlerGesamtAnzahlGleichGueltigeStimmenAnzahlPlusUngueltigeStimmenAnzahlPlausi(stimmeingabeGueltigeDirektstimmen, stimmeingabeUngueltigeDirektstimmen,
                findStimmeingabePlausiKonfigurationOrDefaults(context, StimmeingabePlausiTyp.WAEHLER_GESAMT_ANZAHL_GLEICH_GUELTIGE_DIREKTSTIMMEN_ANZAHL_PLUS_UNGUELTIGE_DIREKTSTIMMEN_ANZAHL,
                    StimmeingabePlausiFehlerart.ERROR),
                getMessages()),
            context);
      }

      Stimmeingabe stimmeingabeGueltigeListenstimmen = model.getStimmeingabeGruppeGueltigeListenstimmen().orElse(null);
      Stimmeingabe stimmeingabeUngueltigeListenstimmen = model.getStimmeingabeGruppeUngueltigeListenstimmen().orElse(null);

      if (stimmeingabeGueltigeListenstimmen != null && stimmeingabeUngueltigeListenstimmen != null) {
        addPlausiKopfzahl(waehlerGesamt,
            new WaehlerGesamtAnzahlGleichGueltigeStimmenAnzahlPlusUngueltigeStimmenAnzahlPlausi(stimmeingabeGueltigeListenstimmen, stimmeingabeUngueltigeListenstimmen,
                findStimmeingabePlausiKonfigurationOrDefaults(context, StimmeingabePlausiTyp.WAEHLER_GESAMT_ANZAHL_GLEICH_GUELTIGE_LISTENSTIMMEN_ANZAHL_PLUS_UNGUELTIGE_LISTENSTIMMEN_ANZAHL,
                    StimmeingabePlausiFehlerart.ERROR),
                getMessages()),
            context);
      }
    }

    addWarnungenDirektListenstimmen(model, kopfzahlModel, context);
  }

  protected void fillGruppeStimmenGueltigeUndUngueltigePlausis(StimmeingabeGruppeStimmenErfassenModel model, StimmeingabePlausiBuilderContext context) {
    // Gültige > 0
    addPlausiGueltigeGroesserNull(model, context);

    // Gültige = Summe aller Wahlvorschlag Direkt-/Listenstimmen
    addPlausiSummeWahlvorschlaege(model, context);
  }

  protected void addPlausiSummeWahlvorschlaege(StimmeingabeGruppeStimmenErfassenModel model, StimmeingabePlausiBuilderContext context) {
    if (model.hasStimmenGruppeGueltige()) {
      StimmeingabeGruppeStimmen stimmeingabeGueltigeStimmen = model.getStimmenGruppeGueltige();

      addPlausiDirektstimme(stimmeingabeGueltigeStimmen,
          new GueltigeStimmenAnzahlGleichGruppeStimmenAnzahlenSummePlausi(getStimmeingabenPolitischeGruppenByStimmenart(model, Stimmenart.DIREKTSTIMME),
              findStimmeingabePlausiKonfigurationOrDefaults(context, StimmeingabePlausiTyp.GUELTIGE_DIREKTSTIMMEN_ANZAHL_GLEICH_GRUPPE_DIREKTSTIMMEN_ANZAHLEN_SUMME, StimmeingabePlausiFehlerart.ERROR),
              getMessages()),
          context);

      addPlausiListenstimme(stimmeingabeGueltigeStimmen,
          new GueltigeStimmenAnzahlGleichGruppeStimmenAnzahlenSummePlausi(getStimmeingabenPolitischeGruppenByStimmenart(model, Stimmenart.LISTENSTIMME),
              findStimmeingabePlausiKonfigurationOrDefaults(context, StimmeingabePlausiTyp.GUELTIGE_LISTENSTIMMEN_ANZAHL_GLEICH_GRUPPE_LISTENSTIMMEN_ANZAHLEN_SUMME, StimmeingabePlausiFehlerart.ERROR),
              getMessages()),
          context);

      addPlausiListenstimmeAbzuziehen(stimmeingabeGueltigeStimmen,
          new GueltigeStimmenAnzahlGleichGruppeStimmenAnzahlenSummePlausi(getStimmeingabenPolitischeGruppenByStimmenart(model, Stimmenart.LISTENSTIMME_ABZUZIEHEN),
              findStimmeingabePlausiKonfigurationOrDefaults(context, StimmeingabePlausiTyp.GUELTIGE_LISTENSTIMMEN_ABZUZIEHEN_ANZAHL_GLEICH_GRUPPE_LISTENSTIMMEN_ABZUZIEHEN_ANZAHLEN_SUMME,
                  StimmeingabePlausiFehlerart.ERROR),
              getMessages()),
          context);
    }
  }

  protected Collection<Stimmeingabe> getStimmeingabenPolitischeGruppenByStimmenart(StimmeingabeGruppeStimmenErfassenModel model, Stimmenart stimmenart) {
    return model.getStimmeingabenPolitischeGruppenByStimmenart(stimmenart);
  }

  private void addPlausiListenstimmeAbzuziehen(StimmeingabeGruppeStimmen stimmeingabeGueltigeStimmen, StimmeingabePlausi stimmeingabePlausi, StimmeingabePlausiBuilderContext context) {
    if (stimmeingabeGueltigeStimmen instanceof StimmeingabeGruppeStimmenListenstimmenAbzuziehen) {
      StimmeingabeGruppeStimmenListenstimmenAbzuziehen stimmeingabeGruppeStimmenListenstimmenAbzuziehen = (StimmeingabeGruppeStimmenListenstimmenAbzuziehen) stimmeingabeGueltigeStimmen;

      addPlausi(stimmeingabeGruppeStimmenListenstimmenAbzuziehen.getStimmeingabeListenstimmeAbzuziehen(), stimmeingabePlausi, context);
    }
  }

  private void addPlausiGueltigeGroesserNull(StimmeingabeGruppeStimmenErfassenModel model, StimmeingabePlausiBuilderContext context) {
    if (model.hasStimmenGruppeGueltige()) {
      StimmeingabeGruppeStimmen stimmeingabeGueltigeStimmen = model.getStimmenGruppeGueltige();

      addPlausiDirektstimme(stimmeingabeGueltigeStimmen, new StimmeingabeAnzahlGroesserAnzahlPlausi(StimmeingabeAnzahlReference.SYSTEM_GRUPPE_GUELTIGE_DIREKTSTIMMEN,
          findStimmeingabePlausiKonfigurationOrDefaults(context, StimmeingabePlausiTyp.GUELTIGE_DIREKTSTIMMEN_ANZAHL_GROESSER_NULL, StimmeingabePlausiFehlerart.ERROR), getMessages()), context);

      addPlausiListenstimme(stimmeingabeGueltigeStimmen, new StimmeingabeAnzahlGroesserAnzahlPlausi(StimmeingabeAnzahlReference.SYSTEM_GRUPPE_GUELTIGE_LISTENSTIMMEN,
          findStimmeingabePlausiKonfigurationOrDefaults(context, StimmeingabePlausiTyp.GUELTIGE_LISTENSTIMMEN_ANZAHL_GROESSER_NULL, StimmeingabePlausiFehlerart.ERROR), getMessages()), context);
    }
  }
}
