package org.fda.basicstats;


import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedWriter;
import java.io.EOFException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.text.NumberFormat;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
import org.fda.commands.AlignEvalCommand;
import org.fda.data.Enums.LLevel;
import org.fda.data.Enums.NLevel;
import org.fda.data.Utilities;
import org.fda.inputdataparser.FastaRecord;
import org.fda.inputdataparser.ReadRecord;


/**
 *
 * @author Gokhan.Yavas
 */
public class BasicStatsCompute {
    //private final List<LengthGCCountPair> contigLengths;
    private ContigRecord[] contigRecordsArray;
    private long sum = 0;
    private double mean = 0;
    private int min = Integer.MAX_VALUE;
    private int max = 0;
    private final Map<NLevel, Integer> nlevels;
    private final Map<LLevel, Integer> llevels;
    private long contigNo=0; 
    private double median = 0;
    private final File basicStatsFile;
    private long total_gc_count=0L;
    private double total_gc_percent=0;
//    private final InputDataType idt;
    private final File objFile;
    private File tmpObjFile;
    private ObjectOutputStream contigRecordsStream;
    private long total_n_count =0L;
    private double nratio;
//    public BasicStatsCompute(File targetFolder, InputDataType idt, boolean justModelling){
    public BasicStatsCompute(File targetFolder, boolean justModelling){        
        nlevels = new TreeMap<>();
        llevels = new TreeMap<>();
        //basicStatsFile = new File(targetFolder.getAbsolutePath()+File.separator+Utilities.assemblystatsFile);
        if(!justModelling){
            basicStatsFile = new File(((AlignEvalCommand)Utilities.current_cmd).getEvalDestFolder().getAbsolutePath()+File.separator+Utilities.assemblystatsFile);
        }
        else{
            basicStatsFile = null;
        }
        objFile = new File(targetFolder.getAbsolutePath()+File.separator+Utilities.contigObjFile);
//        this.idt = idt;
        
        try{
            tmpObjFile = new File(Utilities.rootDir.getAbsolutePath()+File.separator+"contigRec.tmp");
            tmpObjFile.deleteOnExit();
            contigRecordsStream = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(tmpObjFile))) ;
        }
        catch(Exception e){e.printStackTrace();}
    }
    public long getNumberofContigs(){
        return contigNo;
    }    
    public File getStatsFile(){
        return basicStatsFile;
    }
    public long getSum(){
        return sum;
    }
    public Map<NLevel, Integer> getNLevels(){
        return nlevels;
    }
    public long getTotalGCcount(){
        return this.total_gc_count;
    }
    public double getTotalGCpercent(){
        return this.total_gc_percent;
    }
    public double getNRatio(){
        return this.nratio;
    }
    public long getNCount(){
        return this.total_n_count;
    }
    public void addLength(ReadRecord r){
        contigNo++;
        try {
            //contigLengths.add(new ContigRecord(r.getReadRecordLength(), r.getGCcount()));
            contigRecordsStream.writeObject(new ContigRecord(r.getName(), r.getReadRecordLength(), r.getGCcount(), r.getNcount(), 
                    ((FastaRecord)r).getHashValue(), ((FastaRecord)r).getSeqHashValue(), ((FastaRecord)r).getnIntervalsTree()));
        } catch (IOException ex) {
            ex.printStackTrace();
        }
        if(r.getReadRecordLength() < min)
            min = r.getReadRecordLength();
        if(r.getReadRecordLength() > max)
            max = r.getReadRecordLength();        
        sum = sum + r.getReadRecordLength();
        total_gc_count = total_gc_count + r.getGCcount();  
        total_n_count = total_n_count + r.getNcount();
    }
    private void setMean(){
        mean = (double)sum / (double)contigNo;  
    }
    public double getMean(){
        return mean;
    }
    private void setMedian(){
        //median = contigLengths.get(contigLengths.size()/2).getLength();
        median = contigRecordsArray[contigRecordsArray.length/2].getLength();
    }
    public double getMedian(){
        return median;
    }    
    private void setMinMax(){
        max = this.contigRecordsArray[contigRecordsArray.length-1].getLength();
        min = this.contigRecordsArray[0].getLength();
        
    }
    public int getMin(){
        return this.min;        
    }
    public int getMax(){
        return this.max;        
    }
    private void setGCPercent(){
        this.total_gc_percent = 100*(double)this.total_gc_count / (double)this.getSum();
    }
    private void setNRatio(){
        this.nratio = (double)this.total_n_count / (double)this.getSum();
    }
    
    public void computeStats(){
        
        
        setMean();
        setGCPercent();
        setNRatio();
        try {
            // first close the temprary object stream for the contig lengths
            contigRecordsStream.close();
        } catch (IOException ex) {
            ex.printStackTrace();
        }
        contigRecordsArray = new ContigRecord[(int)contigNo];
        ObjectInputStream ois=null;
        try{
            ois = new ObjectInputStream(new BufferedInputStream(new FileInputStream(tmpObjFile)));
            int i=0;
            while(true){
                contigRecordsArray[i]=(ContigRecord)ois.readObject();
                i++;
            }

        }
        catch(ClassNotFoundException e){e.printStackTrace();}
        catch (IOException ex) {

            if(!(ex instanceof EOFException))
                ex.printStackTrace();
        }            
        finally {
            try {
                ois.close();
            } catch (IOException ex) {
                    ex.printStackTrace();                    
            }
        }                            

        Arrays.sort(contigRecordsArray);       
        setMedian();

        NLevel Ngoal=NLevel.N10;
        LLevel Lgoal=LLevel.L10;        
        double temp=0;
        int count=0;
        for(int i=contigRecordsArray.length-1; i >=0; i--){            
            count++;
            temp += contigRecordsArray[i].getLength() ;                
            if(Ngoal!=null && Utilities.calcPercentage(temp, sum) >= Ngoal.getThreshold()){
//                    nlevels.put(Ngoal, contigLengths.get(i).getLength());
                nlevels.put(Ngoal, contigRecordsArray[i].getLength());
                llevels.put(Lgoal, count);
                Ngoal = Ngoal.nextLevel();
                Lgoal = Lgoal.nextLevel();
            }
        }
        if(Ngoal != null){
            NLevel tmp = Ngoal.prevLevel();
            LLevel tmp2 = Lgoal.prevLevel();
            while(Ngoal !=null){
                nlevels.put(Ngoal, nlevels.get(tmp));
                llevels.put(Lgoal, llevels.get(tmp2));
                Ngoal = Ngoal.nextLevel();
                Lgoal = Lgoal.nextLevel();
            }
        }

    }        
    public void write2File(){
        NumberFormat nr = Utilities.numberFormatter;
        Map<String, ContigRecord> contRecMap = new HashMap<>();
        for(ContigRecord r : contigRecordsArray){
            contRecMap.put(r.getContigName(), r);
        }        
        try(ObjectOutputStream out = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(this.objFile))); ){
            //out.writeObject(this.contigLengths);
            //out.writeObject(new ArrayList<ContigRecord>(Arrays.asList(contigRecordsArray)));
            out.writeObject(contRecMap);
        }
        catch(IOException e){e.printStackTrace();}
        if(getStatsFile()==null)
            return;
        try(BufferedWriter bw = new BufferedWriter(new FileWriter(getStatsFile()))){
            bw.append("Number of scaffolds: "+nr.format(this.contigNo)+Utilities.ls);
            bw.append("Total scaffold length: "+ nr.format(getSum())+Utilities.ls);
            bw.append("Max scaffold length: "+ nr.format(getMax())+Utilities.ls);
            bw.append("Min scaffold length: "+ nr.format(getMin())+Utilities.ls);
            bw.append("Mean scaffold length: "+ nr.format(getMean())+Utilities.ls);
            bw.append("Median scaffold length: "+ nr.format(getMedian())+Utilities.ls);
            bw.append("GC count: "+nr.format(getTotalGCcount())+Utilities.ls);
            bw.append("GC percentage: "+nr.format(getTotalGCpercent())+Utilities.ls);
            bw.append("Number of Ambigious (N) bases: "+nr.format(getNCount())+Utilities.ls);
            bw.append("Ratio of Ambigious (N) bases: "+nr.format(getNRatio())+Utilities.ls);
            

            bw.append(Utilities.ls+"N Stats"+Utilities.ls);
            for(NLevel n : NLevel.values()){
                bw.append(n+"\t"+nr.format(this.nlevels.get(n))+Utilities.ls);
            }
            bw.append(Utilities.ls+"L Stats"+Utilities.ls);
            for(LLevel n : LLevel.values()){
                bw.append(n+"\t"+nr.format(this.llevels.get(n))+Utilities.ls);
            }


        }
        catch(Exception e){e.printStackTrace();}        
        
        
    }
    
}
