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

import static java.util.Collections.emptyList;

import java.util.List;

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

import de.ivu.elect.business.ergebnis.entity.Stimmenart;
import de.ivu.elect.business.gebietsbaum.entity.Wahldurchfuehrungsgebiet;
import de.ivu.elect.business.gruppeimgebiet.entity.GruppeImGebiet;
import de.ivu.elect.business.stimmeingabe.control.StimmeingabeErfassenContext;
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.Stimmeingabefehler;
import de.ivu.elect.business.stimmeingabe.entity.ZwischensummeEingabe;
import de.ivu.elect.business.wahlvorschlag.entity.WahlvorschlagStimmzettelPosition;

public abstract class AbstractStimmeingabeGruppeStimmenModelBuilder extends AbstractStimmeingabeModelBuilder {

  private static final long serialVersionUID = 1L;

  @Inject
  protected transient StimmeingabeGruppeStimmenErfassenModelLayout stimmeingabeGruppeStimmenErfassenModelLayout;

  protected AbstractStimmeingabeGruppeStimmenModelBuilder() {
    super();
  }

  public StimmeingabeGruppeStimmenErfassenModel createModelStimmeingabeGruppeStimmen(StimmeingabeErfassenContext context) {
    StimmeingabeGruppeStimmenErfassenModel result = createStimmeingabeGruppeStimmenErfassenModel(context.getWahldurchfuehrungsgebiet());

    for (GruppeImGebiet gruppeImGebiet : sortiereGruppenImGebietNachPosition(context.getGruppenImGebiet())) {
      if (gruppeImGebiet.isSystemGruppe()) {
        initializeModelStimmeingabeSystemGruppeStimmen(result, gruppeImGebiet, context);
      } else if (isWahlvorschlagVorhanden(gruppeImGebiet, context)) { // ELECT-5268
        initializeModelStimmeingabePolitischeGruppeStimmen(result, gruppeImGebiet, context);
      }
    }

    context.getErgebniseingang().setStimmeingabeGruppeStimmenErfassenModel(result);

    return result;
  }

  protected StimmeingabeGruppeStimmenErfassenModel createStimmeingabeGruppeStimmenErfassenModel(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet) {
    return new StimmeingabeGruppeStimmenErfassenModel(wahldurchfuehrungsgebiet);
  }

  protected boolean isWahlvorschlagVorhanden(GruppeImGebiet gruppeImGebiet, StimmeingabeErfassenContext context) {
    return true; // LEGACY, to be overridden...
  }

  protected void initializeModelStimmeingabeSystemGruppeStimmen(StimmeingabeGruppeStimmenErfassenModel result, GruppeImGebiet gruppeImGebiet, StimmeingabeErfassenContext context) {
    if (isSystemGruppeGueltige(gruppeImGebiet)) {
      result.setStimmenGruppeGueltige(createStimmeingabeGruppeStimmen(gruppeImGebiet, context));
    } else if (isSystemGruppeUngueltige(gruppeImGebiet)) {
      result.setStimmenGruppeUngueltige(createStimmeingabeGruppeStimmen(gruppeImGebiet, context));
    }
  }

  public abstract List<StimmeingabeGruppeStimmen> layoutModelStimmeingabeSystemGruppeStimmen(StimmeingabeGruppeStimmenErfassenModel model, StimmeingabeGruppeStimmenErfassenModelContext context);

  protected void initializeModelStimmeingabePolitischeGruppeStimmen(StimmeingabeGruppeStimmenErfassenModel result, GruppeImGebiet gruppeImGebiet, StimmeingabeErfassenContext context) {
    StimmeingabeGruppeStimmen stimmeingabeGruppeStimmen = createStimmeingabeGruppeStimmen(gruppeImGebiet, context);

    if (stimmeingabeGruppeStimmen.hasStimmeingaben()) {
      result.getStimmenPolitischeGruppen().add(stimmeingabeGruppeStimmen);
    }
  }

  public abstract List<StimmeingabeGruppeStimmen> layoutModelStimmeingabePolitischeGruppeStimmen(StimmeingabeGruppeStimmenErfassenModel model, StimmeingabeGruppeStimmenErfassenModelContext context);

  public List<StimmeingabeGruppeStimmen> layoutModelStimmeingabeSystemGruppeStimmenBottom(StimmeingabeGruppeStimmenErfassenModel model, StimmeingabeGruppeStimmenErfassenModelContext context) {
    return getStimmeingabeGruppeStimmenErfassenModelLayout().layoutSystemGruppeStimmenKeine(model, context);
  }

  protected StimmeingabeGruppeStimmen createStimmeingabeGruppeStimmen(GruppeImGebiet gruppeImGebiet, StimmeingabeErfassenContext context) {
    Stimmeingabe stimmeingabeDirektstimme = createStimmeingabeIfStimmenartApplicable(gruppeImGebiet, Stimmenart.DIREKTSTIMME, context);
    Stimmeingabe stimmeingabeListenstimme = createStimmeingabeIfStimmenartApplicable(gruppeImGebiet, Stimmenart.LISTENSTIMME, context);

    // NOTE, dass wir hier auch für nicht zugelassene GruppenImGebiet (oder überhaupt immer dann, wenn BEIDE obigen
    // Stimmeingaben NULL sind) ein Objekt erzeugen -- mit allen LabelGenerator-Aufrufen, die dazugehören. Nötig?

    return createStimmeingabeGruppeStimmen(gruppeImGebiet, stimmeingabeDirektstimme, stimmeingabeListenstimme, context);
  }

  protected StimmeingabeGruppeStimmen createStimmeingabeGruppeStimmen(GruppeImGebiet gruppeImGebiet, @Nullable Stimmeingabe stimmeingabeDirektstimme, @Nullable Stimmeingabe stimmeingabeListenstimme,
      StimmeingabeErfassenContext context) {
    String kennung = formatGruppeStimmenKennung(gruppeImGebiet, context);
    String bezeichnung = formatGruppeStimmenBezeichnung(gruppeImGebiet, context);

    String stimmeingabeDirektstimmeGruppeBezeichnung = formatGruppeStimmenStimmeingabeGruppeBezeichnung(stimmeingabeDirektstimme, context);
    String stimmeingabeDirektstimmeKennung = formatGruppeStimmenStimmeingabeKennung(stimmeingabeDirektstimme, context);
    String stimmeingabeDirektstimmeBezeichnung = formatGruppeDirektstimmenStimmeingabeBezeichnung(stimmeingabeDirektstimme, context);
    String stimmeingabeDirektstimmeTooltip = formatGruppeDirektstimmenStimmeingabeTooltip(stimmeingabeDirektstimme, context);
    List<Stimmeingabefehler> stimmeingabefehlerDirektstimmeLetzterErgebniseingang = getStimmeingabefehlerLetzterErgebniseingang(stimmeingabeDirektstimme, context);
    Stimmeingabe stimmeingabeDirektstimmeVergleich = getStimmeingabeVergleich(stimmeingabeDirektstimme, context);

    String stimmeingabeListenstimmeGruppeBezeichnung = formatGruppeStimmenStimmeingabeGruppeBezeichnung(stimmeingabeListenstimme, context);
    String stimmeingabeListenstimmeKennung = formatGruppeStimmenStimmeingabeKennung(stimmeingabeListenstimme, context);
    String stimmeingabeListenstimmeBezeichnung = formatGruppeListenstimmenStimmeingabeBezeichnung(stimmeingabeListenstimme, context);
    String stimmeingabeListenstimmeTooltip = formatGruppeListenstimmenStimmeingabeTooltip(stimmeingabeListenstimme, context);
    List<Stimmeingabefehler> stimmeingabefehlerListenstimmeLetzterErgebniseingang = getStimmeingabefehlerLetzterErgebniseingang(stimmeingabeListenstimme, context);
    Stimmeingabe stimmeingabeListenstimmeVergleich = getStimmeingabeVergleich(stimmeingabeListenstimme, context);

    WahlvorschlagStimmzettelPosition wahlvorschlagStimmzettelPosition = getStimmzettelPosition(gruppeImGebiet, context);

    return new StimmeingabeGruppeStimmen(wahlvorschlagStimmzettelPosition, kennung, bezeichnung, //
        stimmeingabeDirektstimme, stimmeingabeDirektstimmeGruppeBezeichnung, stimmeingabeDirektstimmeKennung, stimmeingabeDirektstimmeBezeichnung, stimmeingabeDirektstimmeTooltip,
        stimmeingabefehlerDirektstimmeLetzterErgebniseingang, stimmeingabeDirektstimmeVergleich, //

        stimmeingabeListenstimme, stimmeingabeListenstimmeGruppeBezeichnung, stimmeingabeListenstimmeKennung, stimmeingabeListenstimmeBezeichnung, stimmeingabeListenstimmeTooltip,
        stimmeingabefehlerListenstimmeLetzterErgebniseingang, stimmeingabeListenstimmeVergleich //
    );
  }

  protected StimmeingabeGruppeStimmen createZwischensummeEingabeGruppeStimmen(GruppeImGebiet gruppeImGebiet, Long direktstimmeSummenIndex, String direktstimmeSummenName, Long listenstimmeSummenIndex,
      String listenstimmeSummenName, StimmeingabeErfassenContext context) {
    ZwischensummeEingabe zwischensummeEingabeDirektstimme = tryCreateZwischensummeEingabeIfStimmenartApplicable(gruppeImGebiet, Stimmenart.DIREKTSTIMME, direktstimmeSummenIndex,
        direktstimmeSummenName, context);
    ZwischensummeEingabe zwischensummeEingabeListenstimme = tryCreateZwischensummeEingabeIfStimmenartApplicable(gruppeImGebiet, Stimmenart.LISTENSTIMME, listenstimmeSummenIndex,
        listenstimmeSummenName, context);

    return createStimmeingabeGruppeStimmen(gruppeImGebiet, zwischensummeEingabeDirektstimme, zwischensummeEingabeListenstimme, context);
  }

  protected String formatGruppeStimmenStimmeingabeGruppeBezeichnung(Stimmeingabe stimmeingabe, StimmeingabeErfassenContext context) {
    return context.getGruppeStimmenErfassenLabelGenerator().getGruppeStimmenStimmeingabeGruppeBezeichnung(context.getEntityAufStimmzettelDaten(), stimmeingabe, context.getGruppenImGebiet());
  }

  protected String formatGruppeStimmenStimmeingabeKennung(Stimmeingabe stimmeingabe, StimmeingabeErfassenContext context) {
    return context.getGruppeStimmenErfassenLabelGenerator().getGruppeStimmenStimmeingabeKennung(context.getEntityAufStimmzettelDaten(), stimmeingabe, context.getGruppenImGebiet());
  }

  protected String formatGruppeDirektstimmenStimmeingabeBezeichnung(Stimmeingabe stimmeingabe, StimmeingabeErfassenContext context) {
    return context.getGruppeStimmenErfassenLabelGenerator().getGruppeDirektstimmenStimmeingabeBezeichnung(context.getEntityAufStimmzettelDaten(), stimmeingabe, context.getGruppenImGebiet(),
        context.getWahlvorschlaege());
  }

  protected String formatGruppeDirektstimmenStimmeingabeTooltip(Stimmeingabe stimmeingabe, StimmeingabeErfassenContext context) {
    return context.getGruppeStimmenErfassenLabelGenerator().getGruppeDirektstimmenStimmeingabeTooltip(context.getEntityAufStimmzettelDaten(), stimmeingabe, context.getGruppenImGebiet(),
        context.getWahlvorschlaege());
  }

  protected String formatGruppeListenstimmenStimmeingabeBezeichnung(Stimmeingabe stimmeingabe, StimmeingabeErfassenContext context) {
    return context.getGruppeStimmenErfassenLabelGenerator().getGruppeStimmenStimmeingabeBezeichnung(context.getEntityAufStimmzettelDaten(), stimmeingabe, context.getGruppenImGebiet());
  }

  protected String formatGruppeListenstimmenStimmeingabeTooltip(Stimmeingabe stimmeingabe, StimmeingabeErfassenContext context) {
    return context.getGruppeStimmenErfassenLabelGenerator().getGruppeStimmenStimmeingabeTooltip(context.getEntityAufStimmzettelDaten(), stimmeingabe, context.getGruppenImGebiet());
  }

  @Nullable
  protected WahlvorschlagStimmzettelPosition getStimmzettelPosition(GruppeImGebiet gruppeImGebiet, StimmeingabeErfassenContext context) {
    if (gruppeImGebiet == null || gruppeImGebiet.isSystemGruppe() || !gruppeImGebiet.hatWahlvorschlagAufStimmzettelInUebergeordneterGebietshierarchie(context.getWahlvorschlaege())) { // Cf. ELECT-3253 (Übergehen unnötiger GruppeImGebiet)
      return null;
    }

    WahlvorschlagStimmzettelPosition wahlvorschlagStimmzettelPosition = context.getGruppeStimmenErfassenLabelGenerator().getWahlvorschlagStimmzettelPosition(context.getEntityAufStimmzettelDaten(),
        gruppeImGebiet, context.getGruppenImGebiet());

    if (wahlvorschlagStimmzettelPosition != null && wahlvorschlagStimmzettelPosition.hasPosition()) {
      return wahlvorschlagStimmzettelPosition;
    }

    return null;
  }

  protected StimmeingabeGruppeStimmenErfassenModelLayout getStimmeingabeGruppeStimmenErfassenModelLayout() {
    return stimmeingabeGruppeStimmenErfassenModelLayout;
  }

  @Nullable
  protected ZwischensummeEingabe tryCreateZwischensummeEingabeIfStimmenartApplicable(GruppeImGebiet gruppeImGebiet, Stimmenart stimmenart, Long summenIndex, String summenName,
      StimmeingabeErfassenContext context) {
    if (isStimmenartApplicable(gruppeImGebiet, stimmenart, context)) {
      return createZwischensummeEingabe(gruppeImGebiet, stimmenart, summenIndex, summenName, emptyList(), context);
    }

    return null;
  }
}
