
package de.ivu.elect.was.exchange.ergebnis.csv;

import static org.apache.commons.lang3.StringUtils.trimToEmpty;

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.jsefa.csv.CsvSerializer;

import de.ivu.elect.business.anschrift.entity.Anschrift;
import de.ivu.elect.business.datei.entity.DateiFileNames;
import de.ivu.elect.business.ergebnis.control.ErgebnisDaten;
import de.ivu.elect.business.ergebnis.control.ErgebnisExportContext;
import de.ivu.elect.business.exchange.entity.DateiExportArt;
import de.ivu.elect.business.gebietsbaum.entity.Wahldurchfuehrungsgebiet;
import de.ivu.elect.business.gruppe.entity.Gruppe;
import de.ivu.elect.business.kandidatur.entity.Kandidatur;
import de.ivu.elect.business.kandidatur.entity.Listenkandidatur;
import de.ivu.elect.business.kandidatur.entity.NlWasListenkandidatur;
import de.ivu.elect.business.stimmzettel.entity.EntityAufStimmzettelDaten;
import de.ivu.elect.business.wahl.entity.NlWahlPhase;
import de.ivu.elect.business.wahlvorschlag.entity.Liste;
import de.ivu.elect.business.wahlvorschlag.entity.NlListe;
import de.ivu.elect.business.wahlvorschlag.entity.WahlvorschlagStimmzettelPosition;
import de.ivu.elect.nl.eml.readable.ListSubmissionMode;

public class Osv55ErgebnisCsvExportMapper extends AbstractNlCsvErgebnisExportMapper {

  private static final long serialVersionUID = 1L;

  public static final String FILE_FORMAT_NAME = "file.format.ergebnisdokument.export.osv55.main";

  public Osv55ErgebnisCsvExportMapper() {
    super();
  }

  @Override
  public boolean isAcceptWahlPhase(NlWahlPhase nlWahlPhase) {
    return nlWahlPhase.isFreigabe();
  }

  @Override
  protected String getFileFormatName() {
    return FILE_FORMAT_NAME;
  }

  @Override
  public DateiFileNames getDateiExportFileNames(DateiExportArt dateiExportArt, ErgebnisExportContext context) {
    return null;
  }

  @Override
  protected List<Gruppe> getGruppen(ErgebnisDaten ergebnisDaten) {
    return getGruppenMitMindestensEinenSitzSortiertNachListenPosition(ergebnisDaten);
  }

  @Override
  protected void writeData(CsvSerializer serializer, ErgebnisDaten ergebnisDaten, List<Gruppe> gruppenListe, Map<Listenkandidatur, Long> listenkandidaturenSortiertNachStimmenanzahl) {
    EntityAufStimmzettelDaten entityAufStimmzettelDaten = ergebnisDaten.getEntityAufStimmzettelDaten();

    for (Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet : ergebnisDaten.getListenWahlGebiete()) {
      for (Gruppe gruppe : gruppenListe) {
        listenkandidaturenSortiertNachStimmenanzahl.forEach((listenkandidatur, anzahlStimmen) -> {
          Liste liste = listenkandidatur.getListe();

          if (wahldurchfuehrungsgebiet.getNummer().equals(liste.getGebiet().getNummer())) {
            if (listenkandidatur.getGruppe().equals(gruppe)) {
              if (ergebnisDaten.getListenWahlGebiete().size() > 1) {
                writeField(serializer, liste.getGebiet().getNummer());
              }

              WahlvorschlagStimmzettelPosition wahlvorschlagStimmzettelPosition = liste.getStimmzettelPosition(entityAufStimmzettelDaten);
              writeLong(serializer, wahlvorschlagStimmzettelPosition.getPosition());
              writeField(serializer, gruppe.getName());
              if (ergebnisDaten.getListenWahlGebiete().size() > 1) {
                writeField(serializer, getTyp(ergebnisDaten, liste));
              }
              writeField(serializer, istGeschlechtPubliziert(liste));

              if (NlListe.isPublicationLanguageFriesisch(liste)) {
                writeField(serializer, NlListe.PUBLICATION_LANGUAGE_FRIESISCH);
              } else {
                writeField(serializer, NlListe.PUBLICATION_LANGUAGE_NIEDERLAENDISCH);
              }

              NlWasListenkandidatur nlListenkandidatur = new NlWasListenkandidatur(listenkandidatur);

              writeLong(serializer, nlListenkandidatur.getStimmzettelPosition(wahldurchfuehrungsgebiet, entityAufStimmzettelDaten).getPosition());
              writeField(serializer, trimToEmpty(trimToEmpty(nlListenkandidatur.getFamiliennameBestandteil()) + " " + trimToEmpty(nlListenkandidatur.getFamilienname()))); // ELECTVAPP-1372
              writeField(serializer, nlListenkandidatur.getInitialen());
              writeField(serializer, nlListenkandidatur.getVorname());

              Anschrift wohnanschrift = nlListenkandidatur.getWohnanschrift();

              writeField(serializer, wohnanschrift.getOrt());
              writeField(serializer, wohnanschrift.getStaat());

              writeField(serializer, getAbkuerzungFuerGeschlecht(nlListenkandidatur.getGeschlecht()));

              Anschrift erreichbarkeitsanschrift = nlListenkandidatur.getErreichbarkeitsanschrift();

              writeField(serializer, erreichbarkeitsanschrift.getStrasse());
              writeField(serializer, erreichbarkeitsanschrift.getPlz());
              writeField(serializer, erreichbarkeitsanschrift.getOrt());
              writeField(serializer, erreichbarkeitsanschrift.getStaat());

              nextLine(serializer);
            }
          }
        });
      }
    }
  }

  @Override
  protected Map<Listenkandidatur, Long> getListenkandidaturSortiert(ErgebnisDaten ergebnisDaten, List<Kandidatur> kandidaturen, List<Gruppe> gruppenListe) {
    long anzahlStimmen;

    LinkedHashMap<Listenkandidatur, Long> sorierteListenKandidaturen = new LinkedHashMap<>();

    for (Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet : ergebnisDaten.getListenWahlGebiete()) {
      Map<Listenkandidatur, Long> map = new HashMap<>();
      List<Kandidatur> kandidaturenZumGebiet = filterKandidaturenZumGebiet(kandidaturen, wahldurchfuehrungsgebiet);

      for (Kandidatur kandidatur : kandidaturenZumGebiet) {
        if (kandidatur.isInstanceOf(Listenkandidatur.class) && kandidatur.getGruppe().isInstanceOf(Gruppe.class) && gruppenListe.contains(kandidatur.getGruppe())) {
          Listenkandidatur listenkandidatur = kandidatur.castTo(Listenkandidatur.class).getEntity();

          if (ergebnisDaten.getStimmenFuerListenkandidatur(listenkandidatur, wahldurchfuehrungsgebiet) != null) {
            anzahlStimmen = ergebnisDaten.getStimmenFuerListenkandidatur(listenkandidatur, wahldurchfuehrungsgebiet);

            if (anzahlStimmen >= 1) {
              map.put(listenkandidatur, anzahlStimmen);
            }
          }
        }
      }

      Map<Listenkandidatur, Long> nachStimmenSortierteKandidaturen = map.entrySet().stream().sorted(Map.Entry.<Listenkandidatur, Long> comparingByValue().reversed())
          .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (oldValue, newValue) -> oldValue, LinkedHashMap::new));

      sorierteListenKandidaturen.putAll(nachStimmenSortierteKandidaturen);
    }

    return sorierteListenKandidaturen;
  }

  private String getTyp(ErgebnisDaten ergebnisDaten, Liste liste) {
    String type;

    if (NlListe.isListeEinesStelWas(liste)) {
      type = ListSubmissionMode.SET_OF_LISTS.getValue();
    } else if (NlListe.isEinzelneListe(liste, ergebnisDaten.getListen())) {
      type = ListSubmissionMode.SINGLELIST.getValue();
    } else {
      type = ListSubmissionMode.LISTGROUP.getValue();
    }

    return type;
  }

  @Override
  protected void writeIntro(ErgebnisDaten ergebnisDaten, CsvSerializer serializer) {// ELECTVAPP-53
    writeField(serializer, "Overzicht Kandidaatgegevens");
    nextLine(serializer);

    writeField(serializer, "Alleen de gegevens van lijsten die tenminste 1 zetel hebben behaald staan in het overzicht");
    nextLine(serializer);

    writeField(serializer, "Verkiezing: " + ergebnisDaten.getWahl().getName());
    nextLine(serializer);
  }

  @Override
  protected void writeHeader(CsvSerializer serializer, ErgebnisDaten ergebnisDaten) {
    nextLine(serializer);

    if (ergebnisDaten.getListenWahlGebiete().size() > 1) {
      writeField(serializer, "Kieskring");
    }

    writeField(serializer, "Lijstnummer");
    writeField(serializer, "Politieke Groepering");

    if (ergebnisDaten.getListenWahlGebiete().size() > 1) {
      writeField(serializer, "Type");
    }

    writeField(serializer, "Publiceer geslacht");
    writeField(serializer, "Taal");
    writeField(serializer, "Nr.");
    writeField(serializer, "Achternaam");
    writeField(serializer, "Voorletters");
    writeField(serializer, "Roepnaam");
    writeField(serializer, "Woonplaats");
    writeField(serializer, "Land");
    writeField(serializer, "Geslacht");
    writeField(serializer, "(Corresp.)Adres");
    writeField(serializer, "Postcode");
    writeField(serializer, "Woonplaats");
    writeField(serializer, "Land");
  }
}
