package org.fda.contiggenerator;


import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import org.fda.data.Enums;
import org.fda.data.Utilities;
import org.fda.exceptions.IllegalIntervalException;
import org.fda.graphdrawing.data.CustomHistogramDataset;
import org.fda.inputdataparser.FastaReader;
import org.fda.inputdataparser.FastaRecord;
import org.fda.inputdataparser.FastaWriter;
import org.fda.intervaltree.Interval;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.data.statistics.HistogramType;

/**
 *
 * @author Gokhan.Yavas
 */
public abstract class ContigGenerator {
    protected final File infile;
    protected final File outfile;    
    //protected BufferedReader br=null;
    protected List<Integer> lengths= new ArrayList<>();
    protected Map<String, List<Integer>> lengthsMap= new TreeMap();
    //protected Map<String, List<ContigDetails>> intervalsMap= new HashMap<>();
    protected List<ContigDetails> finalcontigcoords = new ArrayList<>();
    protected int totcontnumber=0;
    protected long totsize=0L;
    protected long totrefsize=0L;
    protected int totrefnumber=0;
    protected long totalN=0;
    protected long totalNReference=0L;
    protected boolean createreport=true;
    
    public ContigGenerator(File infile, File outfile){
        this.infile = infile;
        this.outfile = outfile;
        File parent = outfile.getParentFile();
        
        if(parent!=null && !parent.exists())
            parent.mkdirs();        
        
    }

    public File getInfile() {
        return infile;
    }

    public File getOutfile() {
        return outfile;
    }

    public List<Integer> getLengths() {
        return lengths;
    }

    public List<ContigDetails> getFinalcontigcoords() {
        return finalcontigcoords;
    }

    public int getTotcontnumber() {
        return totcontnumber;
    }

    public long getTotsize() {
        return totsize;
    }

    public long getTotrefsize() {
        return totrefsize;
    }

    public int getTotrefnumber() {
        return totrefnumber;
    }

    public long getTotalN() {
        return totalN;
    }

    public long getTotalNReference() {
        return totalNReference;
    }

    public boolean isCreatereport() {
        return createreport;
    }
    public ContigGenerator(File infile, File outfile, boolean createreport){
        this.infile = infile;
        this.outfile = outfile;
        File parent = outfile.getParentFile();
        this.createreport = createreport;
        
        if(parent!=null && !parent.exists())
            parent.mkdirs();        
        
    }
    
    protected void report(){
        if(!createreport)
            return;
        File report, coordsReport;
        report = new File(outfile.getAbsolutePath()+".report");
        coordsReport = new File(outfile.getAbsolutePath()+".coords");
        Collections.sort(lengths);
        //int number = lengths.size();
        NumberFormat nr = Utilities.numberFormatter;
        try(BufferedWriter bw = new BufferedWriter(new FileWriter(report)); BufferedWriter bwCoords = new BufferedWriter(new FileWriter(coordsReport))){
            if(!lengths.isEmpty()){
                bwCoords.append("Contig Name\tCoords\tOrientation\tLength\tAmbiguousBaseCount(#N)\tAmbiguousBaseRatio(Nratio)"+Utilities.ls);
                bw.append("Total reference length (in bp): "+totrefsize+Utilities.ls);
                bw.append("Total ambiguious base size (N) in the reference: "+totalNReference+Utilities.ls);
                bw.append("Total number of reference sequences (chromosomes): "+totrefnumber+Utilities.ls);
                bw.append("Total number of contigs generated: "+this.totcontnumber+Utilities.ls);
                bw.append("Total contig length(bp): "+totsize+Utilities.ls);            
                bw.append("Min contig length(bp): "+lengths.get(0)+Utilities.ls);
                bw.append("Max contig length(bp): "+lengths.get(lengths.size()-1)+Utilities.ls);
                NumberFormat nm = Utilities.numberFormatter;
                double mean = (double)totsize / (double)this.totcontnumber;
                bw.append("Mean contig length(bp): "+nm.format(mean)+Utilities.ls);            
                bw.append("Median contig length(bp): "+lengths.get(this.totcontnumber/2)+Utilities.ls);
                bw.append("Total ambiguious base (N) size in contigs (bp): "+totalN+Utilities.ls);
                bw.append("========================================"+Utilities.ls);
                bw.append("Contig Length Distribution"+Utilities.ls);
//                for(Integer i : lengths){
//                    bw.append(i+Utilities.ls);
//                }
                for(String s : lengthsMap.keySet()){
                    List<Integer> smp = lengthsMap.get(s);
                    Collections.sort(smp);
                    bw.append(">"+s+Utilities.ls);
                    for(int k : smp){
                        bw.append(k+Utilities.ls);
                    }                    
                }
                
//                
                for(ContigDetails d : this.finalcontigcoords){
                    
                    //bwCoords.append(d.getName()+"\t"+d.getOr()+"\t"+d.getIr().getlength()+"\t"+d.getNcount()+"\t"+nr.format(d.getNratio())+Utilities.ls);
                    bwCoords.append(d+Utilities.ls);
                }
                System.out.println("Contigs are generated and saved in "+outfile.getAbsolutePath());
                System.out.println("Report about the contigs' stats are saved in "+report.getAbsolutePath());
                System.out.println("The generated contigs' coordinates are saved in "+coordsReport.getAbsolutePath());
                drawLengthHistogram();
            }
            else{
                bw.append("No contigs generated!");
                bwCoords.append("No contigs generated!");
            }            
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }
    protected void drawLengthHistogram(){
        double[] lengthlist = new double[lengths.size()];
        for(int i=0;i<lengths.size(); i++){
            lengthlist[i] = (double)lengths.get(i);
        }
        CustomHistogramDataset dataset = new CustomHistogramDataset();
        dataset.setType(HistogramType.FREQUENCY);
        
        dataset.addSeries("Length Frequency", lengthlist, Utilities.no_hist_bins,lengthlist[0], lengthlist[lengths.size()-1]);
        
        JFreeChart chart = ChartFactory.createHistogram("Contig Length Histogram ", null, null, dataset,PlotOrientation.VERTICAL, true, false, false);
        chart.getXYPlot().setForegroundAlpha(0.75f);   
        
        //chart.getXYPlot().getDomainAxis().setRange(lengthlist[0], lengthlist[lengths.size()-1]);
        
        File targetFile = new File(outfile.getAbsolutePath()+".histogram"+Utilities.format.getExtension());

        //Utilities.saveAsFile(chart, targetFile, dataset.toTable(Utilities.intervalLength4Histogram));
        Utilities.saveAsFile(chart, targetFile);
        
        
    }
    public void addRefAsContigs(){
        FastaReader fr = new FastaReader();
        FastaWriter frw = new FastaWriter(outfile, true);        
        ContigDetails tmp;
        try{
            fr.openFile(infile);
            FastaRecord r;
                                                
            while((r=((FastaRecord)fr.readNextRecord()))!=null){
                //r.setName(r.getName()+Utilities.primaryChromosomeSuffix);
                frw.write(r);
                //lengths.add(r.getReadRecordLength());
                
                totsize+=r.getReadRecordLength();
                totalN+=r.getNcount();
                totcontnumber++;
                tmp = new ContigDetails(r.getName(), r.getName(), new Interval(1, r.getReadRecordLength(), false), Enums.Orientation.F, r.getNcount(), r.getNratio());
                finalcontigcoords.add(tmp);
                add2Lengths(tmp);
            }
            fr.closeReader();
        }
        catch(IOException io){
            io.printStackTrace();
        } 
        catch (IllegalIntervalException ex) { 
            ex.printStackTrace();
        } 
        finally{
            frw.closeWriter();
        }
        report();        
    }    
    protected void add2Lengths(ContigDetails tmp){
        lengths.add(tmp.getIr().getlength());
        if(lengthsMap.containsKey(tmp.getRefname())){
            lengthsMap.get(tmp.getRefname()).add(tmp.getIr().getlength());
        }
        else{
            List<Integer> tlist = new ArrayList<>();
            tlist.add(tmp.getIr().getlength());
            lengthsMap.put(tmp.getRefname(), tlist);
        }
        
    }
    
}
