
package de.ivu.elect.core.exchange.stimmen.csv.dynamic;

import static de.ivu.elect.exchange.csv.AutoCloseableCsvSerializer.fixNullToEmpty;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toSet;

import java.io.OutputStream;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.inject.Inject;

import org.jsefa.csv.CsvSerializer;

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.exchange.control.FileFormat;
import de.ivu.elect.business.gebietsbaum.control.GebietByBreadthFirstComparator;
import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
import de.ivu.elect.business.gebietsbaum.entity.Wahldurchfuehrungsgebiet;
import de.ivu.elect.business.stimmeingabe.control.ListenwahlStimmeingabeKonfiguration;
import de.ivu.elect.exchange.csv.raw.AbstractRawCsvExportMapper;
import de.ivu.elect.was.exchange.ergebnis.ErgebnisExportMapper;

public abstract class AbstractDynamicStimmenExportMapper<F extends AbstractDynamicStimmenExportDTOFactory> extends AbstractRawCsvExportMapper implements ErgebnisExportMapper {

  private static final long serialVersionUID = 1L;

  @Inject
  private transient ListenwahlStimmeingabeKonfiguration listenwahlStimmeingabeKonfiguration;

  protected AbstractDynamicStimmenExportMapper() {
    super();
  }

  @Override
  public FileFormat getFileFormat() {
    return new FileFormat(getFileFormatName(), getFileType());
  }

  protected abstract FileType getFileType();

  protected boolean isAusbleibdendeGebieteExportieren(ErgebnisExportContext context) {
    return false;
  }

  protected void writeDatenFuerWurzelgebiet(OutputStream os, ErgebnisDaten ergebnisDaten, Wahldurchfuehrungsgebiet wurzelgebiet, ErgebnisExportContext context) {
    F factory = createDtoFactory(ergebnisDaten, wurzelgebiet, context, getListenwahlStimmeingabeKonfiguration());

    List<List<String>> headers = factory.createHeader();

    CsvSerializer serializer = openSerializer(os);

    try {
      for (List<String> header : headers) {
        serializer.writeHeader(fixNullToEmpty(header));
      }

      List<Gebiet> erfassungsgebiete = collectGebiete(ergebnisDaten, wurzelgebiet, context);
      List<List<String>> zeilen = factory.createValues(erfassungsgebiete);

      for (List<String> spalten : zeilen) {
        serializer.writeHeader(fixNullToEmpty(spalten));
      }
    } finally {
      closeSerializer(serializer);
    }
  }

  protected abstract F createDtoFactory(ErgebnisDaten ergebnisDaten, Wahldurchfuehrungsgebiet wurzelgebiet, ErgebnisExportContext context,
      ListenwahlStimmeingabeKonfiguration listenwahlStimmeingabeKonfiguration);

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

  protected List<Gebiet> collectGebiete(ErgebnisDaten ergebnisDaten, Wahldurchfuehrungsgebiet wurzelgebiet, ErgebnisExportContext context) {
    List<Gebiet> gebiete = ergebnisDaten.getErfassteGebiete().stream().filter(wurzelgebiet::isInGebietshierarchieUebergeordnetesGebietVon).sorted(new GebietByBreadthFirstComparator())
        .collect(toList());

    if (isAusbleibdendeGebieteExportieren(context)) {
      // ELECT-12508 und ELECTKOM-1770
      Set<Gebiet> result = new HashSet<>(gebiete);
      result.addAll(ergebnisDaten.getAusbleibendeGebiete().stream().filter(wurzelgebiet::isInGebietshierarchieUebergeordnetesGebietVon).collect(toSet()));

      return result.stream().sorted(new GebietByBreadthFirstComparator()).collect(toList());
    }

    return gebiete;
  }

  protected ListenwahlStimmeingabeKonfiguration getListenwahlStimmeingabeKonfiguration() {
    return listenwahlStimmeingabeKonfiguration;
  }
}
