
package de.ivu.elect.wus.exchange.wahlvorschlag.csv;

import javax.inject.Inject;

import de.ivu.elect.business.ergebnis.entity.Ergebnis;
import de.ivu.elect.business.ergebnis.entity.Ergebnisart;
import de.ivu.elect.business.ergebnis.entity.Gewaehlt;
import de.ivu.elect.business.ergebnis.entity.GewaehltStatus;
import de.ivu.elect.business.exchange.control.MappingException;
import de.ivu.elect.business.gebietsbaum.boundary.GebietFactory;
import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
import de.ivu.elect.business.gebietsbaum.entity.Gebietsart;
import de.ivu.elect.business.gruppe.boundary.PolitischeGruppeFactory;
import de.ivu.elect.business.gruppe.entity.GruppeArt;
import de.ivu.elect.business.gruppe.entity.PolitischeGruppe;
import de.ivu.elect.business.gruppeimgebiet.boundary.GruppeImGebietFactory;
import de.ivu.elect.business.gruppeimgebiet.entity.GruppeImGebiet;
import de.ivu.elect.business.kandidatur.boundary.DirektkandidaturFactory;
import de.ivu.elect.business.kandidatur.boundary.ListenkandidaturFactory;
import de.ivu.elect.business.kandidatur.control.WahlvorschlagImportContext;
import de.ivu.elect.business.kandidatur.entity.Direktkandidatur;
import de.ivu.elect.business.kandidatur.entity.Kandidatur;
import de.ivu.elect.business.kandidatur.entity.KandidaturAttributePraesentationFormatter;
import de.ivu.elect.business.kandidatur.entity.Listenkandidatur;
import de.ivu.elect.business.kandidatur.entity.ListenkandidaturArt;
import de.ivu.elect.business.mandant.entity.Mandant;
import de.ivu.elect.business.person.boundary.PersonendatenVerknuepfer;
import de.ivu.elect.business.person.entity.VerknuepfungsArt;
import de.ivu.elect.business.person.entity.VerknuepfungsStatus;
import de.ivu.elect.business.wahlvorschlag.boundary.DirektkandidatFactory;
import de.ivu.elect.business.wahlvorschlag.boundary.ListeFactory;
import de.ivu.elect.business.wahlvorschlag.entity.Direktkandidat;
import de.ivu.elect.business.wahlvorschlag.entity.Liste;
import de.ivu.elect.business.wahlvorschlag.entity.Wahlvorschlag;
import de.ivu.elect.jee.entity.customdata.EmptyCustomData;

public abstract class AbstractWahlvorschlagCsvExternInternMapper<T extends WahlvorschlagCsvDTO> {

  @Inject
  protected transient GebietFactory gebietFactory;

  @Inject
  protected transient PolitischeGruppeFactory politischeGruppeFactory;

  @Inject
  protected transient GruppeImGebietFactory gruppeImGebietFactory;

  @Inject
  protected transient ListeFactory listeFactory;

  @Inject
  protected transient ListenkandidaturFactory listenkandidaturFactory;

  @Inject
  protected transient DirektkandidatFactory direktkandidatFactory;

  @Inject
  protected transient DirektkandidaturFactory direktkandidaturFactory;

  @Inject
  protected transient KandidaturAttributePraesentationFormatter kandidaturAttributePraesentationFormatter;

  @Inject
  protected transient PersonendatenVerknuepfer personendatenVerknuepfer;


  public Liste mapToListeOhneListenkandidatur(T wahlvorschlagCsv, WahlvorschlagImportContext context) throws MappingException {
    Liste liste = listeFactory.create();

    fillWahlvorschlag(wahlvorschlagCsv, liste, context);

    return liste;
  }

  protected void fillWahlvorschlag(T wahlvorschlagCsv, Wahlvorschlag wahlvorschlag, WahlvorschlagImportContext context) throws MappingException {
    wahlvorschlag.setGruppeImGebiet(mapToGruppeImGebiet(wahlvorschlagCsv, context));

    wahlvorschlag.setKurzname(wahlvorschlagCsv.getWahlvorschlagKurzname());
    wahlvorschlag.setKennwort(wahlvorschlagCsv.getWahlvorschlagKennwort());
    wahlvorschlag.setZusatzbezeichnung(wahlvorschlagCsv.getWahlvorschlagZusatzbezeichnung());
  }


  protected GruppeImGebiet mapToGruppeImGebiet(T wahlvorschlagCsv, WahlvorschlagImportContext context) throws MappingException {
    GruppeImGebiet gruppeImGebiet = gruppeImGebietFactory.create();

    gruppeImGebiet.setGruppe(mapToGruppe(wahlvorschlagCsv, context));
    gruppeImGebiet.setGebiet(mapToGebiet(wahlvorschlagCsv, context));

    fillPositionenWahlvorschlag(wahlvorschlagCsv, gruppeImGebiet);

    if (gruppeImGebiet.getGruppe().getRelativePosition() == null) {
      gruppeImGebiet.getGruppe().setRelativePosition(gruppeImGebiet.getRelativePosition());
    }

    return gruppeImGebiet;
  }

  protected void fillPositionenWahlvorschlag(T wahlvorschlagCsv, GruppeImGebiet gruppeImGebiet) {
    gruppeImGebiet.setVorgegebenePosition(wahlvorschlagCsv.getWahlvorschlagPosition());
    gruppeImGebiet.setEingereichtePosition(wahlvorschlagCsv.getWahlvorschlagPosition());
    gruppeImGebiet.setRelativePosition(wahlvorschlagCsv.getWahlvorschlagPosition());
  }

  protected Gebiet mapToGebiet(T wahlvorschlagCsv, WahlvorschlagImportContext context) throws MappingException {
    // wenn es nur ein einzigies Listen/Direktwahlgebiet im System gibt,
    // werden alle Listen/Direktkandidaturen automatisch daran angehängt.
    return createGebiet(mapToGebietsart(wahlvorschlagCsv, context), mapToGebietsnummer(wahlvorschlagCsv));
  }

  protected abstract String mapToGebietsart(T wahlvorschlagCsv, WahlvorschlagImportContext context);

  protected String mapToGebietsnummer(T wahlvorschlagCsv) {
    return wahlvorschlagCsv.getGebietsnummer();
  }

  protected Gebiet createGebiet(String gebietsartName, String gebietsnummer) {
    Gebietsart gebietsart = new Gebietsart(); // nicht zum Persistieren, nur zum späteren Wiederfinden anhand des TKs
    gebietsart.setName(gebietsartName);

    Gebiet gebiet = gebietFactory.create();
    gebiet.setGebietsart(gebietsart);
    gebiet.setNummer(gebietsnummer);

    return gebiet;
  }

  protected PolitischeGruppe mapToGruppe(T wahlvorschlagCsv, WahlvorschlagImportContext context) throws MappingException {
    return createPolitischeGruppe(mapToGruppeSchluessel(wahlvorschlagCsv, context.getMandant()), mapToGruppeArt(wahlvorschlagCsv),
        wahlvorschlagCsv.getWahlvorschlagKennwort(),
        wahlvorschlagCsv.getWahlvorschlagKurzname(),
        wahlvorschlagCsv.getWahlvorschlagZusatzbezeichnung());
  }

  protected abstract String mapToGruppeSchluessel(T wahlvorschlagCsv, Mandant mandant);

  protected PolitischeGruppe createPolitischeGruppe(String gruppeSchluessel, GruppeArt gruppeArt, String kennwort, String kurzname, String zusatzbezeichnung) {
    PolitischeGruppe gruppe = createPolitischeGruppeEntity();

    gruppe.setSchluessel(gruppeSchluessel);
    gruppe.setName(kennwort);
    gruppe.setKurzname(kurzname);
    gruppe.setGruppeArt(gruppeArt);
    gruppe.setZusatzbezeichnung(zusatzbezeichnung);

    return gruppe;
  }

  protected PolitischeGruppe createPolitischeGruppeEntity() {
    return politischeGruppeFactory.create();
  }

  protected abstract GruppeArt mapToGruppeArt(T wahlvorschlagCsv) throws MappingException;

  public Listenkandidatur mapToListenkandidatur(T wahlvorschlagCsv, WahlvorschlagImportContext context) throws MappingException {
    return mapToListenkandidatur(wahlvorschlagCsv, context, WahlvorschlagCsvExternInternMapper.DublettenVerarbeitungsAnweisung.ERSTELLE_PERSONEN_VERKNUEPFUNGEN);
  }

  protected Listenkandidatur mapToListenkandidatur(T wahlvorschlagCsv, WahlvorschlagImportContext context, WahlvorschlagCsvExternInternMapper.DublettenVerarbeitungsAnweisung dublettenVerarbeitung)
      throws MappingException {
    Listenkandidatur listenkandidatur = listenkandidaturFactory.create();

    fillPositionenListenkandidatur(wahlvorschlagCsv, listenkandidatur);
    Long kandidaturAnzahlNennungen = wahlvorschlagCsv.getKandidaturAnzahlNennungen();
    listenkandidatur.setAnzahlNennungen(kandidaturAnzahlNennungen);

    if (kandidaturAnzahlNennungen != null) { //hasKandidaturDubletteListenposition
      listenkandidatur.setListenkandidaturArt(kandidaturAnzahlNennungen == 0 ? ListenkandidaturArt.ERSATZKANDIDATUR : ListenkandidaturArt.LISTENKANDIDATUR);
    }

    fillKandidatur(wahlvorschlagCsv, listenkandidatur, context, dublettenVerarbeitung);

    return listenkandidatur;
  }

  protected void fillPositionenListenkandidatur(T wahlvorschlagCsv, Listenkandidatur listenkandidatur) {
    listenkandidatur.setRelativePosition(wahlvorschlagCsv.getKandidaturListenposition());
    listenkandidatur.setVorgegebenePosition(wahlvorschlagCsv.getKandidaturListenposition());
  }

  public Direktkandidat mapToDirektkandidat(T wahlvorschlagCsv, WahlvorschlagImportContext context) throws MappingException {
    Direktkandidat direktkandidat = direktkandidatFactory.create();

    fillWahlvorschlag(wahlvorschlagCsv, direktkandidat, context);
    direktkandidat.setDirektkandidatur(mapToDirektkandidatur(wahlvorschlagCsv, context, WahlvorschlagCsvExternInternMapper.DublettenVerarbeitungsAnweisung.ERSTELLE_PERSONEN_VERKNUEPFUNGEN));

    return direktkandidat;
  }

  protected Direktkandidatur mapToDirektkandidatur(T wahlvorschlagCsv, WahlvorschlagImportContext context, WahlvorschlagCsvExternInternMapper.DublettenVerarbeitungsAnweisung dublettenVerarbeitung)
      throws MappingException {
    Direktkandidatur direktkandidatur = direktkandidaturFactory.create();

    fillKandidatur(wahlvorschlagCsv, direktkandidatur, context, dublettenVerarbeitung);

    return direktkandidatur;
  }

  protected void fillKandidatur(T wahlvorschlagCsv, Kandidatur kandidatur, WahlvorschlagImportContext context, WahlvorschlagCsvExternInternMapper.DublettenVerarbeitungsAnweisung dublettenVerarbeitung)
      throws MappingException {
    kandidatur.setTitel(wahlvorschlagCsv.getKandidaturTitel());
    kandidatur.setFamiliennameBestandteil(wahlvorschlagCsv.getKandidaturNamensbestandteile());
    kandidatur.setFamilienname(wahlvorschlagCsv.getKandidaturFamilienname());
    fillRufnameUndVornamen(wahlvorschlagCsv, kandidatur);

    // NOTE, dass die Namensbestandteile des Familiennamens momentan nicht in den Daten enthalten sind!
    kandidatur.setNamePraesentation(
        kandidaturAttributePraesentationFormatter.formatNamePraesentation(kandidatur.getTitel(), kandidatur.getRufname(), kandidatur.getVornamen(),
            kandidatur.getFamilienname(), kandidatur.getFamiliennameBestandteil(), kandidatur.getKuenstlername()));

    kandidatur.setGeburtsdatum(wahlvorschlagCsv.getKandidaturGeburtsdatum(context.getLocale()));

    kandidatur.setBerufUnformatiert(wahlvorschlagCsv.getKandidaturBeruf());
    kandidatur.setExterneId(wahlvorschlagCsv.getExterneId());

    fillGewaehlteVorperiode(wahlvorschlagCsv, kandidatur);
  }

  protected void fillRufnameUndVornamen(T wahlvorschlagCsv, Kandidatur kandidatur) {
    kandidatur.setRufname(wahlvorschlagCsv.getKandidaturVorname());
    kandidatur.setVornamen(wahlvorschlagCsv.getKandidaturVorname());
  }

  protected enum DublettenVerarbeitungsAnweisung {
    ERSTELLE_PERSONEN_VERKNUEPFUNGEN,
    IGNORIERE_DUBLETTEN
  }

  protected void fillDublette(T wahlvorschlagCsv, Kandidatur kandidatur, WahlvorschlagImportContext context, DublettenVerarbeitungsAnweisung dublettenVerarbeitung) throws MappingException {
    if (dublettenVerarbeitung.equals(WahlvorschlagCsvExternInternMapper.DublettenVerarbeitungsAnweisung.ERSTELLE_PERSONEN_VERKNUEPFUNGEN) && wahlvorschlagCsv.hasKandidaturDublette()) {
      personendatenVerknuepfer.verknuepfeManuell(VerknuepfungsArt.WAHLVORSCHLAG_DUBLETTE, kandidatur, mapToVerknuepfteKandidatur(wahlvorschlagCsv, context), VerknuepfungsStatus.IDENTISCH);
    }
  }

  protected Kandidatur mapToVerknuepfteKandidatur(T wahlvorschlagCsv, WahlvorschlagImportContext context) throws MappingException {
    Kandidatur result;

    // NOTE, dass die GruppeImGebiet nur gesetzt wird, damit der Importer später den TK zuordnen kann
    GruppeImGebiet gruppeImGebiet = gruppeImGebietFactory.create();
    gruppeImGebiet.setGebiet(mapKandidaturDubletteToGebiet(wahlvorschlagCsv));
    gruppeImGebiet.setGruppe(mapKandidaturDubletteToGruppe(wahlvorschlagCsv, context));

    if (wahlvorschlagCsv.hasKandidaturDubletteListenposition()) {
      Listenkandidatur listenkandidatur = mapToListenkandidatur(wahlvorschlagCsv, context, DublettenVerarbeitungsAnweisung.IGNORIERE_DUBLETTEN);

      listenkandidatur.setRelativePosition(wahlvorschlagCsv.getKandidaturDubletteListenposition());
      listenkandidatur.setVorgegebenePosition(wahlvorschlagCsv.getKandidaturDubletteListenposition());

      // NOTE, dass die Liste nur gesetzt wird, damit der Importer später den TK zuordnen kann
      Liste liste = listeFactory.create();
      liste.setGruppeImGebiet(gruppeImGebiet);
      listenkandidatur.setListe(liste);

      result = listenkandidatur;
    } else {
      Direktkandidatur direktkandidatur = mapToDirektkandidatur(wahlvorschlagCsv, context, DublettenVerarbeitungsAnweisung.IGNORIERE_DUBLETTEN);

      // NOTE, dass der Direktkandidat nur gesetzt wird, damit der Importer später den TK zuordnen kann
      Direktkandidat direktkandidat = direktkandidatFactory.create();
      direktkandidat.setGruppeImGebiet(gruppeImGebiet);
      direktkandidatur.setDirektkandidat(direktkandidat);

      result = direktkandidatur;
    }

    return result;
  }

  private Gebiet mapKandidaturDubletteToGebiet(T wahlvorschlagCsv) {
    return createGebiet(wahlvorschlagCsv.getKandidaturDubletteGebietsart(), mapToKandidaturDubletteGebietsnummer(wahlvorschlagCsv));
  }

  protected String mapToKandidaturDubletteGebietsnummer(T wahlvorschlagCsv) {
    return wahlvorschlagCsv.getKandidaturDubletteGebietsnummer();
  }

  protected PolitischeGruppe mapKandidaturDubletteToGruppe(T wahlvorschlagCsv, WahlvorschlagImportContext context) {
    String gruppeSchluessel = wahlvorschlagCsv.hasKandidaturDubletteGruppeSchluessel() ? mapToKandidaturDubletteGruppeSchluessel(wahlvorschlagCsv) : mapToGruppeSchluessel(wahlvorschlagCsv,
        context.getMandant());

    if (wahlvorschlagCsv.isEinzelbewerber()) {
      return createPolitischeGruppe(gruppeSchluessel, GruppeArt.EINZELBEWERBER, wahlvorschlagCsv.getWahlvorschlagKennwort(),
          wahlvorschlagCsv.getWahlvorschlagKurzname(), wahlvorschlagCsv.getWahlvorschlagZusatzbezeichnung());
    }

    return createPolitischeGruppe(gruppeSchluessel, GruppeArt.PARTEI, wahlvorschlagCsv.getWahlvorschlagKennwort(), wahlvorschlagCsv.getWahlvorschlagKurzname(),
        wahlvorschlagCsv.getWahlvorschlagZusatzbezeichnung());
  }

  protected String mapToKandidaturDubletteGruppeSchluessel(T wahlvorschlagCsv) {
    return wahlvorschlagCsv.getKandidaturDubletteGruppeSchluessel();
  }

  protected void fillGewaehlteVorperiode(T wahlvorschlagCsv, Kandidatur kandidatur) {
    if (wahlvorschlagCsv.getGewaehltVorperiode() == null) {
      return;
    }

    if (!kandidatur.hasGewaehltVorperiode()) {
      Gewaehlt gewaehlt = new Gewaehlt(EmptyCustomData.INSTANCE);
      kandidatur.setGewaehltVorperiode(gewaehlt);

      Ergebnis ergebnis = new Ergebnis();
      ergebnis.setErgebnisart(Ergebnisart.VORPERIODE);
      gewaehlt.setErgebnis(ergebnis);
    }
    kandidatur.getGewaehltVorperiode().setGewaehltStatus(GewaehltStatus.valueOf(wahlvorschlagCsv.getGewaehltVorperiode()));
  }
}
