package org.fda.evaluator;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
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.alignmentparser.delta.DeltaReaderRunnable;
import org.fda.alignmentparser.paf.PafReaderRunnable;
import org.fda.data.Enums;
import org.fda.data.Reference;
import org.fda.data.ReferenceSet;
import org.fda.data.Utilities;
import org.fda.multithreading.StandardQMonitor;
import org.fda.regression.DataValue;
import org.fda.regression.RegressionModel;
import org.fda.regression.RegressionModule;

/**
 *
 * @author Gokhan.Yavas
 */
public class Evaluator4Artifact {
    private final List<File> alignmentFileArr ;
    private final ReferenceSet chrset;
    private File modelfile;
    private RegressionModel rm;
    private final Map<Reference, List<DataValue>> ramap;
    private final Map<String, String> contigid2ref;
    private final File sourceFolder;
    public File getModelfile() {
        return modelfile;
    }

    public RegressionModel getRm() {
        return rm;
    }
    public Evaluator4Artifact(File sourceFolder, File outfolder, ReferenceSet chrset, Map<String, List<String>> refmap){
        alignmentFileArr = new ArrayList();
        this.sourceFolder = sourceFolder;
        this.contigid2ref = new HashMap();
        if(refmap!=null){
            for(String ref : refmap.keySet())
                for(String contigid : refmap.get(ref))
                    contigid2ref.put(contigid, ref);
        }
        else{
            for(Reference re : chrset.getRefs()){
                contigid2ref.put(re.getRefID(), re.getRefID());
            }
        }
        Utilities.findAllFiles(sourceFolder, alignmentFileArr, new String[]{Utilities.alignmentTool.getFileExtension()}, new String[]{});
        this.chrset = chrset;
        modelfile = new File(outfolder.getAbsolutePath()+File.separator+Utilities.modelFileName);
        ramap = new TreeMap();
        for(Reference r : chrset.getRefs()){
            ramap.put(r, new ArrayList<DataValue>());
        }
        

    }
    public Evaluator4Artifact(File sourceFolder, File outfolder, ReferenceSet chrset, Map<String, List<String>> refmap, File modelFile){
        this(sourceFolder, outfolder, chrset, refmap);
        modelfile = modelFile;        
    }
    
    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;
    }
    public void process(){
        List<ScaffoldContig> als = initialProcessing();
        
        //List<DataValue> alldata = new ArrayList<DataValue>();
        List<DataValue> alldata;
        DataValue dv;
        for(ScaffoldContig s : als){
            dv = new DataValue(s.getId(), s.getContigLength(), Utilities.initialAlignmentDistanceThreshold, s.getTotalInconsistencyNumber());
            alldata = ramap.get(chrset.findReference(contigid2ref.get(s.getId())));
            alldata.add(dv);
        }
        int k = Utilities.initialAlignmentDistanceThreshold+Utilities.alignmentDistanceThresholdInc;
        //int tmp = Utilities.alignmentDistanceThreshold;
        for(;k<=Utilities.maxAlignmentDistanceThreshold;k+= Utilities.alignmentDistanceThresholdInc){
            Utilities.alignmentDistanceThreshold = k;
            iterativeProcessing(als);
            for(ScaffoldContig s : als){
                dv = new DataValue(s.getId(), s.getContigLength(), k, s.getTotalInconsistencyNumber()); 
                alldata = ramap.get(chrset.findReference(contigid2ref.get(s.getId())));
                alldata.add(dv);
            }            
        }
        
        rm =RegressionModule.process(ramap, chrset);
        
        
        try(ObjectOutputStream out = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(this.modelfile))); ){
            out.writeObject(rm);
        }
        catch(IOException e){e.printStackTrace();}    
        //return(rm);
        
    }
    private List<ScaffoldContig> initialProcessing() {
        //int tmp = Utilities.alignmentDistanceThreshold;
        Utilities.alignmentDistanceThreshold=Utilities.initialAlignmentDistanceThreshold;
        int i;
        StandardQMonitor 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(Enums.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[] deltareaderArr = new Thread[Utilities.no_ReaderThreads];
            AlignmentReaderRunnable rmb;
            for(i=0; i < deltareaderArr.length; i++){
                if(Utilities.alignmentTool==Enums.AlignmentTool.nucmer)
                    rmb =new DeltaReaderRunnable(filesListQueue, alignmentSetQueue, chrset,i, sourceFolder);
                else
                    rmb =new PafReaderRunnable(filesListQueue, alignmentSetQueue, chrset,i, sourceFolder);
                deltareaderArr[i] = new Thread(rmb);
                deltareaderArr[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 StandardQMonitor(filteredQueue);
            Thread qmt = new Thread(qm);
            qmt.start();

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

            // join the queuemonitor thread
            filteredQueue.put(Enums.STOPOBJECT.STOP);
            qmt.join();
        }
        catch(InterruptedException io){ io.printStackTrace();} 
        //Utilities.alignmentDistanceThreshold=tmp;
        return qm.getContigs();
    }
    private void iterativeProcessing(List<ScaffoldContig> als){        
        for(ScaffoldContig al : als){
            al.setMisassembly();
            //al.getTotalInconsistencyNumber();
        }        
    }
}
