package org.fda.contiggenerator;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
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;

/**
 *
 * @author Gokhan.Yavas
 */
public class MisassemblyGenerator {
    protected final File infile;
    protected final File outfile;    
    protected final int threshold;
    protected final int avgMisNumber;
    public MisassemblyGenerator(File infile, File outfile, int threshold, int avgMisNumber){
        this.infile = infile;
        this.outfile = outfile;
        this.threshold = threshold;
        this.avgMisNumber = avgMisNumber;
        File parent = outfile.getParentFile();
        
        if(parent!=null && !parent.exists())
            parent.mkdirs();        
        
    }
    public void process(){
        FastaReader fr = new FastaReader();
        FastaWriter frw = new FastaWriter(outfile);  
        
        try{
            BufferedWriter bw = new BufferedWriter(new FileWriter(new File(outfile.getAbsolutePath()+".coords")));
            fr.openFile(infile);
            FastaRecord r;
            bw.append("Chr\tStart\tEnd\tOrientation"+Utilities.ls);
                                                
            while((r=((FastaRecord)fr.readNextRecord()))!=null){
                misassemble(r, frw, bw);
            }
            fr.closeReader();
            bw.close();
        }
        catch(IOException io){io.printStackTrace();} 
        catch (IllegalIntervalException ex) {ex.printStackTrace();}
        finally{
            frw.closeWriter();
        }
    }
    private void misassemble(FastaRecord r, FastaWriter fw, BufferedWriter bw) throws IllegalIntervalException, IOException{
        // Determine how long the average contig sub part needs to be, to be able to introduce avgMisNumber of misassemblies
        int contiglength=r.getReadRecordLength();
        int partslen=contiglength/(this.avgMisNumber+1);
        FastaRecord frec;
        List<Interval> intArr = new ArrayList();
        List<Orientation> orArr = new ArrayList<>();
        int st=1;
        int end;
        for(int i =0;i<=avgMisNumber;i++){
            if(i==avgMisNumber)
                intArr.add(new Interval(st, contiglength, false));
            else
                intArr.add(new Interval(st, st + partslen-1, false));
            st = st + partslen;                     
            orArr.add(Orientation.F);
        }        
        
        Random rand= new Random();
        double randno;
        boolean done;
        for(int i=0; i<intArr.size()-1; i++){
            randno=rand.nextDouble();
            done = false;
            while(!done){
                if(randno<0.33){
                    // do a overlap relocation
                    try{
                        if(rand.nextDouble()>0.5){
                            intArr.get(i).sethigh(Math.min(intArr.get(i).gethigh()+(int)(2*this.threshold), contiglength));
                        }
                        else{
                            intArr.get(i+1).setlow(Math.max(intArr.get(i+1).getlow()-(int)(2*this.threshold), 1));
                        }        
                        done=true;
                    }
                    catch(IllegalIntervalException ie){
                        randno+=0.33;
                        continue;
                    }
                }
                else if(randno < 0.66){
                    // do a distance relocation
                    try{
                        if(rand.nextDouble()>0.5){
                            intArr.get(i).sethigh(intArr.get(i).gethigh()-(int)(2*this.threshold));
                        }
                        else{
                            intArr.get(i+1).setlow(intArr.get(i+1).getlow()+(int)(2*this.threshold));
                        }                                    
                        done=true;
                    }
                    catch(IllegalIntervalException ie){
                        randno+=0.33;
                        continue;
                    }                    
                }
                else{
                    // do an inversion
                    for(int j=i+1; j<intArr.size();j++){
                        if(orArr.get(j)==Orientation.F){
                            orArr.set(j, Orientation.R);
                        }
                        else{
                            orArr.set(j, Orientation.F);
                        }
                    }
                    done=true;
                }            
            }
        }
        StringBuilder sb = new StringBuilder();
        String tmpstr;
        // Now do the actual string manipulation
        for(int i=0; i<intArr.size(); i++){
            tmpstr=r.getRead().substring(intArr.get(i).getlow()-1, intArr.get(i).gethigh());
            if(orArr.get(i)==Orientation.R)
                sb.append(Utilities.reverseComplement(tmpstr));
            else
                sb.append(tmpstr);
            bw.append(r.getName()+"\t"+intArr.get(i).getlow()+"\t"+intArr.get(i).gethigh()+"\t"+orArr.get(i)+Utilities.ls);
            
        }
        frec = new FastaRecord(r.getHeader(), sb.toString());
        fw.write(frec);
        
    }
    public static void main(String[] args){
        File infile = new File("contigs1.fa");
        File outfile = new File("out.fa");
        int misThre = 1000;
        int number = 200;
        MisassemblyGenerator mg = new MisassemblyGenerator(infile, outfile, misThre, number);
        mg.process();
        
    }
}
