
package de.ivu.elect.exchange.geodata;

import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import javax.inject.Inject;

import de.ivu.elect.business.datei.entity.FileType;
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.exchange.control.NoContext;
import de.ivu.elect.business.geodata.entity.GeodataOrt;
import de.ivu.elect.business.geodata.entity.GeodataOrtUK;
import de.ivu.elect.business.geodata.entity.GeodataPlz;
import de.ivu.elect.exchange.CsvImportMapper;
import de.ivu.elect.exchange.csv.CsvMarshaller;
import de.ivu.elect.jee.exchange.Violations;

public class DestatisGeodataOrtCsvMapper implements GeodataOrtMapper, CsvImportMapper<List<GeodataOrt>, NoContext> {

  public static final String FORMAT_NAME = "file.format.geodata.ort.export.destatis";

  @Inject
  private transient CsvMarshaller csvMarshaller;

  public DestatisGeodataOrtCsvMapper() {
    super();
  }

  @Override
  public List<String> getCsvColumnHeaders(NoContext context) {
    return csvMarshaller.extractHeader(DestatisGeodataOrtCsvDTO.class);
  }

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

  @Override
  public void serialize(List<GeodataOrt> geodataOrte, OutputStream outputStream, NoContext noContext, FileKonfiguration fileKonfiguration) throws MappingException {
    List<DestatisGeodataOrtCsvDTO> dtos = geodataOrte.stream().flatMap(geodataOrtToDtos()).collect(Collectors.toList());

    csvMarshaller.serialize(DestatisGeodataOrtCsvDTO.class, dtos, outputStream, fileKonfiguration);
  }

  private Function<GeodataOrt, Stream<DestatisGeodataOrtCsvDTO>> geodataOrtToDtos() {
    return geodataOrt -> geodataOrt.getGeodataPostleitzahlen().stream().map(geodataPlz -> {
      DestatisGeodataOrtCsvDTO dto = new DestatisGeodataOrtCsvDTO();
      dto.setPlz(geodataPlz.getPlz());
      // NOTE, dass der Gemeindename eigentlich auch Komma-separierte Zusätze wie bspw. Hansestadt oder Brüder-Grimm-Stadt enthält, die wir aber nicht pflegen.
      dto.setGemeindename(geodataOrt.getOrtsname());
      dto.setBundeslandname("");

      return dto;
    });
  }

  @Override
  public List<GeodataOrt> deserialize(InputStream inputStream, NoContext noContext, FileKonfiguration fileKonfiguration) throws MappingException {
    List<DestatisGeodataOrtCsvDTO> dtos = csvMarshaller.deserialize(DestatisGeodataOrtCsvDTO.class, inputStream, fileKonfiguration);

    Map<GeodataOrtUK, GeodataOrt> geodataOrtPerGeodataOrtUk = new HashMap<>();

    dtos.forEach(dto -> {
      GeodataOrtUK geodataOrtUk = new GeodataOrtUK(dto.getOrtsname(), "");

      if (!geodataOrtPerGeodataOrtUk.containsKey(geodataOrtUk)) {
        GeodataOrt geodataOrt = new GeodataOrt();
        geodataOrt.setOrtsname(dto.getOrtsname());

        geodataOrtPerGeodataOrtUk.put(geodataOrtUk, geodataOrt);
      }

      GeodataPlz geodataPlz = new GeodataPlz();
      geodataPlz.setPlz(dto.getPlz());

      geodataOrtPerGeodataOrtUk.get(geodataOrtUk).addGeodataPostleitzahl(geodataPlz);
    });

    return new ArrayList<>(geodataOrtPerGeodataOrtUk.values());
  }

  @Override
  public boolean isAcceptContext(NoContext noContext) {
    return true;
  }

  @Override
  public Violations validateInputFormat(InputStream inputStream, NoContext noContext, FileKonfiguration fileKonfiguration) {
    return new Violations();
  }
}
