/***********************************************************************************
SHIFTX+: chemical shift Predictior for protein structure (Version 1.07)

Author   : Beomsoo Han and Yifeng Liu
           University of Alberta
Contact Info: http://www.shiftx2.ca 
           or http://redpoll.pharmacy.ualberta.ca/shiftx/

Reference: Beomsoo Han, Yifeng Liu, Simon W. Ginzinger and David S. Wishart (2011),
           SHIFTX2: Significantly Improved Protein Chemical Shift Prediction,
           Journal of Biomolecular NMR, accepted. 

Updated: all prediction models are changed           
***********************************************************************************/

import java.util.*;
import java.io.*;
import java.text.DecimalFormat;
import java.io.FilenameFilter;
import java.util.regex.*;

import java.net.*;

public class ShiftXp {

    static final String version = "1.09A";
    static final boolean DEBUG = false; 
    static boolean RCOIL_PRINTOUT = false;
    static String SHIFTX2_DIR = ".";  // to support running on other directory

    // Loading only one time, that's why these are here 
    static RandomCoil rcoil = new RandomCoil();
    static LimitedCShift chkcshift = new LimitedCShift();
    static PredictionModels predmodels = new PredictionModels();


    private static void usage() {

        StringBuffer buf = new StringBuffer();
        buf.append("\nSHIFTX+ Version "+ version +"\n");
        buf.append("Detail information: http://www.shiftx2.ca or http://redpoll.pharmacy.ualberta.ca\n");
        buf.append("--------------------------------------------------------------------------------\n\n");

        buf.append("SYNOPSIS\n\t java -Xmx1500m -cp <SHIFTX+ dir>/bin:<SHIFTX+ dir>/lib/weka.jar ShiftXp [options]\n");

        buf.append("OPTIONS:\n");
        buf.append("\t -i [<directory/> infile.pdb | ALLPDB]\n");
        buf.append("\t           A input pdb filename or all pdbs in a certain directory. \n");   
        buf.append("\t -b <directory/filename regular expression>\n");
        buf.append("\t           Batch Mode for specific files which are matched with regular expression. \n");
        buf.append("\t           (Causion: it can be used when it substitutes option '-i') \n");
        buf.append("\t -atoms [ALL | BACKBONE | SIDECHAIN]   Select atoms types, default=ALL\n");
        buf.append("\t -ph   <number>        pH, default=5.0 \n");
        buf.append("\t -temp <number>        Temperature, default=298\n");
        buf.append("\t -d [TRUE | FALSE]  Deuterated pdb, default=FALSE \n"); 
        buf.append("\t -r [TRUE | FALSE]  Print randon coild shift (only for CSV format), default=FALSE \n"); 
        buf.append("\t -dir <SHIFTX+ directory>  The installed directory of SHIFTX+/SHIFTX2\n\n");

        buf.append("EXAMPLE:\n");
        buf.append("\t java -Xmx1500m -cp ~/shiftx2/bin:~/shiftx2/lib/weka.jar ShiftXp -i 1ubq.pdb\n\n");
        buf.append("\n");

        System.out.println( buf );

        System.exit(-1);

    }
    
    public static void main(String[] args) throws Exception {

        if (args.length < 1)  {
            usage();
        }

        DecimalFormat df = new DecimalFormat("#.##");
        
        String inPDBFile = null;
        String atomtype  = "ALL";
        String rcoil_id = "1";
        String outputFormat = "CSV";
        Boolean deuteratedpdb = false; // if true, then call AdjustDeutratePDB()
        Boolean batchmode = false; // if true, then running as bachmode
        double  pH = 5.0;
        double  temperature = 298;   

        String argString = "";        
        for (String s: args) argString += " " + s;  

            if ( args.length % 2 != 0 ) {
                System.err.println("\n********************************************************************************");
                System.err.println("[Error] wrong in parameter usage. See the following usage");
                System.err.println("********************************************************************************\n");

                usage();
            }

        // argList = argList.trim();
        if (DEBUG) System.out.println("Argument:" + argString );
        
        // SET OPTIONS with INPUT ARGUMENTS
        String[] argFields = argString.split(" -");        
        for (String s: argFields) {        
            int pos = s.indexOf(' ');
        
            if (pos > 0) {
               if( s.substring(0,pos).equals("i")) { 
                       inPDBFile = s.substring(pos+1); // input pdbfile 
                   } else if( s.substring(0,pos).equals("b")) { 
                       batchmode = true;
                       inPDBFile = s.substring(pos+1); // input pdbfile as batch mode
                   } else if( s.substring(0,pos).equals("f")) {
                       outputFormat = s.substring(pos+1);  // default: BMRB, [CSV, TABLE] 
               } else if( s.substring(0,pos).equals("ph")) {
                       pH = Double.valueOf(s.substring(pos+1)); // pH
               } else if( s.substring(0,pos).equals("temp")) {
                       temperature = Double.valueOf(s.substring(pos+1)); // temperature(K)
               } else if( s.substring(0,pos).equals("atoms")) {
                       atomtype = s.substring(pos+1); // ALL[default ALL], BACKBONE, SIDECHAIN 
               } else if( s.substring(0,pos).equals("d"))  {
                       deuteratedpdb = s.substring(pos+1).equals("TRUE"); // TRUE/FALSE(default)               
               } else if( s.substring(0,pos).equals("r"))  {
                       RCOIL_PRINTOUT = s.substring(pos+1).equals("TRUE"); // TRUE/FALSE(default)               
               } else if( s.substring(0,pos).equals("dir")) {
                       SHIFTX2_DIR = s.substring(pos+1); // "." or absobute directory
               } else {
                       System.err.println("\n********************************************************************************");
                       System.err.println("[Error]\t unknown parameter '-"+s.substring(0,pos)+"'. See the following usage");
                       System.err.println("********************************************************************************\n");

                       usage();
               } 
           } else if (s.length() != 0) {
               System.err.println("\n********************************************************************************");
               System.err.println("[Error]\t wrong in parameter use: missing value for the option '-"+s+"'"); 
               System.err.println("********************************************************************************\n");

               usage();
           }     
        }

        // check execute directory 
        // 2011.08.31 changed shiftx2_main.py --> shiftx2.py
        if ( !( new File( SHIFTX2_DIR + "/shiftx2.py").exists() 
             &&  new File(SHIFTX2_DIR + "/lib/weka.jar").exists() )  )  {

           System.err.println("\n********************************************************************************");
           System.err.println("[Error]\t Required: When the SHIFTX2 execute on other directory,\n"
                             +"\t it should be set as a parameter. Use '-dir' option.");
           System.err.println("> Current directory: "+System.getProperty("user.dir"));
           System.err.println("********************************************************************************\n");

           usage();
        }

        if ( inPDBFile == null || inPDBFile.length() < 1)  {
           System.err.println("\n********************************************************************************");
           System.err.println("[Error] Required: There is no input PDB filename.");
           System.err.println("\t See the syntax for ShiftXp ");
           System.err.println("********************************************************************************\n");

           usage();
        }   


        if (DEBUG) {   
            System.out.println("Options:");
            if(batchmode) 
                 System.out.println("\t - Input PDB File - Batch Mode: "+ inPDBFile);
            else System.out.println("\t - Input PDB File: "+ inPDBFile);
            System.out.println("\t - Output Format [Default:CSV]: "+ outputFormat);
            System.out.println("\t - Atom type [Default:ALL] : "+ atomtype);
            System.out.println("\t - pH [Default:5.0] : "+ df.format(pH));
            System.out.println("\t - Temperature [Default: 298]: "+ df.format(temperature));
            System.out.println("\t - Deuterate [Default: FALSE]: "+ deuteratedpdb);            
            System.out.println("\t - SHIFTX2 Directory [Default: current]: "+ SHIFTX2_DIR);            
        }    

        /*
         * initialized & load values
         */
        chkcshift.loadLimitedCShift();
        rcoil.loadRandomCoil();

        if ( ! rcoil_id.equals("1") ) rcoil.setRCoilID(rcoil_id);

        String dirname = "";
        String[] sp;

        if ( batchmode || inPDBFile.toUpperCase().equals("ALLPDB") ) {
            String pattern = "";

            if (batchmode) {
                // get File Information
                sp = inPDBFile.split("/");
                String fileRegExp = sp[sp.length-1];

                if (sp.length > 1) { 
                    for( int i=0; i < sp.length-1; i++) dirname += sp[i] + '/'; 
                } else {
                    dirname = "./";
                }
                
                pattern = fileRegExp.replaceAll("[.]pdb", "[.]pdb"); 
                pattern = pattern.replaceAll("\\*",".*");

            } else if (  inPDBFile.toUpperCase().equals("ALLPDB") )  { // ALLPDB case
                dirname = "./";
                pattern = ".*[.]pdb";  
            }

            if (batchmode)    
                System.out.println("\t> input pdb file:"+inPDBFile+"\n\t> dirname: " + dirname + "\n\t> pattern: " + pattern );

            File f = new File(dirname);
            if (DEBUG) System.out.println("Current Dir: " + f.getCanonicalPath());

            FilenameFilter filter = new FileListFilter(pattern);
            String[] fs = f.list(filter);

            // sort for rearrange file names
            java.util.Arrays.sort(fs);

            if ( fs != null && fs.length > 0 )  {
                if (DEBUG) System.out.println("# of pdb files: " + fs.length);
                                
                // LOAD Model objects Files into Memory
                predmodels.loadPredictionModels(atomtype);

                System.out.println("Calculating Chemical Shift .... ");
              
                for(String fname: fs)    {
                    doprocess ( dirname, fname, outputFormat, pH, temperature, deuteratedpdb, atomtype  ) ;
                }
            } else {
                System.err.println("[Error] Could not found the input pdb file in [" + f.getCanonicalPath() + "] directory");
            }            
            
        } else  {  // -i option: individual file
            sp = inPDBFile.split("/");
            String fname = sp[sp.length-1];

            File f = new File(inPDBFile);  
            if (! f.exists()) {
                System.err.println("\n************************************************************");
                System.err.println("Error (SHIFTX+ module): input PDB file ["+fname+"] does not exist");
                System.err.println("************************************************************\n\n");

                System.exit(1);
            }

            if (sp.length > 1) { 
                for( int i=0; i < sp.length-1; i++) dirname += sp[i] + '/'; 
            } else {
                dirname = "";
            }  
            // LOAD Model objects Files into Memory
            predmodels.loadPredictionModels(atomtype);
            System.out.println("Calculating Chemical Shift .... ");

            //calling chemical shifts prediction procedure
            doprocess ( dirname, fname, outputFormat, pH, temperature, deuteratedpdb, atomtype ) ;
        }
    }

    
    private static void doprocess(String dirname, String pdbfname, String outformat, double pH, double temp, Boolean deuterate, String atomtype ) throws Exception {
        // input dataset 

        // 7 Sep 2011
        // String pdbfname_surfix = pdbfname.replace(".pdb", "");
        String pdbfname_surfix = pdbfname;

        ModuleExecutor modexe = new ModuleExecutor(dirname, pdbfname);  // just generate feature files
        FeatureReader inds = new FeatureReader(rcoil, pH, temp);
        inds.readAllFiles(dirname + pdbfname_surfix); 


        // predicting chemical shifts by each residue & atom
        // also it saves output to a result file 
        if (DEBUG) 
            System.out.println("[dopredcs] in cs prediction value for each data ......");

        if (deuterate) {
            System.out.println("Deuteration Option: On");
        } 

        CSPredictionWekaModel cspred = new CSPredictionWekaModel(predmodels, inds, rcoil, chkcshift, outformat, atomtype);
        cspred.dopredcs(dirname, pdbfname_surfix, deuterate);

        // print out prediction results: select two output format (list & table)
        // Call File Save Function : Output2File(out_predicted_results);
        if (DEBUG)  System.out.println("Done: Predicted Chemical Shifts are saved to ["+ dirname + pdbfname_surfix + ".sxp]\n\n");
        
    }
    
}

// Definition File Filter 
// FilenameFilter filter = new FilenameFilter(pattern) {
class FileListFilter implements FilenameFilter {
    private String pattern;
    
    public FileListFilter(String pattern) { this.pattern = pattern; }
    
    public boolean accept(File directory, String filename) {
        return Pattern.matches(pattern, filename);
    }
};
    
