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

import static java.util.stream.Collectors.toSet;

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

import de.ivu.elect.business.ergebnis.entity.Stimmenart;
import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
import de.ivu.elect.business.gebietsbaum.entity.Wahldurchfuehrungsgebiet;
import de.ivu.elect.business.gruppe.entity.NlSystemGruppe;
import de.ivu.elect.business.stimmeingabe.boundary.AbstractErgebnisGueltigerEingangDAO;
import de.ivu.elect.business.stimmeingabe.boundary.ErgebniseingangKonfiguration;
import de.ivu.elect.business.stimmeingabe.control.ErgebniseingangEingabeHashCodeGenerator;
import de.ivu.elect.business.stimmeingabe.control.ErgebniseingangEingabeHashCodeGenerator.StimmeingabeIndiziert;
import de.ivu.elect.business.stimmeingabe.entity.Ergebniseingang;
import de.ivu.elect.business.stimmeingabe.entity.StimmeingabePlausiErgebniseingangsart;
import de.ivu.elect.business.stimmeingabe.entity.StimmeingabePlausiKonfiguration;
import de.ivu.elect.jee.faces.Messages;

// ELECTVAPP-151, ELECTVAPP-37, #15
public class NlDefaultListenwahlStimmeingabeAnzahlenUngleichStimmeingabeAnzahlenInAnderenErfassungsgebietenPlausi
    extends
      AbstractStimmeingabeAnzahlenUngleichStimmeingabeAnzahlenInAnderenErfassungsgebietenPlausi {

  private static final long ANZAHL_ZU_VERGLEICHEN_LISTEN = ANZAHL_ZU_VERGLEICHEN_ALLE;

  private static final long ANZAHL_ZU_VERGLEICHEN_LISTENKANDIDATUREN_PRO_LISTE = ANZAHL_ZU_VERGLEICHEN_ALLE;

  private final Collection<StimmeingabePlausiErgebniseingangsart> ergebniseingangsartenInWahl;

  public NlDefaultListenwahlStimmeingabeAnzahlenUngleichStimmeingabeAnzahlenInAnderenErfassungsgebietenPlausi(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, Ergebniseingang ergebniseingang,
      AbstractErgebnisGueltigerEingangDAO ergebnisGueltigerEingangDAO, ErgebniseingangKonfiguration ergebniseingangKonfiguration,
      ErgebniseingangEingabeHashCodeGenerator ergebniseingangEingabeHashCodeGenerator, StimmeingabePlausiKonfiguration stimmeingabePlausiKonfiguration, Messages messages) {
    super(wahldurchfuehrungsgebiet, ergebniseingang, ergebnisGueltigerEingangDAO, ergebniseingangEingabeHashCodeGenerator, stimmeingabePlausiKonfiguration, messages);

    this.ergebniseingangsartenInWahl = ergebniseingangKonfiguration.collectErgebniseingangsarten(wahldurchfuehrungsgebiet.getWahldurchfuehrung()).stream()
        .map(StimmeingabePlausiErgebniseingangsart::of).collect(toSet());
  }

  @Override
  protected Collection<StimmeingabePlausiErgebniseingangsart> collectErgebniseingangsartenRelevant() {
    return ergebniseingangsartenInWahl;
  }

  @Override
  protected Gebiet findGebietUebergeordnetRelevant() {
    return getWahldurchfuehrungsgebiet().getWurzelgebiet();
  }

  @Override
  protected String createErgebniseingangEingabeHashCodeSearchPattern(ErgebniseingangEingabeHashCodeGenerator ergebniseingangEingabeHashCodeGenerator,
      Map<Stimmenart, List<StimmeingabeIndiziert>> stimmeingabenPerStimmenart, String wildcardCharacter) {
    long anzahlGruppen = 5L; // Hash-Code NICHT FÜR ALLE GRUPPEN erzeugen, da hintere Stimmzettelpositionen ggf. nicht fortlaufend vorhanden sind, also Lücken aufweisen können!

    return ergebniseingangEingabeHashCodeGenerator.calculateHashCodeSearchPatternByNumberOfGruppen(stimmeingabenPerStimmenart, wildcardCharacter, anzahlGruppen);
  }

  @Override
  protected boolean isStimmeingabenAnzahlenIdentischInternal(Map<Stimmenart, List<StimmeingabeIndiziert>> stimmeingabenPerStimmenart,
      Map<Stimmenart, List<StimmeingabeIndiziert>> stimmeingabenInAnderemErfassungsgebietPerStimmenart) {
    // NOTE ELECT-10467, dass die Anzahlen der Wahlberechtigten IGNORIERT werden müssen, da diese ggf. im System hinterlegt und nicht erfassbar sind!
    return isWaehlerAnzahlenIdentisch(stimmeingabenPerStimmenart, stimmeingabenInAnderemErfassungsgebietPerStimmenart) //
        && isStimmzettelEingenommenAnzahlenIdentisch(stimmeingabenPerStimmenart, stimmeingabenInAnderemErfassungsgebietPerStimmenart) //
        && isStimmzettelEingenommenAbweichendAnzahlenIdentisch(stimmeingabenPerStimmenart, stimmeingabenInAnderemErfassungsgebietPerStimmenart) //
        && isGruppeStimmenAnzahlenIdentisch(stimmeingabenPerStimmenart, stimmeingabenInAnderemErfassungsgebietPerStimmenart) //
    ;
  }

  private boolean isWahlberechtigteAnzahlenIdentisch(Map<Stimmenart, List<StimmeingabeIndiziert>> stimmeingabenPerStimmenart,
      Map<Stimmenart, List<StimmeingabeIndiziert>> stimmeingabenInAnderemErfassungsgebietPerStimmenart) {
    return isSystemGruppeStimmeingabeAnzahlenIdentisch(NlSystemGruppe.WAHLBERECHTIGTE, stimmeingabenPerStimmenart, stimmeingabenInAnderemErfassungsgebietPerStimmenart);
  }

  private boolean isWaehlerAnzahlenIdentisch(Map<Stimmenart, List<StimmeingabeIndiziert>> stimmeingabenPerStimmenart,
      Map<Stimmenart, List<StimmeingabeIndiziert>> stimmeingabenInAnderemErfassungsgebietPerStimmenart) {
    return isSystemGruppeStimmeingabeAnzahlenIdentisch(NlSystemGruppe.WAEHLER, stimmeingabenPerStimmenart, stimmeingabenInAnderemErfassungsgebietPerStimmenart);
  }

  private boolean isStimmzettelEingenommenAnzahlenIdentisch(Map<Stimmenart, List<StimmeingabeIndiziert>> stimmeingabenPerStimmenart,
      Map<Stimmenart, List<StimmeingabeIndiziert>> stimmeingabenInAnderemErfassungsgebietPerStimmenart) {
    return isSystemGruppeStimmeingabeAnzahlenIdentisch(NlSystemGruppe.GUELTIGE, stimmeingabenPerStimmenart, stimmeingabenInAnderemErfassungsgebietPerStimmenart) //
        && isSystemGruppeStimmeingabeAnzahlenIdentisch(NlSystemGruppe.UNGUELTIGE, stimmeingabenPerStimmenart, stimmeingabenInAnderemErfassungsgebietPerStimmenart) //
        && isSystemGruppeStimmeingabeAnzahlenIdentisch(NlSystemGruppe.BLANKO, stimmeingabenPerStimmenart, stimmeingabenInAnderemErfassungsgebietPerStimmenart) //
        && isSystemGruppeStimmeingabeAnzahlenIdentisch(NlSystemGruppe.STIMMZETTEL_EINGENOMMEN, stimmeingabenPerStimmenart, stimmeingabenInAnderemErfassungsgebietPerStimmenart) //
    ;
  }

  private boolean isStimmzettelEingenommenAbweichendAnzahlenIdentisch(Map<Stimmenart, List<StimmeingabeIndiziert>> stimmeingabenPerStimmenart,
      Map<Stimmenart, List<StimmeingabeIndiziert>> stimmeingabenInAnderemErfassungsgebietPerStimmenart) {
    return isSystemGruppeStimmeingabeAnzahlenIdentisch(NlSystemGruppe.STIMMZETTEL_EINGENOMMEN_ABWEICHEND, stimmeingabenPerStimmenart, stimmeingabenInAnderemErfassungsgebietPerStimmenart);
  }

  private boolean isGruppeStimmenAnzahlenIdentisch(Map<Stimmenart, List<StimmeingabeIndiziert>> stimmeingabenPerStimmenart,
      Map<Stimmenart, List<StimmeingabeIndiziert>> stimmeingabenInAnderemErfassungsgebietPerStimmenart) {
    return isGruppeStimmenAnzahlenIdentisch(ANZAHL_ZU_VERGLEICHEN_LISTEN, ANZAHL_ZU_VERGLEICHEN_LISTENKANDIDATUREN_PRO_LISTE, Stimmenart.LISTENSTIMME, stimmeingabenPerStimmenart,
        stimmeingabenInAnderemErfassungsgebietPerStimmenart);
  }

  private boolean isSystemGruppeStimmeingabeAnzahlenIdentisch(NlSystemGruppe nlSystemGruppe, Map<Stimmenart, List<StimmeingabeIndiziert>> stimmeingabenPerStimmenart,
      Map<Stimmenart, List<StimmeingabeIndiziert>> stimmeingabenInAnderemErfassungsgebietPerStimmenart) {
    for (Stimmenart stimmenart : nlSystemGruppe.getStimmenarten()) {
      if (!isSystemGruppeStimmeingabeAnzahlenIdentisch(nlSystemGruppe.getSystemGruppe(), stimmenart, stimmeingabenPerStimmenart, stimmeingabenInAnderemErfassungsgebietPerStimmenart)) {
        return false;
      }
    }

    return true;
  }
}
