
package de.ivu.elect.was.exchange.okwahl;

import java.io.OutputStream;
import java.util.List;
import java.util.Map.Entry;
import java.util.SortedMap;

import javax.inject.Inject;

import de.ivu.elect.business.datei.entity.CsvAnfuehrungszeichen;
import de.ivu.elect.business.datei.entity.CsvMaskierungszeichen;
import de.ivu.elect.business.datei.entity.CsvTrennzeichen;
import de.ivu.elect.business.datei.entity.CsvZeichensatz;
import de.ivu.elect.business.datei.entity.FileType;
import de.ivu.elect.business.exchange.control.CsvFileKonfiguration;
import de.ivu.elect.business.exchange.control.FileFormat;
import de.ivu.elect.business.gebietsbaum.boundary.WahlverwaltungGebietDescriptor;
import de.ivu.elect.business.gebietsbaum.boundary.WahlverwaltungGebietInformation;
import de.ivu.elect.business.stimmen.boundary.GruppeStimmeLabelGenerator;
import de.ivu.elect.business.wahl.entity.Wahl;
import de.ivu.elect.exchange.csv.CsvDescriptor;
import de.ivu.elect.exchange.csv.CsvMarshaller;
import de.ivu.elect.was.exchange.ergebnis.ErgebnisExportGebietFormatter;
import de.ivu.jsefa.CsvHeaderExtractor;

public abstract class AbstractOkWahlCsvExportMapper<T, C, D extends AbstractOkWahlDTO> {

  @Inject
  private transient OkWahlCsvExportMapperFilter okWahlCsvExportMapperFilter;

  @Inject
  private transient GruppeStimmeLabelGenerator gruppeStimmeLabelGenerator;

  @Inject
  private transient ErgebnisExportGebietFormatter ergebnisExportGebietFormatter;

  @Inject
  private transient CsvMarshaller csvMarshaller;

  private final CsvFileKonfiguration fileKonfiguration;

  public AbstractOkWahlCsvExportMapper() {
    super();

    this.fileKonfiguration = createFileKonfiguration();
  }

  public boolean isAcceptContext(Wahl wahl) {
    return okWahlCsvExportMapperFilter.isAcceptContext(wahl) && isAcceptContextInternal(wahl);
  }

  protected abstract boolean isAcceptContextInternal(Wahl wahl);

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

  public abstract String getFileFormatName();

  public FileType getFileType() {
    return FileType.CSV;
  }

  public CsvFileKonfiguration getFileKonfiguration() {
    return fileKonfiguration;
  }

  protected GruppeStimmeLabelGenerator getGruppeStimmeLabelGenerator() {
    return gruppeStimmeLabelGenerator;
  }

  protected ErgebnisExportGebietFormatter getGebietFormatter() {
    return ergebnisExportGebietFormatter;
  }

  public void serialize(T exportObject, OutputStream os, C context,
      Entry<WahlverwaltungGebietInformation, SortedMap<WahlverwaltungGebietInformation, List<WahlverwaltungGebietDescriptor>>> entryPerStimmzettelgebiet, boolean closeOutputStream) {
    List<D> dtos = createDtos(exportObject, os, context, entryPerStimmzettelgebiet, closeOutputStream);

    if (!dtos.isEmpty()) {
      csvMarshaller.serialize((Class<D>) dtos.get(0).getClass(), dtos, os, createCsvDescriptor(dtos), closeOutputStream);
    }
  }

  public CsvDescriptor createCsvDescriptor(List<D> dtos) {
    if (!dtos.isEmpty()) {
      return createCsvDescriptor(dtos, new CsvHeaderExtractor().extractHeader(dtos.get(0).getClass()));
    }

    return CsvDescriptor.forFileKonfiguration(getFileKonfiguration());
  }

  protected CsvDescriptor createCsvDescriptor(List<D> dtos, List<String> header) {
    int indexOfHeaderStimmzettelgebietNummer = header.indexOf(AbstractOkWahlDTO.NAME_STIMMZETTELGEBIET_NUMMER);

    if (indexOfHeaderStimmzettelgebietNummer >= 0 && !dtos.isEmpty()) {
      header.set(indexOfHeaderStimmzettelgebietNummer, dtos.get(0).getStimmzettelgebietNummerHeader());
    }

    return CsvDescriptor.withHeader(getFileKonfiguration(), header);
  }

  protected abstract List<D> createDtos(T exportObject, OutputStream os, C context,
      Entry<WahlverwaltungGebietInformation, SortedMap<WahlverwaltungGebietInformation, List<WahlverwaltungGebietDescriptor>>> entryPerStimmzettelgebiet, boolean closeOutputStream);

  private CsvFileKonfiguration createFileKonfiguration() {
    return new CsvFileKonfiguration() {

      @Override
      public FileFormat getFileFormat() {
        return AbstractOkWahlCsvExportMapper.this.getFileFormat();
      }

      @Override
      public CsvZeichensatz getZeichensatz() {
        return CsvZeichensatz.ISO_8859_1;
      }

      @Override
      public Boolean isWithHeader() {
        return getAnzahlKopfzeilen() > 0;
      }

      @Override
      public Long getAnzahlKopfzeilen() {
        return 1L;
      }

      @Override
      public CsvTrennzeichen getTrennzeichen() {
        return CsvTrennzeichen.SEMIKOLON;
      }

      @Override
      public CsvAnfuehrungszeichen getAnfuehrungszeichen() {
        return CsvAnfuehrungszeichen.OHNE;
      }

      @Override
      public CsvMaskierungszeichen getMaskierungszeichen() {
        return CsvMaskierungszeichen.BACKSLASH;
      }

      @Override
      public Boolean isNutzeTrennzeichenNachLetztemFeld() {
        return true;
      }
    };
  }
}
