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

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.inject.Inject;

import de.ivu.elect.business.ergebnis.entity.Stimmenart;
import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
import de.ivu.elect.business.gruppeimgebiet.entity.GruppeImGebiet;
import de.ivu.elect.business.stimmeingabe.control.NlDefaultListenwahlStimmeingabeKonfiguration;
import de.ivu.elect.business.stimmeingabe.control.StimmeingabePlausiBuilder;
import de.ivu.elect.business.stimmeingabe.entity.Ergebniseingabestufe;
import de.ivu.elect.business.stimmeingabe.entity.Ergebniseingang;
import de.ivu.elect.business.stimmeingabe.entity.NlDefaultListenwahlStimmeingabeGruppeStimmenErfassenModel;
import de.ivu.elect.business.stimmeingabe.entity.NlStimmeingabeKopfzahlErfassenModel;
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.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.Wahl;
import de.ivu.elect.business.wahl.entity.WahlartErgebnisart;
import de.ivu.elect.business.wahlvorschlag.boundary.AbstractDirektkandidatDAO;
import de.ivu.elect.business.wahlvorschlag.boundary.AbstractListeDAO;
import de.ivu.elect.jee.entity.EntityUtil;
import de.ivu.elect.jee.entity.tk.EntityTK;

public class NlDefaultListenwahlStimmeingabePlausiBuilder extends AbstractErgebniseingangPlausiBuilder implements StimmeingabePlausiBuilder {

  private static final long serialVersionUID = 1L;

  @Inject
  private transient NlDefaultListenwahlKopfzahlPlausiBuilder nlDefaultListenwahlKopfzahlPlausiBuilder;

  @Inject
  private transient NlDefaultListenwahlStimmeingabeKonfiguration nlDefaultListenwahlStimmeingabeKonfiguration;

  public NlDefaultListenwahlStimmeingabePlausiBuilder() {
    super();
  }

  @Override
  public boolean isApplicable(Wahl wahl) {
    return nlDefaultListenwahlStimmeingabeKonfiguration.isApplicable(wahl);
  }

  @Override
  public void checkPreConditions(Gebiet gebiet, WahlartErgebnisart ergebnisart, AbstractDirektkandidatDAO direktkandidatDAO, AbstractListeDAO listeDAO) {
    super.checkPreConditions(gebiet, ergebnisart, direktkandidatDAO, listeDAO);

    // TODO Ggf. Anzahl Stimmen pro Wähler, etc. konfiguriert?
  }

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

  /*
   * IMMER auch NlDefaultListenwahlStimmeingabePlausiDefinitions ergänzen!
   */

  public boolean containsAnyStimmeingabefehlerEndstufeSpezifisch(Ergebniseingang ergebniseingang) {
    return nlDefaultListenwahlKopfzahlPlausiBuilder.containsAnyStimmeingabefehlerEndstufeSpezifisch(ergebniseingang);
  }

  public Stimmeingabe findKopfzahlPlausiUnspezifischStimmeingabe(Ergebniseingang ergebniseingang) {
    return nlDefaultListenwahlKopfzahlPlausiBuilder.findKopfzahlPlausiUnspezifischStimmeingabe(ergebniseingang);
  }

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

  private void fillStimmeingabePlausis(StimmeingabePlausiBuilderContext context, NlStimmeingabeKopfzahlErfassenModel kopfzahlErfassenModel,
      NlDefaultListenwahlStimmeingabeGruppeStimmenErfassenModel gruppeStimmenErfassenModel, Collection<StimmeingabeListenkandidaturStimmenErfassenModel> listenkandidaturStimmenErfassenModels) {
    fillAllgemeinePlausis(context);

    fillKopfzahlPlausis(context, kopfzahlErfassenModel, gruppeStimmenErfassenModel);

    fillGruppeStimmenPlausis(context, kopfzahlErfassenModel, gruppeStimmenErfassenModel);

    fillListenkandidaturStimmenPlausis(context, kopfzahlErfassenModel, gruppeStimmenErfassenModel, listenkandidaturStimmenErfassenModels);
  }

  @Override
  protected void fillAllgemeinePlausis(StimmeingabePlausiBuilderContext context) {
    super.fillAllgemeinePlausis(context);

    Collection<Stimmeingabe> stimmeingaben = context.getStimmeingabenZuPlausibilisieren();

    if (!stimmeingaben.isEmpty()) {
      Ergebniseingabestufe ergebniseingabestufe = stimmeingaben.iterator().next().getErgebniseingang().getErgebniseingabestufe();

      if (Ergebniseingabestufe.VORSTUFE_02.equals(ergebniseingabestufe)) {
        Ergebniseingang ergebniseingangVorstufe01 = context.getGebiet().getLetzterErgebniseingangInErgebniseingabestufeGueltig(ergebniseingangDAO, context.getErgebniseingangsart(),
            Ergebniseingabestufe.VORSTUFE_01);

        if (ergebniseingangVorstufe01 != null) {
          Map<EntityTK, Stimmeingabe> stimmeingabenVorstufe01PerStimmeingabeTK = EntityUtil.mappeNachKonkretestemTk(ergebniseingangVorstufe01.getStimmeingaben());
          Map<EntityTK, Stimmeingabe> stimmeingabenVorstufe02PerStimmeingabeTK = EntityUtil.mappeNachKonkretestemTk(stimmeingaben);

          for (Entry<EntityTK, Stimmeingabe> stimmeingabeVorstufe02PerStimmeingabeTK : stimmeingabenVorstufe02PerStimmeingabeTK.entrySet()) {
            EntityTK stimmeingabeTK = stimmeingabeVorstufe02PerStimmeingabeTK.getKey();

            Stimmeingabe stimmeingabeVorstufe01 = stimmeingabenVorstufe01PerStimmeingabeTK.get(stimmeingabeTK);
            Stimmeingabe stimmeingabeVorstufe02 = stimmeingabeVorstufe02PerStimmeingabeTK.getValue();

            if (stimmeingabeVorstufe01 != null) {
              addPlausi(stimmeingabeVorstufe02, // ELECT-9839, ELECTVAPP-37, #14 (Kann-Fehler beim Zweiterfasser), cf. ErgebniseingangEndstufeAusVorstufeCreate für Muss-Fehler beim Wahlleiter!
                  new StimmeingabeAnzahlInVorstufenGleichPlausi(StimmeingabeAnzahlReference.GRUPPE_STIMMEN, stimmeingabeVorstufe01,
                      findStimmeingabePlausiKonfigurationOrDefaults(context, StimmeingabePlausiTyp.STIMMEINGABE_ANZAHL_IN_VORSTUFEN_GLEICH, StimmeingabePlausiFehlerart.WARNING), getMessages()),
                  context);
            }
          }
        }
      }
    }
  }

  private void fillKopfzahlPlausis(StimmeingabePlausiBuilderContext context, NlStimmeingabeKopfzahlErfassenModel kopfzahlErfassenModel,
      NlDefaultListenwahlStimmeingabeGruppeStimmenErfassenModel gruppeStimmenErfassenModel) {
    nlDefaultListenwahlKopfzahlPlausiBuilder.fillKopfzahlPlausis(context, kopfzahlErfassenModel, gruppeStimmenErfassenModel);
  }

  private void fillGruppeStimmenPlausis(StimmeingabePlausiBuilderContext context, NlStimmeingabeKopfzahlErfassenModel kopfzahlErfassenModel,
      NlDefaultListenwahlStimmeingabeGruppeStimmenErfassenModel gruppeStimmenErfassenModel) {
    gruppeStimmenErfassenModel.getStimmeingabeGruppeGueltigeListenstimmen().ifPresent(stimmeingabeGueltigeListenstimmen -> addPlausi(stimmeingabeGueltigeListenstimmen, // ELECTVAPP-37, #3, Anzahl Gültige Stimmen = Summe aller Stimmen für Gruppen
        new GueltigeStimmenAnzahlGleichGruppeStimmenAnzahlenSummePlausi(gruppeStimmenErfassenModel.getStimmeingabenPolitischeGruppenByStimmenart(Stimmenart.LISTENSTIMME),
            findStimmeingabePlausiKonfigurationOrDefaults(context, StimmeingabePlausiTyp.GUELTIGE_LISTENSTIMMEN_ANZAHL_GLEICH_GRUPPE_LISTENSTIMMEN_ANZAHLEN_SUMME, StimmeingabePlausiFehlerart.ERROR),
            getMessages()),
        context));
  }

  private void fillListenkandidaturStimmenPlausis(StimmeingabePlausiBuilderContext context, NlStimmeingabeKopfzahlErfassenModel kopfzahlErfassenModel,
      NlDefaultListenwahlStimmeingabeGruppeStimmenErfassenModel gruppeStimmenErfassenModel, Collection<StimmeingabeListenkandidaturStimmenErfassenModel> listenkandidaturStimmenErfassenModels) {
    for (StimmeingabeGruppeStimmen stimmeingabeGruppeStimmen : gruppeStimmenErfassenModel.getStimmenPolitischeGruppen()) {
      GruppeImGebiet gruppeImGebiet = stimmeingabeGruppeStimmen.getGruppeImGebiet();

      if (gruppeImGebiet != null) {
        for (StimmeingabeListenkandidaturStimmenErfassenModel listenkandidaturStimmenErfassenModel : listenkandidaturStimmenErfassenModels) {
          if (gruppeImGebiet.equals(listenkandidaturStimmenErfassenModel.getBezugGruppeImGebiet())) {
            fillListenkandidaturStimmenPlausis(context, listenkandidaturStimmenErfassenModel, stimmeingabeGruppeStimmen);
          }
        }
      }
    }
  }

  private void fillListenkandidaturStimmenPlausis(StimmeingabePlausiBuilderContext context, StimmeingabeListenkandidaturStimmenErfassenModel listenkandidaturStimmenErfassenModel,
      StimmeingabeGruppeStimmen stimmeingabeGruppeStimmen) {
    Stimmeingabe stimmeingabeGruppeListenstimmen = stimmeingabeGruppeStimmen.getStimmeingabeListenstimme();

    if (stimmeingabeGruppeListenstimmen != null) {
      List<Stimmeingabe> stimmeingabenListenkandidaturListenstimmen = listenkandidaturStimmenErfassenModel.getStimmeingabenListenkandidaturStimmen();

      addPlausi(stimmeingabeGruppeListenstimmen, // ELECTVAPP-37, #12, Anzahl Stimmen für Gruppe = Summe aller Stimmen für Listenkandidaturen
          new GruppeListenstimmenAnzahlGleichListenkandidaturListenstimmenAnzahlenSummePlausi(stimmeingabenListenkandidaturListenstimmen, findStimmeingabePlausiKonfigurationOrDefaults(context,
              StimmeingabePlausiTyp.GRUPPE_LISTENSTIMMEN_ANZAHL_GLEICH_LISTENKANDIDATUR_LISTENSTIMMEN_ANZAHLEN_SUMME, StimmeingabePlausiFehlerart.ERROR), getMessages()),
          context);
    }
  }
}
