
package de.ivu.elect.exchange.csv;

import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.List;

import org.jsefa.csv.CsvSerializer;

public class AutoCloseableCsvSerializer<T> extends AbstractAutoCloseableCsvSerializing<T> {

  private final CsvSerializer serializer;

  private final boolean closeOutputStream;

  public AutoCloseableCsvSerializer(Class<T> clazz, OutputStream os, CsvDescriptor csvDescriptor, boolean closeOutputStream) {
    super(csvDescriptor);

    this.serializer = createCsvIOFactory(clazz).createSerializer();
    this.closeOutputStream = closeOutputStream;

    openStream(os, clazz, csvDescriptor);
  }

  protected void openStream(OutputStream os, Class<T> clazz, CsvDescriptor csvDescriptor) {
    try {
      serializer.open(createWriter(os));

      checkAndWriteHeader(clazz, csvDescriptor);
    } catch (Exception ex) {
      throw new RuntimeException(ex.getMessage(), ex);
    }
  }

  // Cf. AbstractRawCsvExportMapper#createWriter!
  private Writer createWriter(OutputStream os) throws IOException {
    Writer writer = new OutputStreamWriter(os, getCharset());
    writer.write(getCharsetBom().map(bom -> new String(bom.getBytes(), getCharset())).orElse(""));

    return writer;
  }

  private void checkAndWriteHeader(Class< ? > clazz, CsvDescriptor csvDescriptor) {
    if (csvDescriptor.isWithHeader()) {
      List<String> header = csvDescriptor.getHeader();

      if (header == null) {
        writeHeader(CsvDescriptor.createHeaderForClass(clazz));
      } else {
        writeHeader(header);
      }
    }
  }

  private void writeHeader(List<String> header) {
    serializer.writeHeader(header);
  }

  public void write(Object dto) {
    serializer.write(dto);
  }

  @Override
  public void close() {
    if (closeOutputStream) {
      serializer.close(closeOutputStream);
    } else {
      serializer.getLowLevelSerializer().flush();
    }
  }
}
