/**
 *
 */
package org.expasy.bprg;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;

import org.apache.log4j.Logger;

/**
 * @author chernand
 *
 */
public class PeptideQuantiInfos {

    /**
     * Logger
     */
    private static Logger log = Logger.getLogger(PeptideQuantiInfos.class);

    private String proteinName;
    private String peptideString;
    private int labeledAANumber;
    private String form;
    private String masses;

    /**
     * Light mass effectively found in file;
     */
    private double lightMass;

    /**
     * Peak areas : light / light mass -1Da / light mass -2Da
     */
    private double c12Areas = 0;

    /**
     * Heavy mass effectively found in file;
     */
    private double heavyMass;

    /**
     * Peak areas : heavy / heavy mass -1Da / heavy mass -2Da
     */
    private double[] c13Areas = new double[3];

    /**
     * Intermediate masses between light and heavy forms, ordered from heavy to light.
     */
    private double[] intermMasses;

    /**
     * Peak areas :
     * heavy-1aa / heavy-1aa -1Da / heavy-1aa -2Da
     * heavy-2aa / heavy-2aa -1Da / heavy-2aa -2Da
     * heavy-3aa / heavy-3aa -1Da / heavy-3aa -2Da
     * etc.
     */
    private double[][] intermAreas;

    /**
     * Parameters used for quantitation.
     */
    private SilacQuantiParam quantiParams;

    private boolean fileFound = true;

    ///////////////////////////////////////////////////////////////////////////

    /**
     *
     */
    public PeptideQuantiInfos() {
    }


    public void collectInfos(PhenyxSILACPeptide phenyxPeptide, SilacQuantiParam params) {
        quantiParams = params;
        if(quantiParams.data.size() == 1) {
            collectInfos1Label(phenyxPeptide, quantiParams.data.get(0));
        }
        else {
            collectInfosMultiLabels(phenyxPeptide, params);
        }
    }


    /**
     * @param phenyxPeptide
     * @param params
     */
    private void collectInfos1Label(PhenyxSILACPeptide phenyxPeptide,
            PeptideLabel peptideLabel) {

        proteinName = phenyxPeptide.getProteinName();
        peptideString = phenyxPeptide.getPeptideString();

        labeledAANumber = phenyxPeptide.getAACount(new char[]{peptideLabel.aminoacid.charAt(0)});

        //compute heavy and light masses to search
        double massOrigRead = phenyxPeptide.getPeptideMass();
        boolean isHeavyForm = phenyxPeptide.isLabeledWith(peptideLabel.label);
        double massLightToFind = isHeavyForm? massOrigRead- labeledAANumber*peptideLabel.shift : massOrigRead;
        double massHeavyToFind = isHeavyForm? massOrigRead : massOrigRead+ labeledAANumber*peptideLabel.shift;

        form = isHeavyForm?peptideLabel.label:"Normal";

        //load content of coresponding pmf file
        String labelId = phenyxPeptide.getSpectrumId();
        double[][] peakAreasData = loadFile(labelId);

        //if loading not ok (file not found)
        if(peakAreasData==null) {
            masses = "Label: "+labelId+" : file not found.";
            fileFound  = false;
        }
        else{
            //add label infos to excel file
            findPA(peakAreasData, massLightToFind, massHeavyToFind, labeledAANumber);
            String labelToDisplay = phenyxPeptide.getSpectrumLabel() + getModifsAsStrExceptLabel(phenyxPeptide, peptideLabel.label);
            labelToDisplay += "/" + (lightMass==0?"?":""+lightMass);
            labelToDisplay += "/" + (heavyMass==0?"?":""+heavyMass);
            if(intermMasses!=null) {
                for(int foundmass=0; foundmass<intermMasses.length; foundmass++) {
                    labelToDisplay += "/" + (intermMasses[foundmass]==0?"?":""+intermMasses[foundmass]);
                }
            }
            masses = labelToDisplay;
        }
    }

    /**
     * @param phenyxPeptide
     * @param params
     */
    private void collectInfosMultiLabels(PhenyxSILACPeptide phenyxPeptide,
            SilacQuantiParam params) {

        //Count nb of each aa marked
        int counter = phenyxPeptide.getAAPresent(params.getAALabels());
        
        // if 0 present
        // return, do nothing
        if(counter == 0) {
            return;
        }
        
        // if 1 present (&& multi label mode)
        // can do a simple search  with only the label of the contained aa
        else if (counter == 1) {
            int[] counts = phenyxPeptide.getAACounts(params.getAALabels());
            int i;
            for (i = 0; i<counts.length && counts[i]==0; i++) { }
            //  if labeled -> is heavy form; compute light mass and quantify ratio
            //  if not labeled -> is light form; compute heavy mass and quantify ratio
            // forwarding to 1Label function
            collectInfos1Label(phenyxPeptide, params.data.get(i));
        }
        
        // if multiple present : complete search
        else {
            boolean[] labelsThere = phenyxPeptide.isLabeledWith(params.getTagLabels());

            //  if partially labeled -> most probably a mismatch; don't keep this identification
            String labelsAsString = Arrays.toString(labelsThere);
            if(labelsAsString.contains("true") && labelsAsString.contains("false")){
                return;
            }
            
            double massOrigRead = phenyxPeptide.getPeptideMass();
            proteinName = phenyxPeptide.getProteinName();
            peptideString = phenyxPeptide.getPeptideString();
            labeledAANumber = phenyxPeptide.getAACount(params.getAALabels());
            
            //compute current shift
            int[] counts = phenyxPeptide.getAACounts(params.getAALabels());
            double shift = 0;
            int i=0;
            for (PeptideLabel label : params.data) {
                shift+=label.shift*counts[i];
                i++;
            }
            
            double massLightToFind, massHeavyToFind;
            //  if not labeled -> is light form...
            if(Arrays.equals(labelsThere, new boolean[labelsThere.length])) {

                form = "Normal";
                massLightToFind = massOrigRead;
                massHeavyToFind = massOrigRead + shift;
            }
            //  if all labels -> is heavy form...
            else {
                
                form = Arrays.toString(params.getTagLabels());
                massLightToFind = massOrigRead- shift;
                massHeavyToFind = massOrigRead;                
            }
            
            //load content of coresponding pmf file
            String labelId = phenyxPeptide.getSpectrumId();
            double[][] peakAreasData = loadFile(labelId);
            //if loading not ok (file not found)
            if(peakAreasData==null) {
                masses = "Label: "+labelId+" : file not found.";
                fileFound  = false;
            }
            else{
                //add label infos to excel file
                findPA(peakAreasData, massLightToFind, massHeavyToFind, labeledAANumber); //note: labeledAANumber won't be used with multiple labels
                String labelToDisplay = phenyxPeptide.getSpectrumLabel() + getModifsAsStrExceptLabel(phenyxPeptide, params.getTagLabels());
                labelToDisplay += "/" + (lightMass==0?"?":""+lightMass);
                labelToDisplay += "/" + (heavyMass==0?"?":""+heavyMass);
                masses = labelToDisplay;
            }
        }
        
    }

    
    /**
     * @param peptide
     * @param label 
     * @return
     */
    private String getModifsAsStrExceptLabel(PhenyxSILACPeptide peptide, String label) {
        String returnedStr = "/";
        boolean doesContainModifLabel = false;
        Iterable<String> modifsSet = peptide.getModifsExcept(label);
        for (String string : modifsSet) {
            returnedStr += string;
            doesContainModifLabel=true;
        }
        return doesContainModifLabel?returnedStr:"";
    }

    /**
     * @param peptide
     * @param label 
     * @return
     */
    private String getModifsAsStrExceptLabel(PhenyxSILACPeptide peptide, String[] label) {
        String returnedStr = "/";
        boolean doesContainModifLabel = false;
        Iterable<String> modifsSet = peptide.getModifsExcept(label);
        for (String string : modifsSet) {
            returnedStr += string;
            doesContainModifLabel=true;
        }
        return doesContainModifLabel?returnedStr:"";
    }

    /**
     * @param peakAreasData
     * @param massLightToFind
     * @return
     */
    private void findPA(double[][] peakAreasData, double massLightToFind, double massHeavyToFind, int aaCounter) {
        log.debug("Trying to find masses "+massLightToFind+ " and " +massHeavyToFind);

        int i =0;
        for( ; i<peakAreasData.length; i++) {
            if( massLightToFind-quantiParams.error <= peakAreasData[i][0] && peakAreasData[i][0] <= massLightToFind+quantiParams.error ) {
                lightMass = peakAreasData[i][0];
                c12Areas = peakAreasData[i][1];
                log.debug("Found mass C12 : "+ lightMass+" and pa : "+c12Areas);

//                if(quantiParams.findEchoPeaks >=1 && i>0) {
//
//                    if( massLightToFind-1-quantiParams.error <= peakAreasData[i-1][0] && peakAreasData[i-1][0] <= massLightToFind-1+quantiParams.error ) {
//                        c12Areas[1] = peakAreasData[i-1][1];
//                        log.debug("Found pa for mass C12-1Da : "+ c12Areas[1]);
//
//                        if(quantiParams.findEchoPeaks ==2 && i>1) {
//
//                            if( massLightToFind-2-quantiParams.error <= peakAreasData[i-2][0] && peakAreasData[i-2][0] <= massLightToFind-2+quantiParams.error ) {
//                                c12Areas[2] = peakAreasData[i-2][1];
//                                log.debug("Found pa for mass C12-2Da : "+ c12Areas[2]);
//                            }
//                        }
//                    }
//                }
                break;
            }
        }

        if(i>=peakAreasData.length) {
            i=0;//si l'on n'a pas trouvé la pa C12, on recommence du début
        }

        // don't look for -2 Da if peptide has only 2 heavy aa
        // don't look for -1 and -2 Da if peptide has only 1 heavy aa
        boolean heavyMassFound = false;
        for(int iC13 = i; iC13<peakAreasData.length; iC13++) {
            if( massHeavyToFind-quantiParams.error < peakAreasData[iC13][0] && peakAreasData[iC13][0] < massHeavyToFind+quantiParams.error ) {
                
                heavyMass = peakAreasData[iC13][0];
                c13Areas[0] = peakAreasData[iC13][1];
                heavyMassFound = true;
                log.debug("Found mass C13 : "+ heavyMass+" and pa : "+c13Areas[0]);

                // don't look for -1 Da and -2 Da if peptide has only 1 heavy aa
                if(aaCounter == 1) {
                    c13Areas[2] = c13Areas[1] = -1; //say that we won't display these values
                }
                // don't look for -2 Da if peptide has only 2 heavy aa
                if(aaCounter == 2) {
                    c13Areas[2] = -1; //say that we don't display this value
                }
                // NB: we have to initialize this here as we may not reach the search for -2Da if we don't find -1Da
                
                if(quantiParams.findEchoPeaks >=1 && aaCounter > 1 && iC13>0) {
                    
                    if( massHeavyToFind-1-quantiParams.error <= peakAreasData[iC13-1][0] && peakAreasData[iC13-1][0] <= massHeavyToFind-1+quantiParams.error ) {
                        c13Areas[1] = peakAreasData[iC13-1][1];
                        log.debug("Found pa for mass C13-1Da : "+ c13Areas[1]);

                        if(quantiParams.findEchoPeaks ==2 && aaCounter > 2 && iC13>1) {

                            if( massHeavyToFind-2-quantiParams.error <= peakAreasData[iC13-2][0] && peakAreasData[iC13-2][0] <= massHeavyToFind-2+quantiParams.error ) {
                                c13Areas[2] = peakAreasData[iC13-2][1];
                                log.debug("Found pa for mass C13-2Da : "+ c13Areas[2]);
                            }
                        }
                    }
                }

                break;
            }
        }

        // ne pas rechercher les peak à -6Da, -12Da, etc ... si le pic 13C n'a pas été trouvé
        //NB: if there are multiple labels, interm peaks will not be searched
        if(quantiParams.findIntermPeaks && heavyMassFound) {

            intermMasses = new double [aaCounter-1];
            intermAreas = new double [aaCounter-1] [3];

            //from 1 till (max aa nb -1)
            for(int aaNb=1; aaNb<aaCounter; aaNb++) {
                double massInterm = massLightToFind+aaNb*quantiParams.data.get(0).shift;

                for( ; i<peakAreasData.length; i++) {
                    if( massInterm-quantiParams.error <= peakAreasData[i][0] && peakAreasData[i][0] <= massInterm+quantiParams.error ) {
                        intermMasses[intermMasses.length-aaNb] = peakAreasData[i][0];
                        intermAreas[intermAreas.length-aaNb][0] = peakAreasData[i][1];
                        log.debug("Found mass "+massInterm+" : "+ intermMasses[intermMasses.length-aaNb]+" and pa : "+intermAreas[intermAreas.length-aaNb][0]);

                        if(quantiParams.findEchoPeaks >=1 && i>0) {

                            if( massInterm-1-quantiParams.error <= peakAreasData[i-1][0] && peakAreasData[i-1][0] <= massInterm-1+quantiParams.error ) {
                                intermAreas[intermAreas.length-aaNb][1] = peakAreasData[i-1][1];
                                log.debug("Found pa for mass "+massInterm+"-1Da : "+ intermAreas[intermAreas.length-aaNb][1]);

                                if(quantiParams.findEchoPeaks ==2 && i>1) {

                                    if( massInterm-2-quantiParams.error <= peakAreasData[i-2][0] && peakAreasData[i-2][0] <= massInterm-2+quantiParams.error ) {
                                        intermAreas[intermAreas.length-aaNb][2] = peakAreasData[i-2][1];
                                        log.debug("Found pa for mass "+massInterm+"-2Da : "+ intermAreas[intermAreas.length-aaNb][2]);
                                    }
                                }
                            }
                        }
                        break;
                    }
                }

            }
        }

    }


    /**
     * @param labelNb
     * @return
     */
    private double[][] loadFile(String labelId) {
        log.debug("Loading label "+labelId+ " in folder "+quantiParams.paFolder+" with file filter " +quantiParams.paFilter);

        if(quantiParams.paFolder.isDirectory()) {
            File[] folderContent = quantiParams.paFolder.listFiles();
            int iFolderIndex=0;
            for ( ; iFolderIndex<folderContent.length && !folderContent[iFolderIndex].getName().startsWith(quantiParams.paFilter+labelId+"_") ; iFolderIndex++) {
            }

            if (iFolderIndex != folderContent.length){

                File paFile = folderContent[iFolderIndex];
                log.debug("Found file "+paFile);

                try {
                    BufferedReader reader = new BufferedReader(new FileReader(paFile));
                    reader.readLine();

                    ArrayList<String> dataStr = new ArrayList<String>();
                    String lineContent="";
                    while ( (lineContent=reader.readLine())!=null) {
                        if (lineContent.length()!=0)
                            dataStr.add(lineContent);
                    }

                    dataStr.trimToSize();
                    double[][] data = new double[dataStr.size()][2];
                    String[] temp;
                    int i=0;
                    for(Iterator<String> it= dataStr.iterator(); it.hasNext() ; i++ ) {
                        String line = it.next();
                        temp = line.split("\t");
                        data[i][0] = new Double(temp[0]).doubleValue();
                        data[i][1] = new Double(temp[1]).doubleValue();
                        //log.trace(data[i][0]+"*"+data[i][1]);
                    }

                    return data;
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        }

        return null;
    }

    /**
     * Return only C12 value (was also adding c12-1Da and c12-2Da)
     */
    public double getSumLightForms() {
        double sumC12 = c12Areas;
//        sumC12+= c12Areas[1];
//        sumC12+= c12Areas[2];
        return sumC12;
    }

    /**
     *
     */
    public double getSumHeavyForms() {
        double sumC13 = c13Areas[0];
        sumC13+= c13Areas[1];
        sumC13+= c13Areas[2];
        if(intermAreas!=null) {
            for (int i = 0; i < intermAreas.length; i++) {
                sumC13+= intermAreas[i][0];
                sumC13+= intermAreas[i][1];
                sumC13+= intermAreas[i][2];
            }
        }
        return sumC13;
    }

    /**
     * @return the masses
     */
    public String getMasses() {
        return masses;
    }

    /**
     * @return the form
     */
    public String getForm() {
        return form;
    }

    /**
     * @return the labeledAANumber
     */
    public int getLabeledAANumber() {
        return labeledAANumber;
    }

    /**
     * @return the peptideString
     */
    public String getPeptideString() {
        return peptideString;
    }

    /**
     * @return the proteinName
     */
    public String getProteinName() {
        return proteinName;
    }


    /**
     * @return the c12Areas
     */
    public double getC12Areas() {
        return c12Areas;
    }


    /**
     * @return the c13Areas
     */
    public double[] getC13Areas() {
        return c13Areas;
    }


    /**
     * @return the heavyMass
     */
    public double getHeavyMass() {
        return heavyMass;
    }


    /**
     * @return the intermAreas
     */
    public double[][] getIntermAreas() {
        return intermAreas;
    }


    /**
     * @return the intermMasses
     */
    public double[] getIntermMasses() {
        return intermMasses;
    }


    /**
     * @return the lightMass
     */
    public double getLightMass() {
        return lightMass;
    }


    /**
     * @return the fileFound
     */
    public boolean isFileFound() {
        return fileFound;
    }





}
