
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.DateiFileNames;
import de.ivu.elect.business.datei.entity.FileType;
import de.ivu.elect.business.ergebnis.control.*;
import de.ivu.elect.business.ergebnis.entity.Stimmabgabeart;
import de.ivu.elect.business.exchange.control.FileFormat;
import de.ivu.elect.business.exchange.control.FileKonfiguration;
import de.ivu.elect.business.exchange.entity.DateiExportArt;
import de.ivu.elect.business.gebietsbaum.boundary.GebietDAO;
import de.ivu.elect.business.gebietsbaum.boundary.NlGebietDarstellungNummerFormatter;
import de.ivu.elect.business.gebietsbaum.entity.NlGebietsart;
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.security.Customer;
import de.ivu.elect.business.stimmeingabe.boundary.NlVoteType;
import de.ivu.elect.business.stimmeingabe.control.NlStimmeingabeKopfzahlKonfiguration;
import de.ivu.elect.business.wahl.entity.*;
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.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import static de.ivu.elect.business.hochrechnung.control.StimmenHochrechnerHilfsfunktionen.notNull;
import static de.ivu.elect.business.wahl.entity.NlWahlart.valueOf;
import static org.apache.commons.lang3.StringUtils.containsIgnoreCase;

public class Osv43ErgebnisCsvExportMapper extends AbstractRawCsvExportMapper implements ErgebnisExportMapper, ExportMapperProvidingDateiFileNames<ErgebnisExportObject, ErgebnisExportContext>, NLCsvExportMapper {

    private static final long serialVersionUID = 1L;

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

    @Inject
    private transient GebietDAO gebietDAO;

    @Inject
    private transient CurrentCustomer currentCustomer;

    @Inject
    private transient Messages messages;

    @Inject
    private transient NlStimmeingabeKopfzahlKonfiguration stimmeingabeKopfzahlKonfiguration;

    @Inject
    private transient NlGebietDarstellungNummerFormatter nlGebietDarstellungNummerFormatter;


    public Osv43ErgebnisCsvExportMapper() {
        super();
    }

    @Override
    public boolean isAcceptContext(ErgebnisExportContext context) {
        return context.getWahl() != null && !context.getWahl().getCustomData(NlWahlCustomData.class).getNlWahlPhase().equals(NlWahlPhase.ERFASSUNG);
    }

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

    @Override
    public DateiFileNames getDateiExportFileNames(DateiExportArt dateiExportArt, ErgebnisExportContext context) {
        return new Osv43ErgebnisCsvExportFileNames(getMessages(), dateiExportArt, getCustomer(), getGebietDAO());
    }

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

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

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

    @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) {
        CsvSerializer serializer = openSerializer(os);
        ErgebnisDaten ergebnisDaten = object.getErgebnisDaten();

        writeIntro(ergebnisDaten, serializer, context);
        List<Wahldurchfuehrungsgebiet> gebiete = ergebnisDaten.getGebiete();
        writeHeader(serializer, ergebnisDaten, gebiete);
        writeData(serializer, ergebnisDaten, gebiete);

        closeSerializer(serializer);
    }

    protected void writeData(CsvSerializer serializer, ErgebnisDaten ergebnisDaten, List<Wahldurchfuehrungsgebiet> gebiete) {
        NlErgebnisDaten nlErgebnisDaten = new NlErgebnisDaten(ergebnisDaten);

        writeKopfzahlen(serializer, ergebnisDaten, gebiete, nlErgebnisDaten);

        LinkedHashMap<Gruppe, List<Listenkandidatur>> nachGruppenPositionSortierteMap = ergebnisDaten.getListenkandidaturenNachGruppe().entrySet().stream().
                sorted(Comparator.comparingLong(entry -> entry.getKey().getRelativePosition())).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,
                (oldValue, newValue) -> oldValue, LinkedHashMap::new));

        nachGruppenPositionSortierteMap.forEach((gruppe, listenkandidaturList) -> {
            writeHeaderOfGruppe(serializer, ergebnisDaten, gebiete, gruppe, listenkandidaturList);

            ArrayList<Kandidatur> removeKandidatur = new ArrayList<>();

            listenkandidaturList.forEach(listenkandidatur -> {
                berechneKumulierteStimmenFuerListenkandidatur(serializer, ergebnisDaten, gebiete, removeKandidatur, listenkandidatur);
            });
        });

    }

    private void berechneKumulierteStimmenFuerListenkandidatur(CsvSerializer serializer, ErgebnisDaten ergebnisDaten, List<Wahldurchfuehrungsgebiet> gebiete, ArrayList<Kandidatur> removeKandidatur, Listenkandidatur listenkandidatur) {
        if (!removeKandidatur.contains(listenkandidatur)) {
            nextLine(serializer);
            writeField(serializer, "");
            writeField(serializer, "");

            Collection<Kandidatur> identischVerknuepfteKandidaturen = listenkandidatur.getIdentischVerknuepfteKandidaturen();
            identischVerknuepfteKandidaturen.add(listenkandidatur);//ELECTVAPP-931
            NlWasListenkandidatur nlListenkandidatur = new NlWasListenkandidatur(listenkandidatur);
            ueberpruefeStimmzettelPositionIdentischVerkuepfterKandidaturen(serializer, ergebnisDaten, identischVerknuepfteKandidaturen, nlListenkandidatur);

            String familienbestandteil = nlListenkandidatur.getFamiliennameBestandteil();//ELECTVAPP-929
            if (familienbestandteil != null) {
                writeField(serializer, familienbestandteil + " " + nlListenkandidatur.getFamilienname() + ", " + nlListenkandidatur.getInitialen());//ELECT-1132
            } else {
                writeField(serializer, nlListenkandidatur.getFamilienname() + ", " + nlListenkandidatur.getInitialen());
            }
            removeKandidatur.addAll(identischVerknuepfteKandidaturen);
            getKumulierteStimmenIdentischVerknuepfterListenkandidaturen(serializer, ergebnisDaten, gebiete, identischVerknuepfteKandidaturen);
        }
    }

    private void getKumulierteStimmenIdentischVerknuepfterListenkandidaturen(CsvSerializer serializer, ErgebnisDaten ergebnisDaten, List<Wahldurchfuehrungsgebiet> gebiete, Collection<Kandidatur> identischVerknuepfteKandidaturen) {

        for (Wahldurchfuehrungsgebiet gebiet : gebiete) {

            //ELECTVAPP-932 und ELECT-11205
            if (gebiete.get(0).equals(gebiet) || trittKandidaturAnImGebiet(identischVerknuepfteKandidaturen, gebiet)) {
                Long stimmenProGebietProGruppeproKandidatur = identischVerknuepfteKandidaturen.stream().map(kandidatur -> {
                    if (kandidatur.isListenkandidatur()) {
                        return ergebnisDaten.
                                getStimmenFuerListenkandidatur(kandidatur.castTo(Listenkandidatur.class).getEntity(), gebiet);
                    }
                    return 0L;
                }).mapToLong(Long::longValue).sum();
                writeLong(serializer, stimmenProGebietProGruppeproKandidatur);
            } else {
                writeMinus(serializer);//ELECT-11205
            }
        }
    }

    protected boolean trittKandidaturAnImGebiet(Collection<Kandidatur> identischVerknuepfteKandidaturen, Wahldurchfuehrungsgebiet gebiet) {
        return identischVerknuepfteKandidaturen.stream().filter(kandidatur -> kandidatur.isListenkandidatur() &&
                kandidatur.getGebiet().isInGebietshierarchieVon(gebiet)).collect(Collectors.toList()).size() > 0;
    }


    private void ueberpruefeStimmzettelPositionIdentischVerkuepfterKandidaturen(CsvSerializer serializer, ErgebnisDaten ergebnisDaten, Collection<Kandidatur> identischVerknuepfteKandidaturen, NlWasListenkandidatur nlListenkandidatur) {
        List<Long> stimmZettelPositionen = identischVerknuepfteKandidaturen.stream().map(kandidatur -> {
            if (kandidatur.isListenkandidatur()) {
                return kandidatur.castTo(Listenkandidatur.class).getEntity().getStimmzettelPosition(ergebnisDaten.getWurzelgebiet(), ergebnisDaten.getEntityAufStimmzettelDaten()).getPosition();
            }
            return 0L;

        }).collect(Collectors.toList());

        if (stimmZettelPositionen.stream().distinct().count() <= 1) {
            writeLong(serializer, notNull(nlListenkandidatur.getStimmzettelPosition(ergebnisDaten.getWurzelgebiet(), ergebnisDaten.getEntityAufStimmzettelDaten()).getPosition()));
        } else {
            String familiennameBestandteil = nlListenkandidatur.getFamiliennameBestandteil();
            if (familiennameBestandteil != null) {
                writeField(serializer, "(" + familiennameBestandteil + " " + nlListenkandidatur.getFamilienname() + ", " + nlListenkandidatur.getInitialen() + ")");
            } else {
                writeField(serializer, "(" + nlListenkandidatur.getFamilienname() + ", " + nlListenkandidatur.getInitialen() + ")");

            }
        }
    }

    void writeHeaderOfGruppe(CsvSerializer serializer, ErgebnisDaten ergebnisDaten, List<Wahldurchfuehrungsgebiet> gebiete, Gruppe gruppe, List<Listenkandidatur> listenkandidaturList) {
        nextLine(serializer);
        writeLong(serializer, gruppe.getRelativePosition());
        writeField(serializer, gruppe.getName());
        writeField(serializer, "");
        writeField(serializer, "");
        for (Wahldurchfuehrungsgebiet gebiet : gebiete) {
            Long stimmenProGebietProGruppe = listenkandidaturList.stream().map(listenkandidatur -> ergebnisDaten.
                    getStimmenFuerListenkandidatur(listenkandidatur, gebiet)).mapToLong(Long::longValue).sum();
            writeLong(serializer, stimmenProGebietProGruppe);

        }
    }

    private void setEntryOfKopfzahlen(CsvSerializer serializer, String eintrag) {
        nextLine(serializer);
        writeField(serializer, "");
        writeField(serializer, eintrag);
        writeField(serializer, "");
        writeField(serializer, "");
    }

    private void  writeKopfzahlen(CsvSerializer serializer, ErgebnisDaten ergebnisDaten, List<Wahldurchfuehrungsgebiet> gebiete, NlErgebnisDaten nlErgebnisDaten) {


        if (mussErfasstWerden(ergebnisDaten, gebiete, NlVoteType.WAHLBERECHTIGTE)) {
            setEntryOfKopfzahlen(serializer, "opgeroepenen");
            gebiete.forEach(gebiet -> writeLong(serializer, nlErgebnisDaten.getSummeWahlberechtigteGesamt(gebiet, Stimmabgabeart.GESAMT)));
        }

        if (mussErfasstWerden(ergebnisDaten, gebiete, NlVoteType.WAEHLER_OHNE_WAHLSCHEIN)) {
            setEntryOfKopfzahlen(serializer, "geldige stempas");
            gebiete.forEach(gebiet -> writeLong(serializer, nlErgebnisDaten.getSummeWaehlerOhneWahlschein(gebiet, Stimmabgabeart.GESAMT)));
        }
        if (mussErfasstWerden(ergebnisDaten, gebiete, NlVoteType.WAEHLER_MIT_VOLLMACHT)) {
            setEntryOfKopfzahlen(serializer, "geldig volmachtbewijs");
            gebiete.forEach(gebiet -> writeLong(serializer, nlErgebnisDaten.getSummeWaehlerMitVollmacht(gebiet, Stimmabgabeart.GESAMT)));
        }

        if (mussErfasstWerden(ergebnisDaten, gebiete, NlVoteType.WAEHLER_MIT_WAHLSCHEIN)) {
            setEntryOfKopfzahlen(serializer, "geldige kiezerspas");
            gebiete.forEach(gebiet -> writeLong(serializer, nlErgebnisDaten.getSummeWaehlerMitWahlschein(gebiet, Stimmabgabeart.GESAMT)));//ELECTVAPP-1289
        }
        if (mussErfasstWerden(ergebnisDaten, gebiete, NlVoteType.WAEHLER_GESAMT)) {
            setEntryOfKopfzahlen(serializer, "toegelaten kiezers");
            gebiete.forEach(gebiet -> writeLong(serializer, nlErgebnisDaten.getSummeWaehlerGesamt(gebiet, Stimmabgabeart.GESAMT)));

            if (isWahlebenePSB(ergebnisDaten.getWahl())) {//ELECT-11665
                if(valueOf(ergebnisDaten.getWahl().getWahlart()).isBriefwahlGemeindeUnterstuetzt()){//ELECTVAPP-1289
                    setEntryOfKopfzahlen(serializer, "toegelaten kiezers (Briefstembureaus gemeente)");
                    gebiete.forEach(gebiet -> writeLong(serializer, nlErgebnisDaten.getSummeWaehlerGesamtNurBriefwahlGemeinde(gebiet)));

                }
                if (NlWahlart.isBriefwahlAuslandUnterstuetzt(ergebnisDaten.getWurzelgebiet())) { //ELECTVAPP-1289
                    setEntryOfKopfzahlen(serializer, "toegelaten kiezers (Briefstembureaus buitenland)");
                    gebiete.forEach(gebiet -> writeLong(serializer, nlErgebnisDaten.getSummeWaehlerGesamtNurBriefwahlAusland(gebiet)));
                }
            }

        }

        NlErgebnisDatenGueltige ergebnisDatenGueltige = nlErgebnisDaten.getErgebnisDatenGueltige();
        if (mussErfasstWerden(ergebnisDaten, gebiete, NlVoteType.GUELTIGE_INSGESAMT)) {
            setEntryOfKopfzahlen(serializer, "geldige stembiljetten");
            gebiete.forEach(gebiet -> writeLong(serializer, ergebnisDatenGueltige.getSummeGueltigeListenstimmen(gebiet, Stimmabgabeart.GESAMT)));
        }
        if (mussErfasstWerden(ergebnisDaten, gebiete, NlVoteType.BLANCO)) {
            setEntryOfKopfzahlen(serializer, "blanco stembiljetten");
            gebiete.forEach(gebiet -> writeLong(serializer, ergebnisDatenGueltige.getSummeBlankoListenstimmen(gebiet, Stimmabgabeart.GESAMT)));
        }
        if (mussErfasstWerden(ergebnisDaten, gebiete, NlVoteType.UNGUELTIGE)) {
            setEntryOfKopfzahlen(serializer, "ongeldige stembiljetten");
            gebiete.forEach(gebiet -> writeLong(serializer, ergebnisDatenGueltige.getSummeUngueltigeListenstimmen(gebiet, Stimmabgabeart.GESAMT)));
        }
//
        NlErgebnisDatenStimmzettel ergebnisDatenStimmzettel = nlErgebnisDaten.getErgebnisDatenStimmzettel();//ELECTVAPP-928
//
        setEntryOfKopfzahlen(serializer, "aangetroffen stembiljetten");
        gebiete.forEach(gebiet -> writeLong(serializer, ergebnisDatenStimmzettel.getSummeStimmzettelEingenommenGesamt(gebiet, Stimmabgabeart.GESAMT)));

        if (mussErfasstWerden(ergebnisDaten, gebiete, NlVoteType.STIMMZETTEL_EINGENOMMEN_ZU_VIEL)) {
            setEntryOfKopfzahlen(serializer, "meer stembiljetten dan toegelaten kiezers");
            gebiete.forEach(gebiet -> writeLong(serializer,
                    ergebnisDatenStimmzettel.getSummeStimmzettelEingenommenAbweichendZuviel(gebiet, Stimmabgabeart.GESAMT)));
        }
        if (mussErfasstWerden(ergebnisDaten, gebiete, NlVoteType.STIMMZETTEL_EINGENOMMEN_ZU_WENIG)) {
            setEntryOfKopfzahlen(serializer, "minder stembiljetten dan toegelaten kiezers");
            gebiete.forEach(gebiet -> writeLong(serializer, ergebnisDatenStimmzettel.getSummeStimmzettelEingenommenAbweichendZuwenig(gebiet, Stimmabgabeart.GESAMT)));
        }
        if (mussErfasstWerden(ergebnisDaten, gebiete, NlVoteType.STIMMZETTEL_NICHT_ABGEGEBEN)) {
            setEntryOfKopfzahlen(serializer, "kiezers met stembiljet hebben niet gestemd");
            gebiete.forEach(gebiet -> writeLong(serializer, ergebnisDatenStimmzettel.getSummeStimmzettelEingenommenAbweichendGrundAusgegebenNichtAbgegeben(gebiet, Stimmabgabeart.GESAMT)));
        }
        if (mussErfasstWerden(ergebnisDaten, gebiete, NlVoteType.STIMMZETTEL_ZU_WENIG_AUSGEGEBEN)) {
            setEntryOfKopfzahlen(serializer, "er zijn te weinig stembiljetten uitgereikt");
            gebiete.forEach(gebiet -> writeLong(serializer, ergebnisDatenStimmzettel.getSummeStimmzettelEingenommenAbweichendGrundAusgegebenZuwenig(gebiet, Stimmabgabeart.GESAMT)));
        }
        if (mussErfasstWerden(ergebnisDaten, gebiete, NlVoteType.STIMMZETTEL_ZU_VIEL_AUSGEGEBEN)) {
            setEntryOfKopfzahlen(serializer, "er zijn te veel stembiljetten uitgereikt");
            gebiete.forEach(gebiet -> writeLong(serializer, ergebnisDatenStimmzettel.getSummeStimmzettelEingenommenAbweichendGrundAusgegebenZuviel(gebiet, Stimmabgabeart.GESAMT)));
        }
        if (mussErfasstWerden(ergebnisDaten, gebiete, NlVoteType.BRIEFWAHL_STIMMZETTEL_EINGENOMMEN_ZU_WENIG)) {
            setEntryOfKopfzahlen(serializer, "geen stembiljet in enveloppe (Briefstembureaus)");
            gebiete.forEach(gebiet -> writeLong(serializer, ergebnisDatenStimmzettel.getSummeStimmzettelEingenommenAbweichendGrundInWahlbriefZuwenig(gebiet, Stimmabgabeart.GESAMT)));
        }
        if (mussErfasstWerden(ergebnisDaten, gebiete, NlVoteType.BRIEFWAHL_STIMMZETTEL_EINGENOMMEN_ZU_VIEL)) {
            setEntryOfKopfzahlen(serializer, "meer stembiljetten in een enveloppe (Briefstembureaus)");
            gebiete.forEach(gebiet -> writeLong(serializer, ergebnisDatenStimmzettel.getSummeStimmzettelEingenommenAbweichendGrundInWahlbriefZuviel(gebiet, Stimmabgabeart.GESAMT)));
        }
        if (mussErfasstWerden(ergebnisDaten, gebiete, NlVoteType.BRIEFWAHL_STIMMZETTEL_VERLOREN)) {
            setEntryOfKopfzahlen(serializer, "er zijn stembiljetten kwijtgeraakt (Briefstembureaus buitenland)");
            gebiete.forEach(gebiet -> writeLong(serializer, ergebnisDatenStimmzettel.getSummeStimmzettelEingenommenAbweichendGrundVerloren(gebiet, Stimmabgabeart.GESAMT)));
        }
        if (mussErfasstWerden(ergebnisDaten, gebiete, NlVoteType.STIMMZETTEL_ABWEICHEND_UNBEKANNTER_GRUND)) {
            setEntryOfKopfzahlen(serializer, "geen verklaring");
            gebiete.forEach(gebiet -> writeLong(serializer, ergebnisDatenStimmzettel.getSummeStimmzettelEingenommenAbweichendGrundUnbekannt(gebiet, Stimmabgabeart.GESAMT)));
        }
        if (mussErfasstWerden(ergebnisDaten, gebiete, NlVoteType.STIMMZETTEL_ABWEICHEND_ANDERER_GRUND)) {
            setEntryOfKopfzahlen(serializer, "andere verklaring");
            gebiete.forEach(gebiet -> writeLong(serializer, ergebnisDatenStimmzettel.getSummeStimmzettelEingenommenAbweichendGrundSonstiges(gebiet, Stimmabgabeart.GESAMT)));
        }
    }


    protected boolean mussErfasstWerden(ErgebnisDaten ergebnisDaten, List<Wahldurchfuehrungsgebiet> gebiete, NlVoteType nlVoteType) {
        boolean voteTypeMussErfasstWerden = false;
        WahlartErgebnisart ergebnisart = ergebnisDaten.getErgebnis().getErgebnisart();


        for (Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet : gebiete) {
            voteTypeMussErfasstWerden |= nlVoteType.isMussErfasstWerden(stimmeingabeKopfzahlKonfiguration, wahldurchfuehrungsgebiet, ergebnisart);
        }
        return voteTypeMussErfasstWerden;
    }


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

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

    protected void writeIntro(ErgebnisDaten ergebnisDaten, CsvSerializer serializer, ErgebnisExportContext context) {

        writeField(serializer, "Verkiezing");
        writeField(serializer, "");
        Wahldurchfuehrung wahl = ergebnisDaten.getWahl();
        writeField(serializer, wahl.getName());
        nextLine(serializer);
        writeField(serializer, "Datum");
        writeField(serializer, "");
        writeField(serializer, new SimpleDateFormat("yyyy-MM-dd").format(wahl.getWahldatum()));//ELECTVAPP-888
        nextLine(serializer);
        writeField(serializer, "Gebied");
        writeField(serializer, "");

        Wahldurchfuehrungsgebiet wurzelgebiet = ergebnisDaten.getWurzelgebiet();
        String wurzelgebietName = wurzelgebiet.getName();
        if (NlWahlart.TWEEDE_KAMER.equalsWahlart(wahl)) {
            if (isWahlebenHSB(wahl)) {
                writeField(serializer, "Kieskring " + wurzelgebietName);
            } else if (isWahlebenePSB(wahl)) {
                if(NlGebietsart.isOpenbaarLichaam(wurzelgebiet)){ // ELECTVAPP-1300
                    writeField(serializer, "Openbaar lichaam " + wurzelgebietName);
                }else{
                    writeField(serializer, "Gemeente " + wurzelgebietName);
                }

            } else {
                writeField(serializer, wurzelgebietName);
            }
        } else {
            writeField(serializer, wurzelgebietName);
        }
        nextLine(serializer);
        writeField(serializer, "Nummer");
        writeField(serializer, "");
        writeField(serializer, getGebietsNr(ergebnisDaten, wahl));
        nextLine(serializer);

    }

    protected String getGebietsNr(ErgebnisDaten ergebnisDaten, Wahldurchfuehrung wahl) {
        String gebietsNr;
        Wahldurchfuehrungsgebiet wurzelgebiet = ergebnisDaten.getWurzelgebiet();
        String wurzelGebietsNummer = wurzelgebiet.getNummer();

        if (NlWahlart.TWEEDE_KAMER.equalsWahlart(wahl)) {
            if (isWahlebenHSB(wahl)) {
                gebietsNr = "HSB" + wurzelGebietsNummer;
            } else if (isWahlebenePSB(wahl)) {
                gebietsNr = getFormatierteGebietsNr(wurzelgebiet, wurzelGebietsNummer);
            } else {
                gebietsNr = "CSB";
            }
        } else {
            gebietsNr = getFormatierteGebietsNr(wurzelgebiet, wurzelGebietsNummer);
        }

        return gebietsNr;
    }

    private String getFormatierteGebietsNr(Wahldurchfuehrungsgebiet wurzelgebiet, String wurzelGebietsNummer) {

        for (NlGebietsart nlGebietsart : NlGebietsart.values()) {
            if (nlGebietsart.equalsGebiet(wurzelgebiet)) {
                return nlGebietDarstellungNummerFormatter.formatiereNummer(nlGebietsart, wurzelGebietsNummer);
            }
        }
        return wurzelGebietsNummer;
    }

    private boolean isWahlebenePSB(Wahldurchfuehrung wahl) {
        return NlWahlart.isWasPSB(wahl, getCustomer());
    }

    private boolean isWahlebenHSB(Wahldurchfuehrung wahl) {
        return NlWahlart.isWasHSB(wahl, getCustomer());
    }

    protected void writeHeader(CsvSerializer serializer, ErgebnisDaten ergebnisDaten, List<Wahldurchfuehrungsgebiet> gebiete) {
        nextLine(serializer);
        writeField(serializer, "Lijstnummer");
        writeField(serializer, "Aanduiding");
        writeField(serializer, "Volgnummer");
        writeField(serializer, "Naam kandidaat");
        writeField(serializer, "Totaal");
        IntStream.range(1, gebiete.size()).forEach(i -> writeField(serializer, gebiete.get(i).getName()));
        nextLine(serializer);
        writeField(serializer, "Gebiednummer");
        writeField(serializer, "");
        writeField(serializer, "");
        writeField(serializer, "");
        writeField(serializer, "");
        IntStream.range(1, gebiete.size()).forEach(i -> writeField(serializer, gebiete.get(i).getNummer()));
        if (isWahlebenePSB(ergebnisDaten.getWahl())) {
            nextLine(serializer);
            writeField(serializer, "Postcode");
            writeField(serializer, "");
            writeField(serializer, "");
            writeField(serializer, "");
            writeField(serializer, "");
            IntStream.range(1, gebiete.size()).forEach(i -> writeField(serializer, gebiete.get(i).getAnschrift().getPlz()));
        }
    }

    protected GebietDAO getGebietDAO() {
        return gebietDAO;
    }

    protected Customer getCustomer() {
        return currentCustomer.getCustomer();
    }

    protected Messages getMessages() {
        return messages;
    }

}
