#!/usr/bin/env python
#######################################################################
# SHIFTX2 main program
# - accepts command line argument
# - call shiftx+ to predict cs based on structure
# - call shifty+ to predict cs based on sequence
# - combine prediciton results and store them in output files
#
# Author: Yifeng Liu
# August 4th, 2010
#
# Lastest Update: Aug 25, 2014 by Beomsoo Han
# Version: 1.09
#######################################################################
import os, sys, getopt, re, fnmatch, time
from optparse import OptionParser
from shiftx2_util import *

JAVAMEM = " -Xmx1900m " # java memory used by SHIFTX+

DEUTERATED = None
NOSHIFTY = None
SHIFTX1 = None
VERBOSE = None
OUTFORMAT = None
BATCH = None
CHAINID = None
EXPLICIT = None
SHIFTTYPE = None
NMRMC = None

SHIFTX2ROOT = ""

# temporary file name
TIMESTAMP = str(int(time.time())) # time stamp for the application
tempfolder = '/tmp/'

# shiftx+ interface
# java [-cp ./lib/weka.jar] shiftx2 [-atoms: ALL|BACKBONE|SIDECHAIN] [-f: CSV|BMRB|TABULAR] [-pH: 5.0] [-temp: 298] [-d: TRUE|FALSE]  -i <infile.pdb>

# shifty+ interface
# Usage: python shifty3.py <query.pdb> <output.shifty> <id_cutoff>

def chain_selector(pdbfile, chainid):
    ''' select only the given chain (identified by chainid) and print it into a temporary file XXXX.pdb.chain '''

    outlist = [] # list containing the output lines
    HASCHAIN = False

    input = open(pdbfile, 'r')
    lines = input.readlines()
    input.close()

    cyspat = "CYS " + chainid # the CYS $chain pattern
    for line in lines:
        line = line.rstrip()
        if ((line.startswith("SSBOND") and cyspat in line) \
                or ((line.startswith("HETATM") or line.startswith("ATOM")) and ((line[21] == chainid) or (chainid=='_')))
            or ((line.startswith("TER") and ((line[21] == chainid) or (chainid=='_'))))
            or line.startswith("END")):
            # print line
            outlist.append(line)
            if line.startswith("TER") or line.startswith("END"):
                break

    outstr = "\n".join(outlist)

    # check whether there are peptide chain the given PDB
    if len(outlist) == 1:
        errmsg = "\n\n Error: No matched chain is found in " + infile + ", abborting...\n\n"
        parser.error(errmsg)
    else:
        if not ("ATOM" in outstr):
            outstr = None
        return outstr

def replace_nomenclature(pdbfile, chainfile):
    ''' replae phospholated residue's strings (HETAT --> ATOM, SEP -> SER, PTR -> TYR, TPO -> THR) '''

    outlist = [] # list containing the output lines
    outlist_phosres = [] # list containing the phospholylated residues (Residue No, Amino Acid type)

    input = open(chainfile, 'r')
    lines = input.readlines()
    input.close()

    phosResNo = None
    for line in lines:
        line = line.rstrip()
        if (line.startswith("HETATM")):
            if (line.find("SEP") != -1):
                line = line.replace("HETATM","ATOM  ")
                line = line.replace("SEP","SER")
                str_phosres = line[23:26] + ",SEP"
            elif (line.find("PTR") != -1):
                line = line.replace("HETATM","ATOM  ")
                line = line.replace("PTR","TYR")
                str_phosres = line[23:26] + ",PTR"
            elif (line.find("TPO") != -1):
                line = line.replace("HETATM","ATOM  ")
                line = line.replace("TPO","THR")
                str_phosres = line[23:26] + ",TPO"
            else:
                str_phosres = None

            # keep simple: residue index no, residue type
            if str_phosres != None and phosResNo != line[23:26]:
                # print str_phosres + "\n"
                outlist_phosres.append(str_phosres)
                phosResNo = line[23:26]

        outlist.append(line)
        if line.startswith("TER") or line.startswith("END"):
            break

    outstr = "\n".join(outlist)
    outstr_phosres = "\n".join(outlist_phosres)

    if len(outlist_phosres) == 0:
        errmsg = "\n\n Error: No Phosphorylated Amino Acid (SEP, PTR, or TPO) is found in this chain of " + infile + ", abborting...\n\n"
        parser.error(errmsg)
    else:
        # print outlist_phosres
        outfile_phosres = chainfile + ".phos"
        tmpout = open(outfile_phosres, 'w')
        print >> tmpout, outstr_phosres
        tmpout.close()

    # check whether there are peptide chain the given PDB
    return outstr

def add_atom_format_option(combine_cmd):
    ''' append the appropriate option to the given command line '''

    if not OUTFORMAT or "CSV" in OUTFORMAT: # "CSV"
        combine_cmd = combine_cmd + " 1"
    elif "TABULAR" in OUTFORMAT:
        combine_cmd = combine_cmd + " 2"
    elif "BMRB" in OUTFORMAT:
        combine_cmd = combine_cmd + " 3"
    elif "NEF" in OUTFORMAT:
        combine_cmd = combine_cmd + " 4"

    if not SHIFTTYPE or "ALL" in SHIFTTYPE: # "ALL"
        combine_cmd = combine_cmd + " 1"
    elif "BACKBONE" in SHIFTTYPE:
        combine_cmd = combine_cmd + " 2"
    elif "SIDECHAIN" in SHIFTTYPE:
        combine_cmd = combine_cmd + " 3"

    return combine_cmd

######################################################################################################
# batch processing mode: predict all PDB files in the current directory
######################################################################################################
def batch_mode(pattern_str):
    ''' batch processing mode: predict PDB files matching regular expression in the current directory '''
    global JAVAMEM
    global tempfolder

    pdb_list = []
    tempfiles = []  ## to eliminate temporary files
    # tmplist = os.listdir(os.path.curdir)

    ## Add/Modify by B HAN
    ## use fnmatch package instead of re package
    pattern_split = os.path.split(pattern_str)
    if pattern_split[0] == '':
        dirpath = os.path.abspath(".")
    else:
        dirpath = pattern_split[0]
    pattern = pattern_split[1]

    for file in os.listdir(dirpath):
        if fnmatch.fnmatch(file, pattern):
            pdb_list.append(os.path.abspath(dirpath) +'/'+ file)

    len_pdb_list = len(pdb_list)

    ## check existance of PDB files (Add by B HAN)
    if len_pdb_list == 0 :
        errmsg = "\nError [Batch Mode]: Cound not find any PDB files \n\t in [" + dirpath + "] \n\t with pattern [" + pattern +"]"
        return errmsg
    else:
        print "\n## Starting Batch mode for %d PDBs ...\n" % len_pdb_list


    ## Switched by Above CODES
    # pstr = pattern_str
    # pstr = re.sub('\\.', '\\\\.', pstr)
    # pstr = re.sub('\\*', '.*', pstr)
    # pattern = re.compile(pstr)

    # for file in tmplist:
        # match = pattern.search(file)
        # switch 'if conditions'  by B HAN
        # if match and (file.endswith(".pdb") or file.endswith(".PDB")):
        # if match and file:
            # pdb_list.append(os.path.abspath(file))

    # run shiftx1 or shiftx+
    if not SHIFTX1:
        # run SHIFTX+
        print "## Running SHIFTX+ modules ..."  ## Add by B HAN

        ## switch os.path.abspath("") with SHIFTX2ROOT (set from sys.path[0]) by B HAN
        cmd_prefix = "java " + JAVAMEM + " -cp " + SHIFTX2ROOT + "/bin:" + SHIFTX2ROOT + \
            "/lib/weka.jar ShiftXp -b " + "'" +  BATCH + "'" + " -atoms " + options.atoms + \
            " -ph " + str(options.ph) + " -temp " + str(options.temp)
        if DEUTERATED:
            cmd_prefix = cmd_prefix + " -d TRUE"

        if SHIFTX2ROOT != os.getcwd() :
            cmd_prefix = cmd_prefix + " -dir " + SHIFTX2ROOT

        os.system(cmd_prefix)
        if VERBOSE:
            print cmd_prefix
    else:
        print "## Running SHIFTX (instead of SHIFTX+) modules ..."  ## Add by B HAN

        for infile in pdb_list:
            # run SHIFTX
            ## cmd_prefix = "modules/shiftx/shiftx 1 " + os.path.abspath("") + "/" + \
            cmd_prefix = SHIFTX2ROOT + "/modules/shiftx/shiftx 1 " + infile + " " + infile + ".sx"
            os.system(cmd_prefix)
            if VERBOSE:
                print cmd_prefix
            # convert from table format to CSV
            ## cmd_prefix = "python script/shiftx2shiftxp.py " + os.path.abspath("") + "/" + \
            ##    infile + ".sx " + os.path.abspath("") + "/" + infile + ".sxp"
            cmd_prefix = "python "+ SHIFTX2ROOT +"/script/shiftx2shiftxp.py " + \
                infile + ".sx " +  infile + ".sxp"
            ## switched by B HAN
            ## infile + ".sx " + os.path.abspath("") + "/" + infile.split('.')[0] + ".sxp"
            os.system(cmd_prefix)
            if VERBOSE:
                print cmd_prefix

    # run shifty
    if not NOSHIFTY:
        print "\n## Running SHIFTY+ modules ..."  ## Add by B HAN

    for infile in pdb_list:
        if not NOSHIFTY:
            ## by B HAN
            ## sys.path.append(os.path.abspath("./shifty3"))
            ## os.chdir(os.path.abspath("shifty3"))
            sys.path.append(SHIFTX2ROOT+"/shifty3")
            os.chdir(SHIFTX2ROOT+"/shifty3")
            shifty_cmd = "python shifty3.py -i " + infile + " -o " + infile + ".shifty -c 40 -t " + tempfolder
            if DEUTERATED:
                shifty_cmd = shifty_cmd + " -d"
            if VERBOSE:
                print shifty_cmd
            os.system(shifty_cmd)
            os.chdir(os.path.abspath(".."))
        else:
            os.chdir(SHIFTX2ROOT + "/shifty3")
            shifty_cmd = "touch " + infile + ".shifty"
            os.system(shifty_cmd)
            os.chdir(os.path.abspath(".."))

    # combine result
    print "\n## Combining both prediction results ..."  ## Add by B HAN

    for infile in pdb_list:
        ## switched by B HAN
        ## sx_outfile = infile.split('.')[0] + ".sxp"
        sx_outfile = infile + ".sxp"

        if not os.path.exists(sx_outfile):
            # errmsg = "Error: SHIFTX+ prediction file " + sx_outfile + " not found."
            errmsg = "Error: SHIFTX+ cannot make a prediction file."
            return errmsg

        combine_cmd = "python " + SHIFTX2ROOT +"/script/combine_cs.py " + sx_outfile + " " + infile + ".shifty "
        if not options.outfile:
            combine_cmd = combine_cmd + infile + ".cs"
        else:
            combine_cmd = combine_cmd + options.outfile
        if VERBOSE:
            print combine_cmd

        combine_cmd = add_atom_format_option(combine_cmd)
        os.system(combine_cmd)

    for infile in pdb_list:
        ## switched by B HAN, cause to support any file name
        ## tempfiles.append(infile.split('.')[0] + ".sxp")
        ## tempfiles.append(infile.split('.')[0] + ".shifty")
        tempfiles.append(os.path.join(tempfolder, infile + ".sxp"))
        tempfiles.append(os.path.join(tempfolder, infile + ".shifty"))

    cleanup_tempfiles(tempfiles)
    return "DONE for %d PDBs" % len_pdb_list

def model_batch(pattern_str):
    ''' batch processing multiple NMR models'''
    global JAVAMEM
    global tempfolder

    pdb_list = []
    tmplist = os.listdir(os.path.curdir)
    tmplist = tmplist + os.listdir(tempfolder)

    pstr = pattern_str
    pstr = re.sub('\\.', '\\\\.', pstr)
    pstr = re.sub('\\*', '.*', pstr)
    pattern = re.compile(pstr)

    for file in tmplist:
        match = pattern.search(file)
        ## switch 'if conditions'  by B HAN
        ## if match and (file.endswith(".pdb") or file.endswith(".PDB")):
        if match and file:
            pdb_list.append(file)

    # run shiftx1 or shiftx+
    if not SHIFTX1:
        # run SHIFTX+
        cmd_prefix = "java " + JAVAMEM + " -cp " + SHIFTX2ROOT + "/bin:" + SHIFTX2ROOT + "/lib/weka.jar ShiftXp -b " + \
            "'" +  pattern_str + "'" + " -atoms " + options.atoms + \
            " -ph " + str(options.ph) + " -temp " + str(options.temp)
        if DEUTERATED:
            cmd_prefix = cmd_prefix + " -d TRUE"

        if SHIFTX2ROOT != os.getcwd() :
            cmd_prefix = cmd_prefix + " -dir " + SHIFTX2ROOT

        os.system(cmd_prefix)
        if VERBOSE:
            print cmd_prefix
    else:
        for infile in pdb_list:
            # run SHIFTX
            cmd_prefix = SHIFTX2ROOT + "/modules/shiftx/shiftx 1 " + infile + \
                " " + infile + ".sx"
            os.system(cmd_prefix)
            if VERBOSE:
                print cmd_prefix

            # convert from table format to CSV
            cmd_prefix = "python "+ SHIFTX2ROOT +"/script/shiftx2shiftxp.py " + \
                infile + ".sx " + infile + ".sxp"

            os.system(cmd_prefix)
            if VERBOSE:
                print cmd_prefix

def nmr_multichain_mode(infile, outfile):
    ''' run shiftx2 in NMR mode with multichain option:
    1. the PDB input file is split into multiple models
    2. shiftx+ is run with each model with multichain option enalbed
    3. shiftx+ result is averaged
    4. shifty is run for one model of the single file
    5. shiftx+ and shifty results are combined
    6. (optional) plot the correlation coefficients '''
    global tempfolder
    print "\n## Running in NMR+Multi-chain MODE (It will produce the average shifts of all models on all chains)"

    tempfiles = []

    # the PDB input file is split into multiple models
    model_files = split_model(infile)
    infile = os.path.abspath(infile)

    infile_prefix = infile

    # change each model file suffix from .pdb to .sxp
    model_outfiles = []
    resnum_dict = None
    for f in model_files:
        ## modified by B HAN
        ## if f.endswith('.pdb') or f.endswith('.PDB'):
        if f:
            fout = os.path.abspath(f) + '.cf.sxp'
            fc = os.path.abspath(f) + '.cf' # shiftx2 chain file for a model file

            # convert each of the model file to a chain file
            # residue number is based on the first model
            if not resnum_dict:
                resnum_dict = merge_chains(f, fc)
            else:
                merge_chains(f, fc)

            model_outfiles.append(fout)
            ## tempfiles.append(f)
            tempfiles.append(os.path.abspath(f))
            tempfiles.append(fout)

    # run shiftx with batch mode and pattern f[0:-4] + '*.pdb'
    pattern_str = infile[0:-4] + '-m*.pdb.cf'
    model_batch(pattern_str)
    print "pattern:"+pattern_str

    # average model and print output file
    chainoutfile = infile + '.cf.sxp'
    outfile = options.infile + '.sxp'
    model_average(model_outfiles, chainoutfile)

    # run shifty
    if not NOSHIFTY:
        sys.path.append( SHIFTX2ROOT + "/shifty3")
        os.chdir(SHIFTX2ROOT + "/shifty3")
        shifty_cmd = "python shifty3.py -i " + infile[0:-4] + '-m1.pdb.cf' + \
            " -o " + infile + ".shifty -c 40 -t " + tempfolder
        if DEUTERATED:
            shifty_cmd = shifty_cmd + " -d"
        if VERBOSE:
            print shifty_cmd
        os.system(shifty_cmd)
        os.chdir(os.path.abspath(".."))
    else:
        os.chdir(SHIFTX2ROOT + "/shifty3")
        shifty_cmd = "touch " + infile + ".shifty"
        os.system(shifty_cmd)
        os.chdir(os.path.abspath(".."))

    # sx_outfile = infile_prefix + ".sxp"
    sx_outfile = infile + ".cf.sxp"
    if not os.path.exists(sx_outfile):
        # errmsg = "Error: [SHIFTX+] SHIFTX+ prediction file " + sx_outfile + " not found."
        errmsg = "Error: SHIFTX+ cannot make a prediction file."
        return errmsg

    # combine result
    combine_cmd = "python "+ SHIFTX2ROOT + "/script/combine_cs.py " + infile_prefix + ".cf.sxp " + infile + ".shifty "
    if not options.outfile:
        combine_cmd = combine_cmd + infile + ".cf.cs"
        ## add by B HAN; if it does not exist, error message will occur
        options.outfile = options.infile + ".cs"
    else:
        combine_cmd = combine_cmd + options.outfile
    if VERBOSE:
        print combine_cmd

    combine_cmd = add_atom_format_option(combine_cmd)
    os.system(combine_cmd)

    # recreating shiftx+, shifty3, and shiftx2 output with corrected residue number and chain IDs
    restore_chain_resnum(chainoutfile, options.infile + '.sxp', resnum_dict, "CSV") # recreate shiftx+ output
    restore_chain_resnum(infile+".shifty", options.infile + '.shifty', resnum_dict, "CSV") # recreate shifty3 output
    restore_chain_resnum(infile+".cf.cs", options.outfile, resnum_dict, "CSV") # recreate shiftx2 output

    # calculate pairwise correlation with given observation file
    # should be accomplished in a function
    if obs_cs!= None:
        # calculate correlation
        cmd = "python " + SHIFTX2ROOT +"/script/model_corr.py " + obs_cs + " " + options.infile + ".modelcorr " + ' '.join(model_outfiles)
        os.system(cmd)

        # plot correlation graphs, plot line graph but no barplots
        cmd = "Rscript " + SHIFTX2ROOT + "/script/nmr_plot.r " + options.infile + ".modelcorr " + options.infile + " NULL"
        os.system(cmd)

        tempfiles.append(os.path.join(tempfolder, infile_prefix + ".modelcorr"))

    # clean up temporary files
    # for f in model_files + model_outfiles:
    #    os.unlink(f)
    ## tempfiles.append(infile_prefix + ".sxp")

    tempfiles.append(options.infile + ".sxp")
    tempfiles.append(options.infile + ".shifty")
    cleanup_tempfiles(tempfiles)

    return "DONE: " + options.outfile


def nmr_mode(infile, obs_cs):
    ''' run shiftx2 in nmr mode:
    1. the PDB input file is split into multiple models
    2. shiftx+ is run with each model
    3. shiftx+ result is averaged
    4. shifty is run for one model of the single file
    5. shiftx+ and shifty results are combined
    6. (optional) plot the correlation coefficients '''

    global tempfolder
    global options
    print "\n## Running in NMR MODE (It will produce the average shifts of all models)"

    tempfiles = []

    # the PDB input file is split into multiple models
    model_files = split_model(infile)
    infile = os.path.abspath(infile)
    ## switched by B HAN
    ## infile_prefix = infile.split('.')[0]
    infile_prefix = infile

    # change each model file suffix from .pdb to .sxp
    model_outfiles = []
    for f in model_files:
        ## modified by B HAN
        ## if f.endswith('.pdb') or f.endswith('.PDB'):
        if f:
            ## switched by B HAN
            ## fout = f[0:-4] + '.sxp'
            #### fout = f + '.sxp'
            fout = os.path.abspath(f) + '.sxp'
            model_outfiles.append(fout)
            ## tempfiles.append(f)
            tempfiles.append(os.path.abspath(f))
            tempfiles.append(fout)

    # run shiftx with batch mode and pattern f[0:-4] + '*.pdb'

    pattern_str = infile[0:-4] + '-m*.pdb'
    model_batch(pattern_str)
    print "pattern:"+pattern_str

    # average model and print output file
    ## switched by B HAN
    ## outfile = infile[0:-4] + '.sxp'
    outfile = options.infile + '.sxp'
    model_average(model_outfiles, outfile)

    shifty_outfile = os.path.abspath(options.infile) + '.shifty'
    # run shifty
    if not NOSHIFTY:
        sys.path.append(SHIFTX2ROOT + "/shifty3")
        os.chdir(SHIFTX2ROOT + "/shifty3")
        shifty_cmd = "python shifty3.py -i " + infile + \
            " -o " + shifty_outfile + " -c 40 -t " + tempfolder
        if DEUTERATED:
            shifty_cmd = shifty_cmd + " -d"
        if VERBOSE:
            print shifty_cmd
        os.system(shifty_cmd)
        os.chdir(os.path.abspath(".."))
    else:
        os.chdir(SHIFTX2ROOT + "/shifty3")
        shifty_cmd = "touch " + shifty_outfile
        os.system(shifty_cmd)
        os.chdir(os.path.abspath(".."))

    sx_outfile = options.infile + ".sxp"
    if not os.path.exists(sx_outfile):
        # errmsg = "Error: [SHIFTX+] SHIFTX+ prediction file " + sx_outfile + " not found."
        errmsg = "Error: SHIFTX+ cannot make a prediction file."
        return errmsg

    # combine result
    combine_cmd = "python "+ SHIFTX2ROOT + "/script/combine_cs.py " + options.infile + ".sxp " + options.infile + ".shifty "
    if not options.outfile:
        combine_cmd = combine_cmd + options.infile + ".cs"
        ## add by B HAN; if it does not exist, error message will occur
        options.outfile = options.infile + ".cs"
    else:
        combine_cmd = combine_cmd + options.outfile
    if VERBOSE:
        print combine_cmd

    combine_cmd = add_atom_format_option(combine_cmd)
    os.system(combine_cmd)

    # calculate pairwise correlation with given observation file
    # should be accomplished in a function
    if obs_cs!= None:
        # calculate correlation
        cmd = "python " + SHIFTX2ROOT +"/script/model_corr.py " + obs_cs + " " + options.infile + ".modelcorr " + ' '.join(model_outfiles)
        os.system(cmd)

        # plot correlation graphs, plot line graph but no barplots
        cmd = "Rscript " + SHIFTX2ROOT + "/script/nmr_plot.r " + options.infile + ".modelcorr " + options.infile + " NULL"
        os.system(cmd)

        # tempfiles.append(os.path.join(tempfolder, infile_prefix + ".modelcorr"))

    # clean up temporary files
    # for f in model_files + model_outfiles:
    #    os.unlink(f)
    ## tempfiles.append(infile_prefix + ".sxp")
    if not EXPLICIT:
        tempfiles.append(options.infile + ".sxp")
        tempfiles.append(options.infile + ".shifty")

    cleanup_tempfiles(tempfiles)
    return "DONE: " + options.outfile

######################################################################################################
#  Multi-chain mode: predict multi-chain PDB file as a single long chain
#  while reserving the original residue number
######################################################################################################
def multichain_mode(infile, outfile):
    # first merge all chains in a PDB and print out into a new PDB file as new input file
    global tempfolder
    chainfile = SHIFTX2ROOT + "/TEMP.pdb"
    chainoutfile = SHIFTX2ROOT + "/TEMP.pdb.cs"
    resnum_dict = merge_chains(infile, chainfile)

    # then proceed as single mode using the chain file
    single_mode(chainfile, chainoutfile)

    ## append by B HAN
    if outfile == None:
        outfile = infile + ".cs"

    # finally restore the residue numbers
    # print out prediction with chain IDs.
    if not OUTFORMAT or OUTFORMAT == "CSV":
        restore_chain_resnum(chainoutfile, outfile, resnum_dict, "CSV")
    elif OUTFORMAT == "TABULAR":
        restore_chain_resnum(chainoutfile, outfile, resnum_dict, "TAB")
    elif OUTFORMAT == "BMRB":
        restore_chain_resnum(chainoutfile, outfile, resnum_dict, "BMRB")

    # clean up temp files
    if EXPLICIT:
       print "\n# Temporary files (TEMP.*) for multi-chain are reserved"
    cleanup_tempfiles(tempfolder, ["TEMP.pdb", "TEMP.pdb.cs"])
    outmsg = "DONE: " + outfile
    return outmsg

######################################################################################################
#  Single file mode: predict only the given PDB file
######################################################################################################
def single_mode(infile, outfile):
    ''' single file mode: predict only the given PDB file '''
    global tempfolder
    # temporary file names
    pdbfile = infile
    infile = os.path.abspath(infile)
    tempfiles = []
    # chainfile = infile[0:-4] + ".chainselected" # a pdb file containing only the selected chain
    ## change temporary file name with adding '.chainselected' by B HAN
    chainfile = infile + ".cf" # a pdb file containing only the selected chain
    sx1_outfile = infile + ".sx" # shiftx1 output file
    ## switched by B HAN
    ## sxp_outfile = infile.split('.')[0] + ".sxp" # shiftxp output file
    sxp_outfile = infile + ".sxp" # shiftxp output file
    sy_outfile = infile + ".shifty" # shifty
    if outfile == None:
        outfile = infile + ".cs"
    inputfile = infile

    # print out the chain into a temp file
    if CHAINID: # if the user specifies a chain ID
        inputfile = chainfile
        sxp_outfile = inputfile + ".sxp" ## reset shiftxp output file to support full name (by B HAN)
        chainstr = chain_selector(pdbfile, CHAINID)
        if chainstr==None: # no such chain
            errmsg = "Error: No chain '" + CHAINID + "' found in " + infile + ", abborting..."
            return errmsg
        tmpout = open(chainfile, 'w')
        print >> tmpout, chainstr
        tmpout.close()

        if PHOSPHORYLATED: # if chain is selected and phosphorylated option is set
            # print "Running Phosphorylated Procedures - call replace_nomenclature"
            chainstr = replace_nomenclature(pdbfile, chainfile)

            tmpout = open(chainfile, 'w')
            print >> tmpout, chainstr
            tmpout.close()


    if not SHIFTX1:
        # run SHIFTX+
        cmd_prefix = "java " + JAVAMEM + " -cp " + SHIFTX2ROOT + "/bin:" + SHIFTX2ROOT + "/lib/weka.jar ShiftXp -i " + inputfile \
                   + " -atoms " + options.atoms + " -ph " + str(options.ph) + " -temp " + str(options.temp)
        if DEUTERATED:
            cmd_prefix = cmd_prefix + " -d TRUE"

        if SHIFTX2ROOT != os.getcwd() :
            cmd_prefix = cmd_prefix + " -dir " + SHIFTX2ROOT

        try:
            if VERBOSE:
                print cmd_prefix

            retcode = os.system(cmd_prefix)
            if retcode != 0:
                raise
        except:
            print "[SHIFTX+] Error while running SHIFTX+"
            sys.exit(-1)
    else:
        # run SHIFTX
        cmd_prefix = SHIFTX2ROOT + "/modules/shiftx/shiftx 1 " + inputfile + " " + sx1_outfile
        os.system(cmd_prefix)
        if VERBOSE:
            print cmd_prefix
        # convert from table format to CSV
        cmd_prefix = "python " + SHIFTX2ROOT + "/script/shiftx2shiftxp.py " + sx1_outfile + "  " + sxp_outfile
        os.system(cmd_prefix)
        if VERBOSE:
            print cmd_prefix

    # run shifty
    if not NOSHIFTY:
        sys.path.append(SHIFTX2ROOT + "/shifty3")
        os.chdir(SHIFTX2ROOT +"/shifty3")
        shifty_cmd = "python shifty3.py -i " + inputfile + " -o " + sy_outfile + " -c 40 -t " + tempfolder
        if DEUTERATED:
            shifty_cmd = shifty_cmd + " -d"
        if VERBOSE:
            print shifty_cmd
        os.system(shifty_cmd)
        #if errnum!=0:
            #errmsg = "Error: [SHIFTY+] execution halted due to unexpected errors."
            #return errmsg
        os.chdir(os.path.abspath(".."))
    else:
        os.chdir( SHIFTX2ROOT + "/shifty3")
        shifty_cmd = "touch " + sy_outfile
        os.system(shifty_cmd)
        os.chdir(os.path.abspath(".."))

    # check result file existence:
    if not (os.path.exists(sxp_outfile)):
        # errmsg = "Error: [SHIFTX+] SHIFTX+ prediction file " + sxp_outfile + " not found."
        errmsg = "Error: SHIFTX+ cannot make a prediction file."
        return errmsg

    # combine result
    combine_cmd = "python script/combine_cs.py " + sxp_outfile + " " + sy_outfile + " "
    combine_cmd = combine_cmd + outfile # specify output file name

    combine_cmd = add_atom_format_option(combine_cmd)
    if VERBOSE:
        print combine_cmd

    os.system(combine_cmd)

    # clean up temporary files:
    tempfiles.append(sxp_outfile)
    tempfiles.append(sy_outfile)
    tempfiles.append(chainfile)

    cleanup_tempfiles(tempfiles)
    outmsg = "DONE: " + outfile
    return outmsg # everything is alright.

def cleanup_tempfiles(files):
    ''' remove given files from current directory '''
    global EXPLICIT, tempfolder
    tmpsuffix = ['.cf', '.cf.sxp', '.pdb', '.cf.cs', '.shifty', '.fasta', '.blastout', '.option', '.xalignin', '.xalignout', '.rbo', 'parms', '.tmp', 'xalign_x']
    if not EXPLICIT:
        for f in files:
            if os.path.exists(f):
                os.unlink(os.path.abspath(f))
        # clean additional files in tempfolder
        tmpfiles = os.listdir(tempfolder)
        for f in tmpfiles:
            for suffix in tmpsuffix:
                if f.endswith(suffix):
                    os.unlink(os.path.join(tempfolder, f))
        # removing the temporary folder
        if os.listdir(tempfolder)!=[] and VERBOSE:
            print os.listdir(tempfolder)
        else:
            os.rmdir(os.path.abspath(tempfolder))
    else:
        pass # keep everything in the tempfolder
    return None

# combine script interface
if __name__ == '__main__':
    print "## SHIFTX2 Ver 1.09A (Released Aug 21, 2015) ##"

    SHIFTX2ROOT = sys.path[0]

    #if len(sys.argv)<3:
    #    print "Usage: python shiftx2_main.py [input.pdb] [-atoms: all]"
    #    sys.exit(-1)

    # http://docs.python.org/library/optparse.html#module-optparse
    usage = "usage %prog -i infile.pdb [options]"
    parser = OptionParser(usage)

    parser.add_option("-i", "--infile", dest="infile", help="Input file")
    parser.add_option("-o", "--outfile", dest="outfile", help="Output file")
    parser.add_option("-c", "--chain", dest="chainid", \
                          help="the chain ID to predict, by defaut it is the first chain to be predicted")
    parser.add_option("-f", "--outformat", dest="outformat", default="CSV", \
                          help="[CSV|BMRB|TABULAR|NEF]")
    parser.add_option("-a", "--atoms", dest="atoms", default="ALL", \
                          help="[ALL|BACKBONE|SIDECHAIN]")
    parser.add_option("-p", "--ph", dest="ph", default=5.0, \
                          help="PH value, default PH=5.0")
    parser.add_option("-t", "--temperature", dest="temp", default=298.0, \
                          help="Temperature, default Temp=298K")
    parser.add_option("-b", "--batch", dest="batch", \
                          help="run shiftx2 in batch mode for PDBs matching the given regular expression. For example: -b '*.pdb' predicts all PDB files in the current directory (chain selection is disabled in batch mode)")
    parser.add_option("-m", "--nmr", dest="nmr", action="store_true", \
                          help="run shiftx2 in NMR mode to calculate the averaged shifts for multiple models")
    parser.add_option("-g", "--graph", dest="obscs", \
                          help="given an observed chemical shift file in BMRB format, generate correlation graphs for multiple NMR model predictions. this option can only be used in NMR mode.")
    parser.add_option("-u", "--multichain", dest="multichain", action="store_true", \
                          help="toggle option to predict multi-chain PDB as a single long chain. SHIFTX2 will concatenate all chains in a single input PDB file and make prediction for the concatenated chain.")
    parser.add_option("-d", "--deuterated", dest="deuterated", action="store_true", \
                          help="toggle option for deuterated proteins")
    parser.add_option("-v", "--verbose", dest="verbose", action="store_true", \
                          help="toggle option to be verbose and print debug output messages")
    parser.add_option("-e", "--explicit", dest="explicit", action="store_true", \
                          help="toggle option to keep intermediate SHIFTX+ (.sxp files) and SHIFTY+ (.shifty files) results")
    parser.add_option("-x", "--shiftx1", dest="shiftx1", action="store_true", \
                          help="toggle option to run SHIFTX 1.0 instead of SHIFTX+")
    parser.add_option("-n", "--noshifty", dest="noshifty", action="store_true", \
                        help="toggle option to exclude SHIFTY+ results")
    parser.add_option("-z", "--temp", dest="tempfolder", default="/tmp/", \
                        help="Specify which folder to store temporary files")
    parser.add_option("-r", "--nmr-mc", dest="nmrmc", action="store_true",\
                        help="run shiftx2 in NMR-Multichain mode for PDB with multiple models, each of which contains multiple chains.")
    parser.add_option("-k", "--phosphorylated", dest="phosphorylated", action="store_true", \
                          help="toggle option to calculate phosphorylated amino acids's chemical shifts")

    (options, args) = parser.parse_args()

    if (not options.batch) and (not options.infile):
        parser.error("no input pdb file specified. use -h option for help messages.\n")

    # creating the temporary folder; related with -z option (default is /tmp/)
    if options.tempfolder:
        if not os.path.exists(options.tempfolder):
            parser.error("the specified temporary folder does not exist. \n\tMake a folder or set the path with '-z' option\n\n")
        elif os.path.abspath(options.tempfolder)==os.path.abspath('.'):
            parser.error("the specified temporary folder cannot be the same as the current folder")
        else:
            tempfolder = os.path.abspath(options.tempfolder)
    else:
        tempfolder = '/tmp/'
    tempfolder = tempfolder + '/' + TIMESTAMP + '/'
    os.mkdir(tempfolder) # creating the temporary folder for temp files

    cpattern = re.compile('[A-Z]')

    infile = options.infile
    DEUTERATED = options.deuterated
    NOSHIFTY = options.noshifty
    SHIFTX1 = options.shiftx1
    VERBOSE = options.verbose
    OUTFORMAT = options.outformat
    BATCH = options.batch
    outfile = options.outfile
    NMR = options.nmr
    EXPLICIT = options.explicit
    obs_cs = options.obscs # observe chemical shift file in BMRB format
    SHIFTTYPE = options.atoms
    MULTICHAIN = options.multichain # predict PDB as a single long chain while reserving the residue numbers.
    NMRMC = options.nmrmc # toggle on to run shiftx2 in multichain NMR mode
    PHOSPHORYLATED = options.phosphorylated # toggle on to calculate for the phosphorylated residues

    # copy input file to the temp folder
    if ( options.infile ):
        cmd = 'cp %s %s' % (infile, tempfolder)
        os.system(cmd)
        tempinfile = tempfolder + os.path.basename(infile)

    # grep the chain id from the user input
    CHAINID = options.chainid

    if CHAINID:
        chainid = CHAINID.upper()[0]
        # chain ID can be lower or upper case letters, digits or underscore
        cpattern = re.compile('[A-Za-z0-9]|_')
        if not cpattern.search(chainid):
            print "[SHIFTX2] Error: improper chain ID", chainid, " the first chain will be used"
            CHAINID = None
        CHAINID = chainid
    else:
        if PHOSPHORYLATED:
            parser.error("To calculate the phosphorylated residues,\n a Chain ID should be selected with chain ID option (-c or --chain)\n")

    # try:
    if NMR:
        errmsg = nmr_mode(tempinfile, obs_cs)
    elif BATCH:
        errmsg = batch_mode(options.batch)
    elif MULTICHAIN:
        errmsg = multichain_mode(infile, outfile)
    elif NMRMC:
        errmsg = nmr_multichain_mode(tempinfile, obs_cs)
    else:
        errmsg = single_mode(infile, outfile)
    #except:
    #    errmsg = "[SHIFTX2] Error: execution halted due to unexpected errors."

    # print "\n", errmsg, "\n"
