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

import static java.util.Arrays.asList;

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.boundary.AbstractGebietDAO;
import de.ivu.elect.business.gebietsbaum.boundary.AbstractGebietsgruppierungDAO;
import de.ivu.elect.business.gebietsbaum.boundary.WahlverwaltungGebietInformation;
import de.ivu.elect.business.gebietsbaum.boundary.WahlverwaltungGebietInformationResolver;
import de.ivu.elect.business.gebietsbaum.boundary.WahlverwaltungGebietInformationResolverContext;
import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
import de.ivu.elect.business.gebietsbaum.entity.Wahldurchfuehrungsgebiet;
import de.ivu.elect.business.stimmeingabe.boundary.AbstractErgebnisGueltigerEingangDAO;
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.business.wahl.entity.AbstractWahlParameterLoader;
import de.ivu.elect.jee.faces.Messages;

// ELECTMUC-2452, ELECTKOM-308
public class EuwStimmeingabeAnzahlenUngleichStimmeingabeAnzahlenInAnderenErfassungsgebietenPlausi extends AbstractStimmeingabeAnzahlenUngleichStimmeingabeAnzahlenInAnderenErfassungsgebietenPlausi {

  private final WahlverwaltungGebietInformationResolver wahlverwaltungGebietInformationResolver;

  private final WahlverwaltungGebietInformationResolverContext wahlverwaltungGebietInformationResolverContext;

  public EuwStimmeingabeAnzahlenUngleichStimmeingabeAnzahlenInAnderenErfassungsgebietenPlausi(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, Ergebniseingang ergebniseingang,
      AbstractWahlParameterLoader wahlParameterLoader, AbstractGebietDAO gebietDAO, AbstractGebietsgruppierungDAO gebietsgruppierungDAO,
      AbstractErgebnisGueltigerEingangDAO ergebnisGueltigerEingangDAO, WahlverwaltungGebietInformationResolver wahlverwaltungGebietInformationResolver,
      ErgebniseingangEingabeHashCodeGenerator ergebniseingangEingabeHashCodeGenerator, StimmeingabePlausiKonfiguration stimmeingabePlausiKonfiguration, Messages messages) {
    super(wahldurchfuehrungsgebiet, ergebniseingang, ergebnisGueltigerEingangDAO, ergebniseingangEingabeHashCodeGenerator, stimmeingabePlausiKonfiguration, messages);

    this.wahlverwaltungGebietInformationResolver = wahlverwaltungGebietInformationResolver;
    this.wahlverwaltungGebietInformationResolverContext = new WahlverwaltungGebietInformationResolverContext(wahldurchfuehrungsgebiet.getWahldurchfuehrung(), wahlParameterLoader, gebietDAO,
        gebietsgruppierungDAO);
  }

  @Override
  protected Collection<StimmeingabePlausiErgebniseingangsart> collectErgebniseingangsartenRelevant() {
    // Bei Anpassungen auch EuwStimmeingabePlausiDefinitions anpassen!!!
    return asList(StimmeingabePlausiErgebniseingangsart.VORLAEUFIG, StimmeingabePlausiErgebniseingangsart.ENDGUELTIG);
  }

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

  private WahlverwaltungGebietInformation findKreisInformation() {
    return wahlverwaltungGebietInformationResolver.createDescriptor(wahlverwaltungGebietInformationResolverContext, getWahldurchfuehrungsgebiet()).getKreis();
  }

  @Override
  protected String createErgebniseingangEingabeHashCodeSearchPattern(ErgebniseingangEingabeHashCodeGenerator ergebniseingangEingabeHashCodeGenerator,
      Map<Stimmenart, List<StimmeingabeIndiziert>> stimmeingabenPerStimmenart, String wildcardCharacter) {
    long anzahlGruppenRelevant = 2; // == D01 & D02 -> D10 wird dann später überprüft...

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

  @Override
  protected boolean isStimmeingabenAnzahlenIdentischInternal(Map<Stimmenart, List<StimmeingabeIndiziert>> stimmeingabenPerStimmenart,
      Map<Stimmenart, List<StimmeingabeIndiziert>> stimmeingabenInAnderemErfassungsgebietPerStimmenart) {
    return isWahlberechtigteAnzahlenIdentisch(stimmeingabenPerStimmenart, stimmeingabenInAnderemErfassungsgebietPerStimmenart) //
        && isWaehlerAnzahlenIdentisch(stimmeingabenPerStimmenart, stimmeingabenInAnderemErfassungsgebietPerStimmenart) //
        && isUngueltigeStimmenAnzahlenIdentisch(stimmeingabenPerStimmenart, stimmeingabenInAnderemErfassungsgebietPerStimmenart) //
        && isGueltigeStimmenAnzahlenIdentisch(stimmeingabenPerStimmenart, stimmeingabenInAnderemErfassungsgebietPerStimmenart) //
        && isGruppeStimmenAnzahlenIdentisch(stimmeingabenPerStimmenart, stimmeingabenInAnderemErfassungsgebietPerStimmenart) //
    ;
  }

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

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

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

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

  private boolean isGruppeStimmenAnzahlenIdentisch(Map<Stimmenart, List<StimmeingabeIndiziert>> stimmeingabenPerStimmenart,
      Map<Stimmenart, List<StimmeingabeIndiziert>> stimmeingabenInAnderemErfassungsgebietPerStimmenart) {
    return isGruppeStimmenAnzahlenAnPosition01Identisch(stimmeingabenPerStimmenart, stimmeingabenInAnderemErfassungsgebietPerStimmenart) // D01
        && isGruppeStimmenAnzahlenAnPosition02Identisch(stimmeingabenPerStimmenart, stimmeingabenInAnderemErfassungsgebietPerStimmenart) // D02
        && isGruppeStimmenAnzahlenAnPosition10Identisch(stimmeingabenPerStimmenart, stimmeingabenInAnderemErfassungsgebietPerStimmenart) // D10
    ;
  }

  private boolean isGruppeStimmenAnzahlenAnPosition01Identisch(Map<Stimmenart, List<StimmeingabeIndiziert>> stimmeingabenPerStimmenart,
      Map<Stimmenart, List<StimmeingabeIndiziert>> stimmeingabenInAnderemErfassungsgebietPerStimmenart) {
    return isGruppeStimmenAnzahlenAnIndexIdentisch(0, stimmeingabenPerStimmenart, stimmeingabenInAnderemErfassungsgebietPerStimmenart);
  }

  private boolean isGruppeStimmenAnzahlenAnPosition02Identisch(Map<Stimmenart, List<StimmeingabeIndiziert>> stimmeingabenPerStimmenart,
      Map<Stimmenart, List<StimmeingabeIndiziert>> stimmeingabenInAnderemErfassungsgebietPerStimmenart) {
    return isGruppeStimmenAnzahlenAnIndexIdentisch(1, stimmeingabenPerStimmenart, stimmeingabenInAnderemErfassungsgebietPerStimmenart);
  }

  private boolean isGruppeStimmenAnzahlenAnPosition10Identisch(Map<Stimmenart, List<StimmeingabeIndiziert>> stimmeingabenPerStimmenart,
      Map<Stimmenart, List<StimmeingabeIndiziert>> stimmeingabenInAnderemErfassungsgebietPerStimmenart) {
    return isGruppeStimmenAnzahlenAnIndexIdentisch(9, stimmeingabenPerStimmenart, stimmeingabenInAnderemErfassungsgebietPerStimmenart);
  }

  private boolean isGruppeStimmenAnzahlenAnIndexIdentisch(int indexZeroBased, Map<Stimmenart, List<StimmeingabeIndiziert>> stimmeingabenPerStimmenart,
      Map<Stimmenart, List<StimmeingabeIndiziert>> stimmeingabenInAnderemErfassungsgebietPerStimmenart) {
    return isGruppeStimmenAnzahlenAnIndexIdentisch(indexZeroBased, Stimmenart.LISTENSTIMME, stimmeingabenPerStimmenart, stimmeingabenInAnderemErfassungsgebietPerStimmenart);
  }
}
