package io.ifar.skidroad.writing.csv;

import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.concurrent.BlockingQueue;

import com.fasterxml.jackson.dataformat.csv.CsvFactory;
import com.fasterxml.jackson.dataformat.csv.CsvGenerator;
import com.fasterxml.jackson.dataformat.csv.CsvSchema;
import io.ifar.goodies.Tuple;
import io.ifar.skidroad.LogFile;
import io.ifar.skidroad.tracking.LogFileTracker;
import io.ifar.skidroad.writing.AbstractWritingWorker;

import static java.nio.file.StandardOpenOption.*;

/**
 * CSV-based {@link io.ifar.skidroad.writing.AbstractWritingWorker} implementation that consumes Tuples.
 */
public class CSVWritingWorker<T extends Tuple> extends AbstractWritingWorker<CsvGenerator, T> {
    /**
     * We don't have 'real' schema to bind columns to named values; but we still need to
     * specify details of underlying CSV format
     */
    private final static CsvSchema CSV_SCHEMA =
            CsvSchema.emptySchema()
            .withoutHeader()
            .withoutEscapeChar()
            .withQuoteChar('"')
            .withColumnSeparator(',')
            .withLineSeparator("\r\n")
    ;

    private final static CsvFactory csvFactory = new CsvFactory();

    protected final String convertNullTo;

    public CSVWritingWorker(final BlockingQueue<T> queue, final LogFile logFileRecord, final int maxFlushIntervalSeconds, final String nullRepresentation, final LogFileTracker tracker) {
        super(queue, logFileRecord, maxFlushIntervalSeconds, tracker);
        if (nullRepresentation == null || "".equals(nullRepresentation)) {
            convertNullTo = null;
        } else {
            convertNullTo = nullRepresentation;
        }
    }

    @Override
    protected CsvGenerator openForWriting(Path path) throws IOException {
        OutputStream out = Files.newOutputStream(path, CREATE, WRITE, APPEND);
        return csvFactory.createGenerator(out);
    }

    @Override
    protected void writeItem(CsvGenerator gen, Tuple item) throws IOException {
        gen.writeStartArray();
        Object[] values = item.getValues();

        // if not for null checks, could simplify a bit; maybe in future will have this in CsvGenerator
        for (Object value : item.getValues()) {
            if (value == null) {
                if (convertNullTo != null) {
                    gen.writeString(convertNullTo);
                } else {
                    gen.writeNull();
                }
            } else if (value instanceof Boolean) {
                gen.writeBoolean(((Boolean) value).booleanValue());
            } else { // no point in distinguishing numbers etc; all end up as Strings anyway
                gen.writeString(value.toString());
            }
        }
        gen.writeEndArray();
    }
}

@@ -1022,16 +1022,66 @@
 chema()%0A
+                    .withNullValue(%22%5C%5CN%22)%0A        
         
@@ -1105,32 +1105,40 @@
 r()%0A            
+       
+ 
 .withoutEscapeCh
@@ -1138,24 +1138,32 @@
 scapeChar()%0A
+        
             
@@ -1190,24 +1190,32 @@
             
+        
 .withColumnS
@@ -1236,24 +1236,32 @@
             
+ 
+       
 .withLineSep
@@ -1274,21 +1274,16 @@
 (%22%5Cr%5Cn%22)
-%0A    
 ;%0A%0A    p
@@ -1348,49 +1348,42 @@
 ();%0A
-%0A
     pr
-otected final String convertNullTo
+ivate final CsvSchema schema
 ;%0A%0A 
@@ -1502,16 +1502,44 @@
 Seconds,
+%0A                           
  final S
@@ -1593,16 +1593,20 @@
 tracker)
+%0A   
  %7B%0A     
@@ -1706,149 +1706,151 @@
 ion 
-=
+!
 = null
- %7C%7C %22%22.equals(nullRepresentation)) %7B%0A            convertNullTo = null;%0A        %7D else %7B%0A            convertNullTo = nullRepresentation
+) %7B%0A            schema = CSV_SCHEMA.rebuild().setNullValue(nullRepresentation).build();%0A        %7D else %7B%0A            schema = CSV_SCHEMA
 ;%0A  
@@ -2043,14 +2043,32 @@
     
-return
+CsvGenerator generator =
  csv
@@ -2097,16 +2097,79 @@
 r(out);%0A
+        generator.setSchema(schema);%0A        return generator;%0A
     %7D%0A%0A 
@@ -2304,406 +2304,113 @@
     
-Object%5B%5D values = item.getValues();%0A%0A        // if not for null checks, could simplify a bit; maybe in future will have this in CsvGenerator%0A        for (Object value : item.getValues()) %7B%0A            if (value == null) %7B%0A                if (convertNullTo != null) %7B%0A                    gen.writeString(convertNullTo);%0A                %7D else %7B%0A                    gen.writeNull();%0A                %7D
+for (Object value : item.getValues()) %7B%0A            if (value == null) %7B%0A                gen.writeNull();
 %0A   

Make proper use of Jackson CSV schema for null values.