
package de.ivu.elect.was.sitzberechnung.control;

import java.util.Collection;

import javax.annotation.Nonnull;
import javax.inject.Inject;

import de.ivu.elect.business.ergebnis.control.StimmergebnisDaten;
import de.ivu.elect.business.gebietsbaum.entity.Wahldurchfuehrungsgebiet;
import de.ivu.elect.business.kandidatur.boundary.WaehlerBenannteKandidaturKonfiguration;
import de.ivu.elect.business.kandidatur.entity.Kandidatur;
import de.ivu.elect.business.kandidatur.entity.WaehlerBenannteKandidaturen;
import de.ivu.elect.business.sitzberechnung.control.Sitzberechnungsalgorithmus;
import de.ivu.elect.business.sitzberechnung.entity.SitzberechnungsalgorithmusKonfiguration;
import de.ivu.elect.business.sitzberechnung.entity.Sitzberechnungseingabe;
import de.ivu.elect.business.sitzberechnung.entity.Sitzberechnungsergebnis;
import de.ivu.elect.business.wahl.entity.AbstractWahlParameterLoader;
import de.ivu.elect.business.wahl.entity.Wahl;
import de.ivu.elect.business.wahl.entity.WahlParameterWerte;
import de.ivu.elect.business.wahl.entity.WahlartErgebnisartVorlaeufigErfassungsart;
import de.ivu.elect.business.wahlvorschlag.entity.Wahlvorschlag;
import de.ivu.elect.jee.faces.Messages;
import de.ivu.elect.jee.presentation.Icons;
import de.ivu.elect.presentation.kandidatur.table.KandidaturGewaehltKeinSitzberechnungsalgorithmusColumnBuilder;
import de.ivu.elect.presentation.kandidatur.table.KandidaturGewaehltSitzberechnungsalgorithmusColumnBuilder;
import de.ivu.elect.was.sitzberechnung.entity.ElectSitzberechnungEntities;

public abstract class AbstractElectSitzberechnungsalgorithmus<K extends SitzberechnungsalgorithmusKonfiguration> implements Sitzberechnungsalgorithmus<K> {

  @Inject
  private transient WaehlerBenannteKandidaturKonfiguration waehlerBenannteKandidaturKonfiguration;

  @Inject
  private transient ElectSitzberechnungEntitiesImWahlgebietMapper sitzberechnungEntitiesImWahlgebietMapper;

  @Inject
  private transient ElectSitzberechnungseingabeMapper sitzberechnungseingabeMapper;

  @Inject
  private transient ElectSitzberechnungsergebnisMapper sitzberechnungsergebnisMapper;

  protected AbstractElectSitzberechnungsalgorithmus() {
    super();
  }

  @Override
  public boolean isBerechnetErgebnis() {
    return true;
  }

  @Override
  public boolean isBerechnetErgebnisFallsWaehlerBenannteWahlvorschlaegeSonstigeErfasst() {
    return false;
  }

  @Override
  public Sitzberechnungsergebnis berechneSitzverteilung(Sitzberechnungseingabe<K> sitzberechnungseingabe) {
    return berechneSitzverteilung(getContext(sitzberechnungseingabe), sitzberechnungseingabe);
  }

  protected abstract Sitzberechnungsergebnis berechneSitzverteilung(ElectSitzberechnungContext sitzberechnungContext, Sitzberechnungseingabe<K> sitzberechnungseingabe);

  protected ElectSitzberechnungEntities mapSitzberechnungEntitiesImWahlgebiet(ElectSitzberechnungContext sitzberechnungContext, Wahldurchfuehrungsgebiet wahlgebiet,
                                                                              Sitzberechnungseingabe<K> sitzberechnungseingabe) {
    return sitzberechnungEntitiesImWahlgebietMapper.mapExtern(sitzberechnungContext, wahlgebiet, sitzberechnungseingabe, getStaatMapper(),
        this::getWaehlbarkeit,
        kandidatur -> isWaehlerBenannt(sitzberechnungContext, kandidatur), kandidatur -> isWaehlerBenanntSonstige(sitzberechnungContext, kandidatur));
  }

  protected de.ivu.elect.sitzberechnung.Sitzberechnungseingabe mapSitzberechnungseingabeExtern(ElectSitzberechnungContext sitzberechnungContext, ElectSitzberechnungEntities sitzberechnungEntities,
                                                                                               Sitzberechnungseingabe<K> sitzberechnungseingabe) {
    StimmergebnisDaten stimmergebnisDaten = sitzberechnungseingabe.getStimmergebnisDaten();

    return sitzberechnungseingabeMapper.mapExtern(sitzberechnungContext, sitzberechnungEntities,
        getErgebnisKonfliktMapper(sitzberechnungContext, sitzberechnungEntities),
        getAnzahlSitzeMapper(sitzberechnungContext, stimmergebnisDaten, sitzberechnungEntities),
        getAnzahlStimmenMapper(sitzberechnungContext, stimmergebnisDaten, sitzberechnungEntities),
        sitzberechnungseingabe);
  }

  protected Sitzberechnungsergebnis mapSitzberechnungsergebnisIntern(ElectSitzberechnungContext sitzberechnungContext, ElectSitzberechnungEntities sitzberechnungEntities,
                                                                     de.ivu.elect.sitzberechnung.Sitzberechnungsergebnis sitzberechnungsergebnis) {
    return sitzberechnungsergebnisMapper.mapIntern(sitzberechnungContext, sitzberechnungEntities,
        getErgebnisKonfliktMapper(sitzberechnungContext, sitzberechnungEntities),
        getGewaehltMapper(sitzberechnungContext, sitzberechnungEntities), sitzberechnungsergebnis);
  }

  protected ElectSitzberechnungContext getContext(Sitzberechnungseingabe<K> sitzberechnungseingabe) {
    K konfiguration = sitzberechnungseingabe.getKonfiguration();

    return new ElectSitzberechnungContext(getSitzberechnungsalgorithmusEnum(), sitzberechnungseingabe.getKonfiguration(), sitzberechnungseingabe.getErgebnis(),
        konfiguration.getMessages(),
        konfiguration.getErgebnisKonfliktCustomDataFormatter());
  }

  protected ElectAnzahlSitzeMapper getAnzahlSitzeMapper(ElectSitzberechnungContext sitzberechnungContext, StimmergebnisDaten stimmergebnisDaten, ElectSitzberechnungEntities sitzberechnungEntities) {
    return new AbstractElectAnzahlSitzeMapper() {
    };
  }

  protected ElectAnzahlStimmenMapper getAnzahlStimmenMapper(ElectSitzberechnungContext sitzberechnungContext, StimmergebnisDaten stimmergebnisDaten,
                                                            ElectSitzberechnungEntities sitzberechnungEntities) {
    return new AbstractElectAnzahlStimmenMapper(stimmergebnisDaten) {
    };
  }

  protected ElectGewaehltMapper getGewaehltMapper(ElectSitzberechnungContext sitzberechnungContext, ElectSitzberechnungEntities sitzberechnungEntities) {
    return new AbstractElectGewaehltMapper() {
    };
  }

  protected ElectErgebnisKonfliktMapper getErgebnisKonfliktMapper(ElectSitzberechnungContext sitzberechnungContext, ElectSitzberechnungEntities sitzberechnungEntities) {
    return new AbstractElectErgebnisKonfliktMapper(sitzberechnungContext.getErgebnisKonfliktCustomDataFormatter()) {
    };
  }

  protected ElectStaatMapper getStaatMapper() {
    return new ElectStaatMapper();
  }

  protected abstract de.ivu.elect.sitzberechnung.Waehlbarkeit getWaehlbarkeit(Kandidatur kandidatur);

  @Override
  public KandidaturGewaehltSitzberechnungsalgorithmusColumnBuilder getKandidaturGewaehltColumnBuilder(Messages messages, Icons icons) {
    return new KandidaturGewaehltKeinSitzberechnungsalgorithmusColumnBuilder(messages, icons);
  }

  @Override
  public boolean isErstelltDHondtQuotientPolitischeGruppe() {
    return false;
  }

  @Override
  public boolean isErstelltDHondtQuotientListenInGruppe() {
    return false;
  }

  @Override
  public boolean isErstelltDHondtQuotientListenInListenverbindung() {
    return false;
  }

  @Override
  public boolean isErstelltDHondtQuotientListenImGebiet() {
    return false;
  }

  @Override
  public boolean isWaehlerBenannteWahlvorschlaegeErfassbar(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, Collection<? extends Wahlvorschlag> wahlvorschlaege) {
    return getWaehlerBenannteKandidaturKonfiguration().isWaehlerBenannteWahlvorschlaegeManuellErfassbar(wahldurchfuehrungsgebiet, () -> wahlvorschlaege);
  }

  protected boolean isWaehlerBenannt(ElectSitzberechnungContext sitzberechnungContext, Kandidatur kandidatur) {
    return WaehlerBenannteKandidaturen.isWaehlerBenannteDirektkandidatur(kandidatur) || WaehlerBenannteKandidaturen.isWaehlerBenannteListenkandidatur(
        kandidatur);
  }

  protected boolean isWaehlerBenanntSonstige(ElectSitzberechnungContext sitzberechnungContext, Kandidatur kandidatur) {
    return WaehlerBenannteKandidaturen.isWaehlerBenannteListenkandidaturSonstige(sitzberechnungContext.getMessages(), kandidatur);
  }

  protected WaehlerBenannteKandidaturKonfiguration getWaehlerBenannteKandidaturKonfiguration() {
    return waehlerBenannteKandidaturKonfiguration;
  }

  @Nonnull
  protected WahlartErgebnisartVorlaeufigErfassungsart resolveWahlartErgebnisartVorlaeufigErfassungsartConfigured(AbstractWahlParameterLoader wahlParameterLoader, Wahl wahl) {
    WahlartErgebnisartVorlaeufigErfassungsart result = wahlParameterLoader.resolveEnumValue(wahl,
        WahlParameterWerte.STIMMEINGABE_WAHLART_ERGEBNISART_VORLAEUFIG_ERFASSUNGSART,
        WahlartErgebnisartVorlaeufigErfassungsart.class);

    if (result == null) {
      result = WahlartErgebnisartVorlaeufigErfassungsart.ERGEBNIS_KOMPLETT;
    }

    return result;
  }
}
