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

import de.ivu.elect.NlCsvExportMapper;
import de.ivu.elect.business.CurrentCustomer;
import de.ivu.elect.business.datei.entity.CsvMaskierungszeichen;
import de.ivu.elect.business.datei.entity.CsvZeichensatz;
import de.ivu.elect.business.datei.entity.FileType;
import de.ivu.elect.business.ergebnis.control.ErgebnisDaten;
import de.ivu.elect.business.ergebnis.control.ErgebnisExportContext;
import de.ivu.elect.business.ergebnis.control.ErgebnisExportObject;
import de.ivu.elect.business.ergebnis.entity.Sitze;
import de.ivu.elect.business.exchange.control.FileFormat;
import de.ivu.elect.business.exchange.control.FileKonfiguration;
import de.ivu.elect.business.exchange.control.MappingException;
import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
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.person.entity.Geschlecht;
import de.ivu.elect.business.security.Customer;
import de.ivu.elect.business.wahlvorschlag.entity.Liste;
import de.ivu.elect.business.wahlvorschlag.entity.NlListe;
import de.ivu.elect.exchange.ExportMapperProvidingDateiFileNames;
import de.ivu.elect.exchange.csv.raw.AbstractRawCsvExportMapper;
import de.ivu.elect.jee.faces.Messages;
import de.ivu.elect.was.exchange.ergebnis.ErgebnisExportMapper;
import org.jsefa.csv.CsvSerializer;
import org.jsefa.csv.lowlevel.config.QuoteMode;

import javax.inject.Inject;
import java.io.OutputStream;
import java.util.*;
import java.util.stream.Collectors;

public abstract class AbstractNlCsvErgebnisExportMapper extends AbstractRawCsvExportMapper implements ErgebnisExportMapper, ExportMapperProvidingDateiFileNames<ErgebnisExportObject, ErgebnisExportContext> , NlCsvExportMapper {

    private static final long serialVersionUID = 1L;

    @Inject
    private Messages messages;


    @Inject
    private transient CurrentCustomer currentCustomer;// ELECTVAPP-933

    @Override
    public boolean isInjectSave() {
        return true; // ELECTVAPP-754
    }

    @Override
    public QuoteMode getQuoteMode() {
        return QuoteMode.ALWAYS; // ELECTVAPP-883
    }

    @Override
    public boolean isTrennzeichenNachLetztemFeld() {
        return false; // ELECTVAPP-883
    }


    protected Customer getCustomer() {
        return currentCustomer.getCustomer();// ELECTVAPP-933
    }

    @Override
    public CsvMaskierungszeichen setMaskierungszeichen(){
        return CsvMaskierungszeichen.QUOTE_DOPPELT;
    }
    @Override
    public CsvZeichensatz setCsvZeichensatz() { //ELECTVAPP-1251;
        return CsvZeichensatz.UTF_8_BOM;
    }


    @Override
    public void serialize(ErgebnisExportObject object, OutputStream os, ErgebnisExportContext context, FileKonfiguration fileKonfiguration) throws MappingException {
        CsvSerializer serializer = openSerializer(os);
        ErgebnisDaten ergebnisDaten = object.getErgebnisDaten();

        writeIntro(ergebnisDaten, serializer);
        writeHeader(serializer, ergebnisDaten);
        nextLine(serializer);

        List<Kandidatur> kandidaturen = ergebnisDaten.getKandidaturen();
        List<Gruppe> gruppenListe = getGruppen(ergebnisDaten);

        Map<Listenkandidatur, Long> listenkandidaturenSortiert =
                getListenkandidaturSortiert(ergebnisDaten, kandidaturen, gruppenListe);

        writeData(serializer, ergebnisDaten, gruppenListe, listenkandidaturenSortiert);

        closeSerializer(serializer);

    }

    protected abstract List<Gruppe> getGruppen(ErgebnisDaten ergebnisDaten);


    protected abstract void writeData(CsvSerializer serializer, ErgebnisDaten ergebnisDaten,
                                      List<Gruppe> gruppenListe, Map<Listenkandidatur, Long> listenkandidaturenSortiertNachStimmenanzahl);

    protected List<Gruppe> getGruppenMitMindestensEinenSitzSortiertNachListenPosition(ErgebnisDaten ergebnisDaten) {
        HashMap<Gruppe, Long> map = new HashMap<>();
        for (Sitze sitz : ergebnisDaten.getSitze()) {
            if (map.containsKey(sitz.getGruppe())) {
                map.put(sitz.getGruppe(), map.get(sitz.getGruppe()) + sitz.getAnzahl());
            } else {
                map.put(sitz.getGruppe(), sitz.getAnzahl());

            }
        }
        return map.entrySet().stream().filter(e -> e.getValue() >= 1).sorted(Comparator.comparingLong(one -> one.getKey().getVorgegebenePosition())).
                map(Map.Entry::getKey).collect(Collectors.toList());
    }

    public Messages getMessages() {
        return messages;
    }

    public String getAbkuerzungFuerGeschlecht(Geschlecht geschlecht) {
        if (geschlecht == null) {
            return "";
        }
        if (geschlecht.isMaennlich()) {
            return getMessages().getMessageForMessageKey("Geschlecht.MAENNLICH.kuerzel").toLowerCase();
        }
        if (geschlecht.isWeiblich()) {
            return getMessages().getMessageForMessageKey("Geschlecht.WEIBLICH.kuerzel").toLowerCase();

        }
        if (geschlecht.isDivers()) {
            return getMessages().getMessageForMessageKey("Geschlecht.DIVERS.kuerzel").toLowerCase();
        }
        return "";
    }

    public String istGeschlechtPubliziert(Liste liste) {//  keine "Friesische"-Inkarnation
        if (NlListe.isPublishGender(liste)) {
            return "Ja";
        } else {
            return "Nee";
        }
    }

    protected abstract Map<Listenkandidatur, Long> getListenkandidaturSortiert(ErgebnisDaten ergebnisDaten, List<Kandidatur> kandidaturen, List<Gruppe> gruppenListe);

    protected List<Kandidatur> filterKandidaturenZumGebiet(List<Kandidatur> kandidaturen, Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet) {
        String kieskringNummer = wahldurchfuehrungsgebiet.getNummer();
        List<Kandidatur> list = new ArrayList<>();
        for (Kandidatur e : kandidaturen) {
            if (e.isInstanceOf(Listenkandidatur.class)) {
                Liste liste = e.castTo(Listenkandidatur.class).getEntity().getListe();
                Gebiet gebiet = liste.getGebiet();
                if (gebiet.getNummer().equals(kieskringNummer)) {
                    list.add(e);
                }
            }
        }
        return list;
    }

    @Override
    public FileFormat getFileFormat() {
        return new FileFormat(getFileFormatName(), FileType.CSV);
    }

    @Override
    public boolean isAcceptContext(ErgebnisExportContext context) {
        return true;
    }

    @Override
    protected abstract String getFileFormatName();

    @Override
    protected boolean closeOutputStreamOnCloseSerializer() {
        return true;
    }

    protected abstract void writeIntro(ErgebnisDaten ergebnisDaten, CsvSerializer serializer);

    protected abstract void writeHeader(CsvSerializer serializer, ErgebnisDaten ergebnisDaten);

}

