package org.fda.evaluator;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.EOFException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import org.fda.alignment.AlignmentFilterG;
import org.fda.alignment.AlignmentFilterQ;
import org.fda.alignment.ScaffoldContig;
import org.fda.alignmentparser.AlignmentReaderRunnable;
import org.fda.data.Enums.STOPOBJECT;
import org.fda.data.ReferenceSet;
import org.fda.data.Utilities;
import org.fda.alignmentparser.delta.DeltaReaderRunnable;
import org.fda.alignmentparser.paf.PafReaderRunnable;
import org.fda.data.Enums;
import org.fda.multithreading.StandaloneContigAlignmentQueueMonitor;

/**
 *
 * @author Gokhan.Yavas
 */
public class Evaluator extends EvaluatorBase{
    private List<File> alignmentFileArr ;

    public Evaluator(File sourceFolder,  File outdir, ReferenceSet chrset) throws IOException{
        
        super(sourceFolder, outdir, chrset);
        alignmentFileArr = new ArrayList();
        Utilities.findAllFiles(sourceFolder, alignmentFileArr, new String[]{Utilities.alignmentTool.getFileExtension()}, new String[]{});
        
        // this is the binary file
        contigfile = new File(outdir.getAbsolutePath()+File.separator+Utilities.contigBinFileName);
        
        // this is the contig stats file
        outfile = new File(outdir.getAbsolutePath()+File.separator+Utilities.contigsStats);
        
        // this is alignments file
        alignmentfile = new File(outdir.getAbsolutePath()+File.separator+Utilities.alignmentFile);
        
        // this is the misassembly file
        missassemblyFile = new File(outdir.getAbsolutePath()+File.separator+Utilities.misassemblyFile);
        
        p = new Processor(this, chrset, outdir);
        
    }
    private Map<File, List<File>> returnPartitions(){
        Map<File, List<File>> fmap = new HashMap<>();
        File parent;
        List<File> tmp;
        for(File  f : alignmentFileArr){
            parent = f.getParentFile();
            if(fmap.containsKey(parent))
                fmap.get(parent).add(f);
            else{
                tmp = new ArrayList();
                tmp.add(f);
                fmap.put(parent, tmp);
            }
        }
        return fmap;
    }
    
    @Override
    public QLPair[] process() {
        int i;
        StandaloneContigAlignmentQueueMonitor qm=null;       
        Map<File, List<File>> fmap = returnPartitions();
        try{
            
            BlockingQueue filesListQueue = new ArrayBlockingQueue(fmap.size()+Utilities.no_ReaderThreads);
            filesListQueue.addAll(fmap.values());
            
            // add the objects that will stop the delta readers
            for(i=0; i<Utilities.no_ReaderThreads; i++)           
                filesListQueue.put(STOPOBJECT.STOP);  
            
            // Alignmentsets to be filtered are written into this queue
            BlockingQueue alignmentSetQueue = new ArrayBlockingQueue(Utilities.alignmentqueuesize);
            // Filtered alignmentsets are written into this queue
            BlockingQueue filteredQueue = new ArrayBlockingQueue(Utilities.alignmentqueuesize);

            Thread[] alignmentreaderArr = new Thread[Utilities.no_ReaderThreads];
            AlignmentReaderRunnable rmb;
            for(i=0; i < alignmentreaderArr.length; i++){
                if(Utilities.alignmentTool==Enums.AlignmentTool.nucmer)
                    rmb=new DeltaReaderRunnable(filesListQueue, alignmentSetQueue, chrset,i, this.sourceFolder);
                else
                    rmb=new PafReaderRunnable(filesListQueue, alignmentSetQueue, chrset,i, this.sourceFolder);
                alignmentreaderArr[i] = new Thread(rmb);
                alignmentreaderArr[i].start();
            }

            Thread[] filterArr = new Thread[Utilities.no_filterThreads];                       
            for(i=0; i < filterArr.length; i++){
                if(Utilities.globalFilter)
                    filterArr[i] = new Thread(new AlignmentFilterG(alignmentSetQueue, i,  filteredQueue));
                else
                    filterArr[i] = new Thread(new AlignmentFilterQ(alignmentSetQueue, i,  filteredQueue));                    
                filterArr[i].start();                
            }
            
            // Now we have to create the sorter thread here and pass the filteredQueue to this
            //qm = new StandaloneContigAlignmentQueueMonitor(filteredQueue, this.contigfile, this.outfile, this.sourceFolder);
            qm = new StandaloneContigAlignmentQueueMonitor(filteredQueue, this.contigfile, this.bw, this.sourceFolder, this.bwc, this.p);
            Thread qmt = new Thread(qm);
            qmt.start();

            // Join the individual reader threads
            for(i=0; i < alignmentreaderArr.length; i++)
                alignmentreaderArr[i].join();                                        
            // put stopper for filter threads into the alignment queue
            for(i=0; i< filterArr.length; i++)
                alignmentSetQueue.put(STOPOBJECT.STOP);
            
            // join the filter threads
            for(i=0; i < filterArr.length; i++)
                filterArr[i].join();

            // join the queuemonitor thread
            filteredQueue.put(STOPOBJECT.STOP);
            qmt.join();
        }
        catch(InterruptedException io){ io.printStackTrace();} 
        QLPair[] histData =getHistData(qm.getContigCount());
        //System.out.println("Contig alignments are processed!");
        return histData;
    }
    private QLPair[] getHistData(int contigNumber){    
    //private double[] getHistData(int contigNumber, double maxPenalty){
        QLPair[] quallist = new QLPair[contigNumber];
        ObjectOutputStream outstream=null;
        ObjectInputStream instream = null;

        try {                        
            File tmpfile = new File(contigfile.getAbsolutePath()+".tmp");
            tmpfile.deleteOnExit();
            Files.move(contigfile.toPath(), tmpfile.toPath(), StandardCopyOption.ATOMIC_MOVE);
            ScaffoldContig als;
            
            outstream = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(contigfile)));
            outstream.writeInt(contigNumber);
            instream = new ObjectInputStream(new BufferedInputStream(new FileInputStream(tmpfile)));
            int indx=0;
            while(true){
                als = (ScaffoldContig)instream.readObject();
                if(als.getIsMisassembled() || als.containsScaffoldingGap())
                    misassemblyBw.append(als.getString4Misassemblies());
                outstream.writeObject(als);
                //quallist[indx]=als.getQuality();
                quallist[indx]=new QLPair(als.getContigLength(), als.getQuality());
                indx++;
            }            
        }
        catch(ClassNotFoundException e){e.printStackTrace();}
        catch (IOException ex) {
            if(!(ex instanceof EOFException))
                ex.printStackTrace();
        }            
        finally {
            try {
                outstream.close();
                instream.close();
//                bw.close();
            } catch (IOException ex) {
                ex.printStackTrace();                    
            }
        } 
        return quallist;
    }
    
    
}
