package org.broadinstitute.sting.gatk.io.storage;

import org.broad.tribble.readers.LineReader;
import org.broad.tribble.source.BasicFeatureSource;
import org.broad.tribble.vcf.*;
import org.broad.tribble.util.variantcontext.VariantContext;
import org.broadinstitute.sting.utils.exceptions.ReviewedStingException;
import org.broadinstitute.sting.gatk.io.stubs.VCFWriterStub;

import java.io.*;

import net.sf.samtools.util.BlockCompressedOutputStream;
import org.broadinstitute.sting.utils.exceptions.UserException;
import org.broadinstitute.sting.utils.text.XReadLines;

/**
 * Provides temporary and permanent storage for genotypes in VCF format.
 *
 * @author mhanna
 * @version 0.1
 */
public class VCFWriterStorage implements Storage<VCFWriterStorage>, VCFWriter {
    protected final File file;
    protected OutputStream stream;
    protected final VCFWriter writer;

    /**
     * Constructs an object which will write directly into the output file provided by the stub.
     * Intentionally delaying the writing of the header -- this should be filled in by the walker.
     * @param stub Stub to use when constructing the output file.
     */
    public VCFWriterStorage( VCFWriterStub stub )  {
        if ( stub.getFile() != null ) {
            this.file = stub.getFile();
            writer = VCFWriterToFile(stub, stub.getFile());
        }
        else if ( stub.getOutputStream() != null ) {
            this.file = null;
            this.stream = stub.getOutputStream();
            writer = new StandardVCFWriter(stream);
        }
        else
            throw new ReviewedStingException("Unable to create target to which to write; storage was provided with neither a file nor a stream.");
    }

    /**
     * common initialization routine for multiple constructors
     * @param stub
     * @param file
     * @return A VCF writer for use with this class
     */
    private StandardVCFWriter VCFWriterToFile(VCFWriterStub stub, File file) {
        try {
            if ( stub.isCompressed() )
                stream = new BlockCompressedOutputStream(file);
            else
                stream = new PrintStream(file);
        }
        catch(IOException ex) {
            throw new UserException.CouldNotCreateOutputFile(file, "Unable to open target output stream", ex);
        }

        return new StandardVCFWriter(file, this.stream, ! stub.isCompressed());
    }


    /**
     * Constructs an object which will redirect into a different file.
     * @param stub Stub to use when synthesizing file / header info.
     * @param file File into which to direct the output data.
     */
    public VCFWriterStorage(VCFWriterStub stub, File file) {
        this.file = file;
        this.writer = VCFWriterToFile(stub, file);
        writer.writeHeader(stub.getVCFHeader());
    }

    public void add(VariantContext vc, byte ref) {
        writer.add(vc, ref);
    }

    /**
     * initialize this VCF header
     *
     * @param header  the header
     */
    public void writeHeader(VCFHeader header) {
        writer.writeHeader(header);
    }

    /**
     * Close the VCF storage object.
     */
    public void close() {
        writer.close();
    }

    /**
     * Merges the stream backing up this temporary storage into the target.
     * @param target Target stream for the temporary storage.  May not be null.
     */
//    public void mergeInto(VCFWriterStorage target) {
//        PrintStream formattingTarget = new PrintStream(target.stream);
//        try {
//            BufferedReader reader = new BufferedReader(new FileReader(file));
//            String line = reader.readLine();
//            while ( line != null ) {
//                if (!VCFHeaderLine.isHeaderLine(line))
//                    formattingTarget.printf("%s%n",line);
//                line = reader.readLine();
//            }
//
//            reader.close();
//        } catch (IOException e) {
//            throw new UserException.CouldNotReadInputFile(file, "Error reading file in VCFWriterStorage: ", e);
//        }
//    }
    public void mergeInto(VCFWriterStorage target) {
        try {
            //System.out.printf("merging %s%n", file);
            BasicFeatureSource<VariantContext> source = BasicFeatureSource.getFeatureSource(file.getAbsolutePath(), new VCFCodec());
            
            for ( VariantContext vc : source.iterator() ) {
                target.writer.add(vc, vc.getReferenceBaseForIndel());
            }

            source.close();
        } catch (IOException e) {
            throw new UserException.CouldNotReadInputFile(file, "Error reading file in VCFWriterStorage: ", e);
        }
    }
}

@@ -1292,35 +1292,35 @@
        writer = 
-VCF
+vcf
 WriterToFile(stu
@@ -1321,17 +1321,16 @@
 le(stub,
- 
 stub.get
@@ -1335,16 +1335,21 @@
 etFile()
+,true
 );%0A     
@@ -1804,35 +1804,243 @@
 ram stub
-%0A     * @param file
+ Stub to use when constructing the output file.%0A     * @param file Target file into which to write VCF records.%0A     * @param indexOnTheFly true to index the file on the fly.  NOTE: will be forced to false for compressed files.
 %0A     * 
@@ -2122,19 +2122,19 @@
 FWriter 
-VCF
+vcf
 WriterTo
@@ -2159,32 +2159,55 @@
  stub, File file
+, boolean indexOnTheFly
 ) %7B%0A        try 
@@ -2532,32 +2532,181 @@
 ex);%0A        %7D%0A%0A
+        // The GATK/Tribble can't currently index block-compressed files on the fly.  Disable OTF indexing even if the user explicitly asked for it.%0A
         return n
@@ -2749,10 +2749,26 @@
 am, 
+indexOnTheFly && 
 !
- 
 stub
@@ -2951,25 +2951,29 @@
    * @param 
-f
+tempF
 ile File int
@@ -3068,17 +3068,21 @@
 b, File 
-f
+tempF
 ile) %7B%0A 
@@ -3100,17 +3100,21 @@
 .file = 
-f
+tempF
 ile;%0A   
@@ -3132,19 +3132,19 @@
 riter = 
-VCF
+vcf
 WriterTo
@@ -3154,24 +3154,31 @@
 e(stub, file
+, false
 );%0A        w
@@ -3606,870 +3606,8 @@
  %7D%0A%0A
-    /**%0A     * Merges the stream backing up this temporary storage into the target.%0A     * @param target Target stream for the temporary storage.  May not be null.%0A     */%0A//    public void mergeInto(VCFWriterStorage target) %7B%0A//        PrintStream formattingTarget = new PrintStream(target.stream);%0A//        try %7B%0A//            BufferedReader reader = new BufferedReader(new FileReader(file));%0A//            String line = reader.readLine();%0A//            while ( line != null ) %7B%0A//                if (!VCFHeaderLine.isHeaderLine(line))%0A//                    formattingTarget.printf(%22%25s%25n%22,line);%0A//                line = reader.readLine();%0A//            %7D%0A//%0A//            reader.close();%0A//        %7D catch (IOException e) %7B%0A//            throw new UserException.CouldNotReadInputFile(file, %22Error reading file in VCFWriterStorage: %22, e);%0A//        %7D%0A//    %7D%0A
     
@@ -3854,16 +3854,23 @@
 FCodec()
+, false
 );%0A     

Disable OTF indexing when writing indices for temporary VCFs when running with -nt option. When last I checked in, Ryan was seeing a ~25% speedup per shard by not indexing.