
package de.ivu.elect.business.stimmeingabe.boundary;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import javax.annotation.Nullable;
import javax.inject.Inject;

import de.ivu.elect.business.WusFeaturesAktiviert;
import de.ivu.elect.business.ergebnis.entity.Stimmenart;
import de.ivu.elect.business.gruppe.entity.SystemGruppe;
import de.ivu.elect.business.gruppe.entity.SystemGruppeEigenschaften;
import de.ivu.elect.business.gruppeimgebiet.control.GruppeImGebietByRelativePositionComparator;
import de.ivu.elect.business.gruppeimgebiet.control.GruppeImGebietByVorgegebenePositionComparator;
import de.ivu.elect.business.gruppeimgebiet.entity.GruppeImGebiet;
import de.ivu.elect.business.kandidatur.entity.Listenkandidatur;
import de.ivu.elect.business.stimmeingabe.control.StimmeingabeErfassenContext;
import de.ivu.elect.business.stimmeingabe.entity.Stimmeingabe;
import de.ivu.elect.business.stimmeingabe.entity.Stimmeingabefehler;
import de.ivu.elect.business.stimmeingabe.entity.ZwischensummeEingabe;
import de.ivu.elect.business.wahlvorschlag.entity.ReferendumOption;
import de.ivu.elect.jee.entity.tk.EntityTK;
import de.ivu.elect.jee.faces.Messages;

public abstract class AbstractStimmeingabeModelBuilder implements Serializable {

  private static final long serialVersionUID = 1L;

  @Inject
  private transient Messages messages;

  @Inject
  private transient SystemGruppeEigenschaften systemGruppeEigenschaften;

  @Inject
  private transient WusFeaturesAktiviert wusFeaturesAktiviert;

  protected AbstractStimmeingabeModelBuilder() {
    super();
  }

  protected boolean isStimmenartApplicable(GruppeImGebiet gruppeImGebiet, Stimmenart stimmenart, StimmeingabeErfassenContext context) {
    if (!gruppeImGebiet.isSystemGruppe()) {
      if (stimmenart.isDirektstimme()) {
        return gruppeImGebiet.hatDirektkandidatAufStimmzettelInUebergeordneterGebietshierarchie(context.getWahlvorschlaege());
      }

      if (stimmenart.isListenstimme()) {
        return gruppeImGebiet.hatListeAufStimmzettelInUebergeordneterGebietshierarchie(context.getWahlvorschlaege());
      }

      if (stimmenart.isReferendumStimmenart()) {
        return gruppeImGebiet.hatReferendumOptionAufStimmzettelInUebergeordneterGebietshierarchie(context.getWahlvorschlaege());
      }
    }

    return true;
  }

  protected SystemGruppeEigenschaften getSystemGruppeEigenschaften() {
    return systemGruppeEigenschaften;
  }

  @Nullable
  protected Stimmeingabe createStimmeingabeIfStimmenartApplicable(GruppeImGebiet gruppeImGebiet, Stimmenart stimmenart, StimmeingabeErfassenContext context) {
    if (isStimmenartApplicable(gruppeImGebiet, stimmenart, context)) {
      return createStimmeingabe(gruppeImGebiet, null, null, stimmenart, context);
    }

    return null;
  }

  protected Stimmeingabe createStimmeingabe(GruppeImGebiet gruppeImGebiet, @Nullable Listenkandidatur listenkandidatur, ReferendumOption referendumOption, Stimmenart stimmenart,
      StimmeingabeErfassenContext context) {
    Stimmeingabe stimmeingabe = new Stimmeingabe();

    stimmeingabe.setGruppeImGebiet(gruppeImGebiet);
    stimmeingabe.setErgebniseingang(context.getErgebniseingang());
    stimmeingabe.setStimmenart(stimmenart);

    stimmeingabe.setListenkandidatur(listenkandidatur);
    stimmeingabe.setReferendumOption(referendumOption);

    presetStimmeingabeAnzahlEingabe(stimmeingabe, context);

    return stimmeingabe;
  }

  protected ZwischensummeEingabe createZwischensummeEingabe(GruppeImGebiet gruppeImGebiet, Stimmenart stimmenart, Long summenIndex, String summenName, Collection<Stimmeingabe> stimmeingaben,
      StimmeingabeErfassenContext context) {
    ZwischensummeEingabe zwischensummeEingabe = new ZwischensummeEingabe();

    zwischensummeEingabe.setGruppeImGebiet(gruppeImGebiet);
    zwischensummeEingabe.setErgebniseingang(context.getErgebniseingang());
    zwischensummeEingabe.setStimmenart(stimmenart);

    zwischensummeEingabe.setSummenIndex(summenIndex);
    zwischensummeEingabe.setSummenName(summenName);

    zwischensummeEingabe.addAbhaengigeStimmeingaben(stimmeingaben);

    presetStimmeingabeAnzahlEingabe(zwischensummeEingabe, context);

    return zwischensummeEingabe;
  }

  private void presetStimmeingabeAnzahlEingabe(Stimmeingabe stimmeingabe, StimmeingabeErfassenContext context) {
    if (stimmeingabe != null) {
      Map<EntityTK, Stimmeingabe> stimmeingabeLetzterErgebniseingangPerTk = context.getLetzterErgebniseingangAktuellStimmeingabenPerTk();

      if (stimmeingabeLetzterErgebniseingangPerTk.containsKey(stimmeingabe.getTk())) {
        stimmeingabe.setAnzahlEingabe(stimmeingabeLetzterErgebniseingangPerTk.get(stimmeingabe.getTk()).getAnzahlEingabe());
      }
    }
  }

  protected Stimmeingabe getStimmeingabeVergleich(Stimmeingabe stimmeingabe, StimmeingabeErfassenContext context) {
    if (stimmeingabe != null) {
      return context.getVergleichErgebniseingangStimmeingabenPerTk().get(stimmeingabe.getTk());
    }

    return null;
  }

  protected List<Stimmeingabefehler> getStimmeingabefehlerLetzterErgebniseingang(@Nullable Stimmeingabe stimmeingabe, StimmeingabeErfassenContext context) {
    if (stimmeingabe != null) {
      Map<EntityTK, List<Stimmeingabefehler>> stimmeingabefehlerLetzterErgebniseingangPerTk = context.getLetzterErgebniseingangAktuellStimmeingabefehlerPerTk();

      if (stimmeingabefehlerLetzterErgebniseingangPerTk.containsKey(stimmeingabe.getTk())) {
        return stimmeingabefehlerLetzterErgebniseingangPerTk.get(stimmeingabe.getTk());
      }
    }

    return Collections.emptyList();
  }

  protected String formatGruppeStimmenKennung(GruppeImGebiet gruppeImGebiet, StimmeingabeErfassenContext context) {
    return context.getGruppeStimmenErfassenLabelGenerator().getGruppeStimmenKennung(context.getEntityAufStimmzettelDaten(), gruppeImGebiet, context.getErgebniseingangsart(),
        context.getGruppenImGebiet());
  }

  protected String formatGruppeStimmenBezeichnung(GruppeImGebiet gruppeImGebiet, StimmeingabeErfassenContext context) {
    return context.getGruppeStimmenErfassenLabelGenerator().getGruppeStimmenBezeichnung(context.getEntityAufStimmzettelDaten(), gruppeImGebiet, context.getErgebniseingangsart(),
        context.getGruppenImGebiet());
  }

  protected boolean isSystemGruppeWahlberechtigte(GruppeImGebiet gruppeImGebiet) {
    return equalsSystemGruppe(gruppeImGebiet, SystemGruppe.WAHLBERECHTIGTE);
  }

  protected boolean isSystemGruppeWaehler(GruppeImGebiet gruppeImGebiet) {
    return equalsSystemGruppe(gruppeImGebiet, SystemGruppe.WAEHLER);
  }

  protected boolean isSystemGruppeGueltige(GruppeImGebiet gruppeImGebiet) {
    return equalsSystemGruppe(gruppeImGebiet, SystemGruppe.GUELTIGE);
  }

  protected boolean isSystemGruppeUngueltige(GruppeImGebiet gruppeImGebiet) {
    return equalsSystemGruppe(gruppeImGebiet, SystemGruppe.UNGUELTIGE);
  }

  protected boolean isSystemGruppeUebrige(GruppeImGebiet gruppeImGebiet) {
    return equalsSystemGruppe(gruppeImGebiet, SystemGruppe.UEBRIGE);
  }

  protected boolean equalsSystemGruppe(GruppeImGebiet gruppeImGebiet, SystemGruppe expected) {
    return expected.equalsGruppe(gruppeImGebiet.getGruppe(), getSystemGruppeEigenschaften());
  }

  protected List<GruppeImGebiet> sortiereGruppenImGebietNachPosition(Collection<GruppeImGebiet> gruppenImGebiet) {
    List<GruppeImGebiet> result = new ArrayList<>(gruppenImGebiet);

    if (/* Quick-hack für 128 Testfälle */ wusFeaturesAktiviert == null || wusFeaturesAktiviert.isNurWas() || isAlleGigHabenVorgegebenePosition(gruppenImGebiet)) {
      // im reinen WAS alles wie bisher
      // NOTE: Nicht die abgeleitete Position verwenden -- ist langsam und wird schon beim Wahlimport aufgelöst!
      // FALSCH, muss vorgegebene Position sein wegen EU Wahl...sort(result, new GruppeImGebietByRelativePositionComparator());
      result.sort(new GruppeImGebietByVorgegebenePositionComparator());
    } else {
      result.sort(new GruppeImGebietByRelativePositionComparator());
    }

    return result;
  }

  private boolean isAlleGigHabenVorgegebenePosition(Collection<GruppeImGebiet> gruppenImGebiet) {
    return gruppenImGebiet.stream().noneMatch(gruppeImGebiet -> gruppeImGebiet.getGruppe().isPolitischeGruppe() && gruppeImGebiet.getVorgegebenePosition() == null);
  }

  protected String getMessage(String msgKey, Object... params) {
    return getMessages().getMessageForMessageKey(msgKey, params);
  }

  protected Messages getMessages() {
    return messages;
  }
}
