package org.fda.contiggenerator;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.fda.data.Enums;
import org.fda.data.Enums.Distribution;
import org.fda.data.Enums.Orientation;
import org.fda.data.Utilities;
import org.fda.exceptions.IllegalIntervalException;
import org.fda.inputdataparser.FastaReader;
import org.fda.inputdataparser.FastaRecord;
import org.fda.inputdataparser.FastaWriter;
import org.fda.intervaltree.Interval;
import org.fda.intervaltree.IntervalSearchTree;

/**
 *
 * @author Gokhan.Yavas
 */
public class ContigGenerator3 extends ContigGeneratorWDistribution implements ContigGeneratorInterface{
    private final Map<String, Integer> totalContigNumberMap;
    private final int maxIterationLimit=200;
    //private final int minIterationLimit=10000;

    public ContigGenerator3(File infile, File outfile, double mean, double sd, Distribution dist, File f){
        super(infile, outfile, mean, sd, dist);                
        this.totalContigNumberMap = parseInput(f);
    }
    public ContigGenerator3(File infile, File outfile, double mean, double sd, Distribution dist, File f, boolean createreport){
        super(infile, outfile, mean, sd, dist, createreport);                
        this.totalContigNumberMap = parseInput(f);
    }
    
    public ContigGenerator3(File infile, File outfile, double mean, double sd, Distribution dist, File f, long seed){
        super(infile, outfile, mean, sd, dist, seed);                
        this.totalContigNumberMap = parseInput(f);
    }
    public ContigGenerator3(File infile, File outfile, double mean, double sd, Distribution dist, File f, long seed, boolean createreport){
        super(infile, outfile, mean, sd, dist, seed, createreport);                
        this.totalContigNumberMap = parseInput(f);
    }
    public ContigGenerator3(File infile, File outfile, double mean, double sd, Distribution dist, File f, boolean setseed, boolean createreport){
        super(infile, outfile, mean, sd, dist, setseed, createreport);                
        this.totalContigNumberMap = parseInput(f);
    }
    
    
    private Map<String, Integer> parseInput(File f){
        Map<String, Integer> nMap = new HashMap<>();
        try(BufferedReader br = new BufferedReader(new FileReader(f))){
            String line;
            String[] arr;
            while((line=br.readLine())!=null){
                line = line.trim();
                if(line.isEmpty())
                    continue;
                
                arr = line.split("\t");
                nMap.put(arr[0], Integer.parseInt(arr[1]));
            }
                
        }
        catch(Exception e){e.printStackTrace();}
        return nMap;
    }
    
    @Override    
    public void generateContigs(){
        FastaReader fr = new FastaReader();
        FastaWriter frw = new FastaWriter(outfile);        
        try{
            fr.openFile(infile);
            FastaRecord r;
                                                
            while((r=((FastaRecord)fr.readNextRecord()))!=null){
//                if(r.getReadRecordLength()<Utilities.simulation_mincontiglength){
//                    System.out.println("This reference chromosome is smaller than "+Utilities.simulation_mincontiglength+" base pairs, which is the minimum required length");
//                    System.out.println("Hence no contigs would be created using this reference chromosome");
//                    continue;
//                }
                // calculate the total gaps in reference r
                totalNReference+=r.getNcount();
                
                totrefnumber++;
                totrefsize+=r.getReadRecordLength();
                
                // now we read the reference sequence and create a user defined number of contigs                 
                if(this.totalContigNumberMap.containsKey(r.getName())){
                    if(setseed)
                        rand.setSeed(r.getReadRecordLength());
                    createContigsOnReference(r, frw, totalContigNumberMap.get(r.getName()));
                }                
                else{
                    System.out.println("Skipping reference sequence "+r.getName());
                }
            }
            fr.closeReader();
        }
        catch(IOException io){io.printStackTrace();} 
        catch (IllegalIntervalException ex) {ex.printStackTrace();}
        finally{
            frw.closeWriter();
        }
        report();
        
    }
    private void createContigsOnReference(FastaRecord r, FastaWriter frw, int number) throws IllegalIntervalException{
        int contlen;
        int st;
        Interval inte;
        int maxItLimit = this.maxIterationLimit * number;
        int totalIterationCnt=0;
        int seqlen = r.getReadRecordLength() ;
        IntervalSearchTree ist = new IntervalSearchTree();
        Orientation or;
        int totgeneratedcontnumber=0;
        ContigDetails tmp;
        int contnumber=0;
        OUTER: while(true){                
            if(totgeneratedcontnumber>=number)
                break;                
            if(totalIterationCnt > maxItLimit){
                System.out.println("Error: this many contigs couldn't be generated with the given parameters from this reference chromosome"+r.getName()+" after "+totalIterationCnt+" iterations");
                if(totgeneratedcontnumber ==0)
                    System.out.println("No contigs could be generated "+r.getName());
                else
                    System.out.println(totgeneratedcontnumber+" contigs were generated on "+r.getName());                                                    
                break;
            }
            contlen = getRandomLength(r.getReadRecordLength());
            //lengths.add(contlen);
            
            int end;                
            while(true){
                if(totalIterationCnt > maxItLimit){
                    System.out.println("Error: this many contigs couldn't be generated with the given parameters from this reference chromosome"+r.getName()+" after "+totalIterationCnt+" iterations");
                    if(totgeneratedcontnumber ==0)
                        System.out.println("No contigs could be generated on "+r.getName());
                    else
                        System.out.println(totgeneratedcontnumber+" contigs were generated on "+r.getName());                                                    
                    break OUTER;
                }

                st = rand.nextInt(seqlen);
                end = st+contlen;
                if(end>seqlen){
                    totalIterationCnt++;
                    continue;                        
                }                    
                try {
                    inte = new Interval(st+1, end, false);
                    List<Interval> intervals = r.getnIntervalsTree().searchAll(inte);
                    Collections.sort(intervals);
                    // now calculate the total N ratio
                    int ntot=0;
                    for(Interval it : intervals){                        
                        ntot += inte.overlap(it);                        
                    }
                    double nrat = (double)ntot/(double)inte.getlength();                
                    if(nrat <= Utilities.nratio){
                        // good news
                        // Now check if this interval is overlapping with previous ones
                        if(!Utilities.contigsOverlap){
                            if(ist.search(inte)==null)
                                ist.put(inte);
                            else{
                                totalIterationCnt++;
                                continue;
                            }
                        }
                        //totalIterationCnt=0;

                        // we checked everything and this interval is good to create a contig
                        totgeneratedcontnumber++;
                        this.totcontnumber++;
                        FastaRecord frec;
                        String fin;
                        
                        String s = r.getRead().substring(inte.getlow()-1, inte.gethigh());
                        //or = Math.random()<0.5?Enums.Orientation.F:Enums.Orientation.R;
                        or = rand.nextDouble()<0.5?Enums.Orientation.F:Enums.Orientation.R;
                        if(or==Orientation.R){
                            fin = Utilities.reverseComplement(s);            
                        }
                        else{
                            fin = s;
                        }
                        //frec = new FastaRecord(">contig"+(++contnumber)+Utilities.primaryChromosomeSep+r.getName(), fin);    
                        frec = new FastaRecord(">contig"+totcontnumber, fin);    
                        totalN+=frec.getNcount();
                        tmp = new ContigDetails(frec.getName(), r.getName(), inte, or, frec.getNcount(), frec.getNratio());
                        finalcontigcoords.add(tmp);
                        totsize+=frec.getReadRecordLength();

                        frw.write(frec);
                        add2Lengths(tmp);
                        
                        break;
                    }
                    else{
                        totalIterationCnt++;
                        continue;                        
                    }
                } catch (IllegalIntervalException ex) {
                    ex.printStackTrace();
                }                
            }
        }
        //this.totcontnumber+=totgeneratedcontnumber;
    }
}
