#!/usr/bin/env python
# Python script
#
#
#     Copyright (C) 2014 --- 2019 Fei Long,  Murshudov
#
#     This code is distributed under the terms and conditions of the
#     CCP4 Program Suite Licence Agreement as a CCP4 Library.
#
#====================================================================
## The date of last modification: 11/07/2014

import os,os.path,sys
import glob,shutil
import re,string
from optparse import OptionParser 
import time
import math
import select
if os.name != 'nt':
    import fcntl
import signal

def listComp(a_list, b_list):

    if a_list[1] > b_list[1]:
        return 1
    elif a_list[1] < b_list[1]:
        return -1

    if len(a_list[0]) < len(b_list[0]):
        return 1
    elif len(a_list[0]) > len(b_list[0]):
        return -1

    if a_list[0] > b_list[0]:
        return 1
    elif a_list[0] == b_list[0]:
        return 0
    elif a_list[0] < b_list[0]:
        return -1


#################################################   

class CExeCode :
    """ A generic abstract base class that is to be inheritted by other classes that warp 
        a executable code. Basically this class defines two main methods that characterize the
        procedures a job by an executable code, i.e.  forming a command line object and then
        run the code according to the command line object"""

    def __init__( self ):

        # set pathes
 
        self.exitCode = 0

        # test 
        self._setCmdLineAndFile()

    def _setCmdLineAndFile(self ):         # will be overrided by individual derived classes
        self._cmdline = 'ls '
        self.batch_name = None
        self._log_name = None
            
    def subExecute(self, mode = 0, err_str ="" ):

        if self._log_name :
            if os.name == 'nt':
                self.logModeWin(mode, err_str)
            else: 
                self.logModeLin(mode, err_str)
        else :
            self.interactiveMode()

    def logModeLin(self, mode = 0, err_str=""):
        """ execute self._cmdline and output to a log file (self._log_name)  
        in a nonblocking way"""

        logfile = open(self._log_name, 'w')
        logfile.write("\n============ PROCESS INFORMATION =============\n")

        # spawn a sub-process for a job and connect to its input/output(and error)
        # streams using pipes

        try : 
            import subprocess
            subProcess = subprocess.Popen(self._cmdline, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True)
            subProcess.stdin.close()
            outfile_sub  = subProcess.stdout
            outfd_sub    = outfile_sub.fileno()
        except ImportError:
            print "module 'subprocess' has not be found, you use a version of PYTHON below 2.4"
            print "Import module 'popen2' instead, the calculations will not be affected"
            import popen2
            subProcess = popen2.Popen4(self._cmdline)
            subProcess.tochild.close()
            outfile_sub  = subProcess.fromchild
            outfd_sub    = outfile_sub.fileno()
    
        self._pid  = subProcess.pid
        pid_c      = self._pid  + 1
        p_name = os.path.basename(self._cmdline.strip().split()[0])
        t__log_name = os.path.basename(self._log_name)
        
       
        log_size = 0 
        log_max  = 100000000
        outfile_eof = 0
        if not mode:
            self._nonBlockingFile(outfd_sub)
            while not outfile_eof :
                ready_to_read, ready_to_write, in_error = \
                     select.select([outfd_sub],[],[],60.0)
                if outfd_sub in ready_to_read:
                    out_quantum = outfile_sub.read()
                    out_quantum_bt = len(out_quantum)
                    log_size = log_size + out_quantum_bt
                    if log_size > log_max:
                        logfile.write("\nTHE SIZE OF LOG FILE, %s, EXCEEDED THE LIMIT AND PROCESS STOPPED\n"%t__log_name)
                        logfile.write("The process running %s is killed\n"%p_name)
                        logfile.close()
                        os.kill(self._pid, signal.SIGKILL)
                        os.kill(pid_c, signal.SIGKILL)
                        print "The process running %s is killed " %p_name
                        time.sleep(1.0)
                        # be safe stop all process
                        sys.exit(1)
                    else :
                        if out_quantum == '':
                            outfile_eof = 1
                        logfile.write(out_quantum)
                        logfile.flush()
        else :
            self._nonBlockingFile(outfd_sub)
            while not outfile_eof :
                ready_to_read, ready_to_write, in_error = \
                     select.select([outfd_sub],[],[],10)
                if outfd_sub in ready_to_read:
                    out_quantum = outfile_sub.read()
                    if out_quantum == '':
                        outfile_eof = 1
                    logfile.write(out_quantum)
                    logfile.flush()
                    # allProcInfo.append(out_quantum)

        self.exitCode = subProcess.wait()
      
        # if mode:
        #    for item in allProcInfo:
        #        logfile.write(item)

        logfile.write("=========END OF PROCESS INFORMATION\n ==========\n")
        logfile.close()

        outfile_sub.close()

        if self.exitCode:   
            print "#-----------------------------------------------------------#"
            print "The process stoped because of a runtime error in code '%s'!"%p_name
            if err_str:
                print "%s"%err_str
            else:
                print "See the associated log file '%s'."%t__log_name
            print "#-----------------------------------------------------------#"
            if not err_str:
                # if normal runtime errors, stop the program(using log. no err_str)  
                sys.exit(1)
                
        return True

    def logModeWin(self, mode = 0, err_str=""):
        """ execute self._cmdline and output to a log file (self._log_name)  
        under Windows system"""

        pass

    
    def _nonBlockingFile(self,fd):
        f_flag = fcntl.fcntl(fd, fcntl.F_GETFL,0)
        fcntl.fcntl(fd, fcntl.F_SETFL, f_flag | os.O_NONBLOCK)

    def interactiveMode(self):
        """ execute self._cmdline  just like what is done in a shell """
        
        os.system(self._cmdline)


# end


class Acedrg(CExeCode ):

    def __init__( self, t_argvs):

        if len(sys.argv)==1:
            print "Look for help: %s -h "%(os.path.basename(sys.argv[0]))

        self.errMessage       = []
        self.errLevel         = 0

        self.scrDir           = ""
        self.outRoot          = ""
        self.baseRoot         = ""

        self.geneInFileName   = ""
        self.geneInFileType   = ""
        self.inStdCifName     = ""
        self.inStdCifDir      = ""
        self.inMmCifName      = ""
        self.inSmiName        = ""
        self.inMdlName        = ""
        self.inPdbName        = ""
        self.inSDFName        = ""
        self.inMol2Name       = ""
        self.outRstCifName    = ""
        self.outRstPdb        = ""
        self.outAtmTypeName   = ""
        self.monomRoot        = ""

        self.acedrgTables     = ""
        self.libmol           = ""
        self.libmolLogName    = ""
        self.libcheck         = ""
        self.libcheckLogName  = ""
        self.refmac           = ""
        self.refmacLogName    = ""

        self.workMode         = 0

        self.molGen           = False
        self.repCrds          = False

        self.testMode         = False

        self.versionInfo       = {}
        self.allBondsAndAngles = {}

        self.allBondsAndAngles["atomClasses"] = {}
        self.allBondsAndAngles["bonds"]       = {}
        self.allBondsAndAngles["angles"]      = {}

        self.acedrg = os.path.abspath(sys.argv[0])
        self.acedrgDir = os.path.dirname(os.path.dirname(self.acedrg))
        #print self.acedrgDir
        inputOptionsP         = self.InputParser(t_argvs) 

        #if inputOptionsP.geneInFileName:
        #    self.checInputFormat()          
          
        self.setWorkMode(inputOptionsP)
        self.checkDependency()
        self.checkVersionInfo()
        self.execute()  
        
    def InputParser(self, t_argvs):

        usage = "Serveral Examples: \n\
        (1) Input a mmCif file to get a ligand/Dictionary/restraint files (mmCif fomat) \n\
            acedrg -c your_mmCif_file -r your_monomer_name(optional) -o output_name_root(optional) \n\n\
        (2) Input a SMILES string in a command line (the SMILES string is a pair of quotation marks) or \n \
            a file containing a SMILES string  to get a ligand/Dictionary/restraint files (mmCif fomat) \n\
            acedrg -i your_SMILES_file -r your_monomer_name(optional) -o output_name_root(optional) \n\n\
        (3) Input a MOL file to get a ligand/Dictionary/restraint files (mmCif fomat) \n\
            acedrg -m your_mol_file -r your_monomer_name(optional) -o output_name_root(optional) \n\n\
        (4) Input a small molecule CIF file to get two files describing your molecules, atom types, \n\
            bond lengths and angles (text format)\n\
            acedrg -e -b your_CIF_file -r your_monomer_name(optional) -o output_name_root(optional) \n\n\
         5) Input a directory containing a number of  molecule CIF files to get files describing your\n\
            molecules, atom types, bond lengths and angles, and their statistics \n\
            acedrg -e -d your_dir_of_CIF_files -o output_name_root(optional) \n\n"

        self.inputParser = OptionParser(usage=usage)

        # Options 
        # input file format 
        self.inputParser.add_option("-b",  "--stdcif", dest="inStdCifName", metavar="FILE", 
                                    action="store", type="string", 
                                    help="Input small molecule CIF File containing coordinates and crystal information")

        self.inputParser.add_option("-c",  "--mmcif", dest="inMmCifName", metavar="FILE", 
                                    action="store", type="string", help="Input MMCIF File containing coordinates and bonds")

        self.inputParser.add_option("-d",  "--cifdir", dest="inStdCifDir", metavar="FILE", 
                                    action="store", type="string", 
                                    help="An input directory to store a group of small molecule CIF Files containing coordinates and crystal information")

        self.inputParser.add_option("-e",  "--molgen", dest="molGen",  
                                    action="store_true",  default=False,  
                                    help="The option when the user want to generate molecules and values of the associated bonds and angles in a cif file")

        self.inputParser.add_option("-f",  "--infile", dest="geneInFileName",  
                                    action="store",  type="string",   
                                    help="user's input file, regardless what format it is")

        self.inputParser.add_option("-g",  "--mol2", dest="inMol2Name", metavar="FILE", 
                                    action="store", type="string", help="Input SYBL_MOL2 File containing coordinates and bonds")

        self.inputParser.add_option("-i",  "--smi", dest="inSmiName", metavar="FILE", 
                                    action="store", type="string", 
                                    help="Input  File containing a SMILE string")

        self.inputParser.add_option("-m",  "--mol", dest="inMdlName", metavar="FILE", 
                                    action="store", type="string", 
                                    help="Input File of MOL format containing coordinates and bonds")

        self.inputParser.add_option("-n",  "--typeOut", dest="typeOut",  
                                    action="store_true",  default=False,  
                                    help="The option when the user want to output two kinds of atom types (CCP4 and Acedrg) only")

        self.inputParser.add_option("-o",  "--out", dest="outRoot", 
                                    action="store", type="string", 
                                    help="A name root that users want their output files called(without extension)")

        self.inputParser.add_option("-p",  "--pdb", dest="inPdbName", metavar="FILE", 
                                    action="store", type="string", 
                                    help="Input File of PDB format containing atomic coordinates ")

        self.inputParser.add_option("-r",  "--res", dest="monomRoot",  
                                    action="store", type="string", 
                                    help="The name of the chemical components users want to put into output files(e.g. PDB or MMCIF)")

        self.inputParser.add_option("-s",  "--sdf", dest="inSdfName", metavar="FILE", 
                                    action="store", type="string", 
                                    help="Input File of SDF format containing coordinates and bonds")

        self.inputParser.add_option("-t",  "--tab", dest="acedrgTables", metavar="FILE", 
                                    action="store", type="string", 
                                    help="Input path that stores all bond and angle tables (if no input, default CCP4 location will be used)")

        self.inputParser.add_option("-v",  "--ver", dest="versionInfo",  
                                    action="store_true",  default=False,
                                    help="The option for checking version information of acedrg")

        self.inputParser.add_option("-y", "--repcrd",
                  action="store_true", dest="repCrd", default=False,
                  help="Use this keyword if you want to replace the atomic coordinates in the input mmCif with those in the input PDB")

        self.inputParser.add_option("-z",  "--noGeoOpt", dest="noGeoOpt",  
                                    action="store_true",  default=False,
                                    help="The option for not doing geometry optimization on coordinates")

        self.inputParser.add_option("-T",  "--Test", dest="testMode",  
                                    action="store_true",  default=False,  
                                    help="The option for internal tests")

        (inputOptionsP, inputOptionsU) = self.inputParser.parse_args(t_argvs)

        if inputOptionsU:    
            print "Line arguments for those keywords are missing "
            for a_opt in inputOptionsU:
                print "Keyword %s ?"%a_opt
            sys.exit()

        return inputOptionsP 

    def checkDependency(self):
     
        if os.environ.has_key("CCP4"):
            tRefmac = os.path.join(os.environ['CBIN'], "refmac5")
            if not glob.glob(tRefmac):
                print "refmac5 could not be found"
                sys.exit()
            else:
                self.refmac = tRefmac

            tLibcheck = os.path.join(os.environ['CBIN'], "libcheck")
            if not glob.glob(tLibcheck):
                print "libcheck could not be found"
                sys.exit()
            else:
                self.libcheck = tLibcheck

            tLibmol = os.path.join(os.environ['CCP4'], "libexec", "libmol")
            if glob.glob(tLibmol):
                self.libmol = tLibmol

            tAcedrgTables = os.path.join(os.environ['CCP4'], "share/acedrg/tables")
            if glob.glob(tAcedrgTables):
                self.acedrgTables = tAcedrgTables
        else :
            print "You need to install CCP4 suite"
            print "or activate ccp4.setup"
            sys.exit()

        if not self.libmol :
            if os.environ.has_key("LIBMOL_ROOT"):
                tLibmol = os.path.join(os.environ['LIBMOL_ROOT'], "libexec/libmol")
                if glob.glob(tLibmol):
                    self.libmol = tLibmol
                else:
                    print "libmol could not be found at %s"%tLibmol
                    sys.exit()

        if not self.libmol: 
            if glob.glob(self.acedrgDir):
               tLibmol = os.path.join(self.acedrgDir, "libexec/libmol")
               if glob.glob(tLibmol):
                   self.libmol = tLibmol
               else:
                   print "libmol could not be found at %s"%tLibmol
                   sys.exit()
            else:
                print "Can not find where the acedrg is installed"
                sys.exit()

        
        if not self.libmol: 
            print "can not find libmol at libexec/"
            sys.exit()
                                

        if not self.acedrgTables:
            if os.environ.has_key("LIBMOL_ROOT"):
                tAcedrgTables = os.path.join(os.environ['LIBMOL_ROOT'], "share/acedrg/tables")
                if os.path.isdir(tAcedrgTables):
                    self.acedrgTables = tAcedrgTables
                else:
                    print "Tables required could not be found at %s"%tAcedrgTables
                    sys.exit()
                                
        if not self.acedrgTables: 
            tAcedrgTables = os.path.join(self.acedrgDir, "share/acedrg/tables")
            # print tAcedrgTables
            if os.path.isdir(tAcedrgTables):
                self.acedrgTables = tAcedrgTables
            else:
                print "Bond and angle tables required could not be found "
                sys.exit()

         
    def checkVersionInfo(self):
   
        self.versionInfo["man"] = os.path.join(self.acedrgTables, "manifest.txt")
        if not os.path.isfile(self.versionInfo["man"]):
            print "Version infomation is not available."
        else:
            # print self.versionInfo["man"]
            try:
                vInfo = open(self.versionInfo["man"], "r")
            except IOError:
                print self.versionInfo["man"], " Could not be opened for reading"
                print "Version infomation is not available."
            else:
                for aL in vInfo.readlines():
                    if aL.find(":") !=-1:
                        strs = aL.strip().split(":")
                        if len(strs)==2:
                            self.versionInfo[strs[0]] = strs[1]      
             
    def checInputFormat(self):
        
        try:
            tF =open(self.geneInFileName, "r")
        except IOError:
            print "%s can not be open for reading "%tFName
            sys.exit()
        else:
            
            tLines = tF.readlines()
            tF.close()
            for aL in tLines:
                strs = aL.strip().split()
                if aL.find("_chem_comp_atom") != -1: 
                    self.inFileType = "MMCIF"
                    break     
                elif aL.find("_atom_site_") != -1:
                    self.geneInName = "CIF" 
                    break
                elif len(strs)==8:
                     if strs[7].find("V2000") != -1 or strs[7].find("V3000") != -1 :
                         self.geneInName = "MOL" 
                         break
                
        
    def setMonoRoot(self):
        
        if self.inMdlName != "":
            try:
                tF =open(self.inMdlName, "r")
            except IOError:
                print "%s can not be open for reading "%self.inMdlName
                sys.exit()
            else:
                tL = tF.readline().strip()
                print tL
                tF.close()
                if len(tL) ==3:
                    self.monomRoot= tL
                elif len(tL) >3:
                    self.monomRoot= tL[:4]
                
        elif self.inMmCifName != "":
            try:
                tF =open(self.inMmCifName, "r")
            except IOError:
                print "%s can not be open for reading "%self.inMmCifName
                sys.exit()
            else:
                tLs = tF.readlines()
                tF.close()
                for aL in tLs:
                    if aL.find("data_comp_") != -1:
                        strs = aL.strip().split("_") 
                        if len(strs) ==3:
                            if len(strs[2]) ==3:
                                self.monomRoot = strs[2]
                                break
        if len(self.monomRoot) < 3:
            self.monomRoot = "UNL"

    def setWorkMode(self, t_inputOptionsP = None):

        # Sequnence for check the locations of acedrg tables 
        # (1) Check if the user provides the location
        # (2) If not, check CCP4 suite default location.
        # (3) If not, check if the environment variable LIBMOL_ROOT is defined in the user's machine.
        # (4) If none of them, program exits.   
        if not t_inputOptionsP.acedrgTables: 
            if os.environ.has_key("CCP4"):
                tAcedrgTables = os.path.join(os.environ['CCP4'], "share/acedrg/tables")
                if glob.glob(tAcedrgTables):
                    self.acedrgTables = tAcedrgTables
            else:
                print "You need to install CCP4 suite"
                sys.exit()
        else:
            if os.path.isdir(t_inputOptionsP.acedrgTables):
                self.acedrgTables = t_inputOptionsP.acedrgTables
 
        if not self.acedrgTables or not glob.glob(self.acedrgTables):
            if os.environ.has_key("LIBMOL_ROOT"):
                tAcedrgTables = os.path.join(os.environ['LIBMOL_ROOT'], "share/acedrg/tables")
                print tAcedrgTables
                if os.path.isdir(tAcedrgTables):
                    self.acedrgTables = tAcedrgTables

        if not self.acedrgTables :
            
            tAcedrgTables = os.path.join(self.acedrgDir, "share/acedrg/tables")
            print tAcedrgTables
            if os.path.isdir(tAcedrgTables):
                self.acedrgTables = tAcedrgTables
            else:
                print "Bond and angle tables required could not be found "
                sys.exit()

        if not t_inputOptionsP.molGen and not t_inputOptionsP.repCrd and not t_inputOptionsP.typeOut: 
            if not t_inputOptionsP.noGeoOpt:
                if t_inputOptionsP.inMmCifName:
                    self.inMmCifName = t_inputOptionsP.inMmCifName
                    self.workMode    = 11            
                elif t_inputOptionsP.inSmiName: 
                    self.inSmiName = t_inputOptionsP.inSmiName
                    self.workMode    = 12            
                elif t_inputOptionsP.inMdlName: 
                    self.inMdlName = t_inputOptionsP.inMdlName
                    self.workMode    = 13
                elif t_inputOptionsP.inSdfName: 
                    self.inSdfName = t_inputOptionsP.inSdfName
                    self.workMode    = 14
                elif t_inputOptionsP.inMol2Name: 
                    self.inMol2Name = t_inputOptionsP.inMol2Name
                    self.workMode    = 15
            else:
                if t_inputOptionsP.inMmCifName:
                    self.inMmCifName = t_inputOptionsP.inMmCifName
                    self.workMode    = 111            
                elif t_inputOptionsP.inSmiName: 
                    self.inSmiName = t_inputOptionsP.inSmiName
                    self.workMode    = 121            
                elif t_inputOptionsP.inMdlName: 
                    self.inMdlName = t_inputOptionsP.inMdlName
                    self.workMode    = 131
                elif t_inputOptionsP.inSdfName: 
                    self.inSdfName = t_inputOptionsP.inSdfName
                    self.workMode    = 141
                elif t_inputOptionsP.inMol2Name: 
                    self.inMol2Name = t_inputOptionsP.inMol2Name
                    self.workMode    = 151
        elif t_inputOptionsP.molGen:
            if t_inputOptionsP.inStdCifName: 
                self.inStdCifName = t_inputOptionsP.inStdCifName
                self.workMode     = 21
            elif t_inputOptionsP.inStdCifDir: 
                self.inStdCifDir = t_inputOptionsP.inStdCifDir
                self.workMode     = 22
        elif t_inputOptionsP.typeOut:
            if t_inputOptionsP.inMmCifName:
                self.inMmCifName = t_inputOptionsP.inMmCifName
                self.workMode    = 31
            elif t_inputOptionsP.inSmiName:
                self.inSmiName = t_inputOptionsP.inSmiName
                self.workMode    = 32
            elif t_inputOptionsP.inMdlName:
                self.inMdlName = t_inputOptionsP.inMdlName
                self.workMode    = 33
            elif t_inputOptionsP.inSdfName:
                self.inSdfName = t_inputOptionsP.inSdfName
                self.workMode    = 34
            if t_inputOptionsP.inStdCifName:
                self.inStdCifName = t_inputOptionsP.inStdCifName
                self.workMode     = 35
        elif t_inputOptionsP.repCrd :
            if t_inputOptionsP.inPdbName and t_inputOptionsP.inMmCifName: 
                self.inMmCifName = t_inputOptionsP.inMmCifName
                self.inPdbName = t_inputOptionsP.inPdbName
                self.workMode     = 41

        if t_inputOptionsP.testMode :
            self.testMode = True

        if t_inputOptionsP.monomRoot:
            self.monomRoot   = t_inputOptionsP.monomRoot
        else:
            print self.inMdlName
            print "Here"
            self.setMonoRoot()
            # self.monomRoot   = "UNL"

        if t_inputOptionsP.outRoot:
            self.outRoot   = t_inputOptionsP.outRoot
            self.baseRoot  = os.path.basename(t_inputOptionsP.outRoot)
            tDir = os.path.dirname(t_inputOptionsP.outRoot)
            if tDir:
                tStrGrp = os.path.abspath(tDir).strip().split("/")
                tCurDir = "/"
                for tSub in tStrGrp:
                    tCurDir = os.path.join(tCurDir, tSub)
                    if not os.path.isdir(tCurDir):
                        os.mkdir(tCurDir)
        else:
            self.outRoot   = "AcedrgOut"
            self.baseRoot   = "AcedrgOut"

      
        self.scrDir = self.outRoot + "_TMP"
        if not os.path.isdir(self.scrDir):
            os.mkdir(self.scrDir)

        if self.inSmiName !="":
            if not os.path.isfile(self.inSmiName): 
                tName = os.path.join(self.scrDir, "inputSmiles.smi")
                try :
                    tF =open(tName, "w")
                except IOError:
                    print "Could not put the input smiles in a file "
                    sys.exit(1)
                else:
                    tF.write(self.inSmiName+ "\n")
                    tF.close()
                    self.inSmiName = tName
                    print tName

         
    def printJobs(self):

        print "=====================================================================" 
        if self.versionInfo.has_key("ACEDRG_VERSION"):
            print "| ACEDRG version:  %s|"%self.versionInfo["ACEDRG_VERSION"].ljust(49)
        else: 
            print "=====================================================================" 
            print "| ACEDRG version is not available                                   |"    
        if self.versionInfo.has_key("DATABASE_VERSION"):
            print "| ACEDRG database version:  %s|"%self.versionInfo["DATABASE_VERSION"].ljust(40)
        else:
            print "| ACEDRG Database version is not available                          |"    
        print "=====================================================================" 
        if self.workMode == 11 or self.workMode==12 or self.workMode ==13 or self.workMode==14 or self.workMode==15 \
           or self.workMode == 111 or self.workMode==121 or self.workMode ==131 or self.workMode==141 or self.workMode==151 :
            print "=====================================================================" 
            print "| Your job is  generating the dictionary (cif) and coord(pdb) files |"
            print "| for your ligand and/or monomer                                    |"
            print "=====================================================================" 
            if self.workMode==11 or self.workMode==111:
                print "Input file: %s"%os.path.basename(self.inMmCifName)
            if self.workMode==12 or self.workMode==121:
                print "Input file: %s"%os.path.basename(self.inSmiName)
            if self.workMode==13 or self.workMode==131:
                print "Input file: %s"%os.path.basename(self.inMdlName)
            if self.workMode==14 or self.workMode==141:
                print "Input file: %s"%os.path.basename(self.inSdfName)
            if self.workMode==15 or self.workMode==151:
                print "Input file: %s"%os.path.basename(self.inMol2Name)
            print "Output dictionary file: %s"%self.outRoot + ".cif"
            if self.workMode == 11 or self.workMode==12 or self.workMode ==13 or self.workMode==14 or self.workMode==15:
                print "Output coordinate file: %s"%self.outRoot + ".pdb"

        if self.workMode == 21 or self.workMode==22:
            print "=====================================================================" 
            print "| Your job is to generate molecules (sets of connected atoms), to   |"
            print "| get unique bonds and angles within the molecules and cluster them |" 
            print "| by specificted desigend atom types.                               |" 
            print "=====================================================================" 
            if self.workMode == 21:
                print "Input file: %s"%os.path.basename(self.inStdCifName)
                print "Output molecules: %s"%self.outRoot + "_all_mols.txt"
                print "Output bonds and angles : %s"%self.outRoot + "_unique_bond_and_angles.txt"
            elif self.workMode==22:
                print "Input directory where cif files are: %s"%self.inStdCifDir
                print "Output bond and angle file : %s "%(self.outRoot + "_all_bonds_and_angles.table")
 
    def runLibmol(self, tIn=None):
 
        self._cmdline = self.libmol
        self._log_name       = os.path.join(self.scrDir, self.baseRoot + "_cod.log")
        if self.workMode == 11 or self.workMode==12 or self.workMode ==13 or self.workMode==14 \
           or self.workMode == 111 or self.workMode == 121 or self.workMode == 131 \
           or self.workMode==141 or self.workMode==151:
            print "===================================================================" 
            print "| Generate the dictionary file using the internal database        |"
            print "===================================================================" 
            self.outRstCifName   = os.path.join(self.scrDir, self.baseRoot + "_cod.rst")   
            self.outRstPdbName   = os.path.join(self.scrDir, self.baseRoot + "_cod.pdb")
        if self.workMode == 11 or self.workMode == 12 or self.workMode == 111 or self.workMode == 121:
            if tIn:
                self.inMmCifName    = tIn
            self._cmdline +=" -c %s -D %s "%(self.inMmCifName, self.acedrgTables)
            self._cmdline += " -r %s -o %s "%(self.monomRoot, self.outRstCifName)
            #print self._cmdline
            #os.system(self._cmdline)
            self.subExecute()
        if self.workMode == 13 or self.workMode == 14 or self.workMode == 131 or self.workMode == 141:
            if tIn:
                self.inMmCifName = tIn
            self._cmdline += " -s %s  -D %s "%(self.inMmCifName, self.acedrgTables)
            self._cmdline += " -r %s -o %s "%(self.monomRoot, self.outRstCifName)
            #print self._cmdline
            #os.system(self._cmdline)
            self.subExecute()
        if self.workMode == 15 or self.workMode == 151 :
            if tIn:
                self.inMmCifName = tIn
            self._cmdline += " -g %s  -D %s "%(self.inMmCifName, self.acedrgTables)
            self._cmdline += " -r %s -o %s "%(self.monomRoot, self.outRstCifName)
            #print self._cmdline
            #os.system(self._cmdline)
            self.subExecute()


        if self.workMode == 21 :
            if tIn:
                self.inStdCifName = tIn
            self.outRstCifName  = self.outRoot + ".cif"
	    self.outMolsName    = self.monomRoot + "_all_mols.txt"
            self.outBondsAndAnglesName  = self.monomRoot + "_unique_bond_and_angles.txt"

            self._cmdline += " -b %s  "%self.inStdCifName
            self._cmdline += " -m yes -r %s -o %s "%(self.monomRoot, self.outRstCifName)
            #print self.cmdline
            self.subExecute()

        if self.workMode == 22 :
            if os.path.isdir(self.inStdCifDir):
                bTable = self.outRoot + "_all_atoms_bonds_angles.table"
                self.workMode  == 21
                tAllBondInMols = []
                tExcludedCif   = []
                for aCif in glob.glob(self.inStdCifDir+"/*.cif"):
                    tMonomRoot = os.path.basename(aCif).split(".")[0]
                    print "Generate molecules, bonds and angles from ", aCif
                    self._cmdline = self.libmol
                    self.outMolsName            = os.path.join(self.scrDir,tMonomRoot + "_all_mols.txt")
                    self.outBondsAndAnglesName  = os.path.join(self.scrDir, tMonomRoot + "_unique_bond_and_angles.txt")
                    self._log_name = os.path.join(self.scrDir,tMonomRoot + "_cod.log")
                    self._cmdline += " -b %s  "%aCif
                    self._cmdline += " -m yes -r %s -o %s.cif "%(tMonomRoot, self.scrDir+"/"+tMonomRoot)
                    #print self._cmdline
                    self.subExecute()
                    if os.path.isfile(self.outBondsAndAnglesName):
                        tAllBondInMols.append(self.outBondsAndAnglesName)
                    else:
                        print "No bonds and angles are generated from ", aCif
                        tExcludedCif.append(aCif)
                if len(tAllBondInMols) !=0:
                    self.getBondsAndAngles(bTable, tAllBondInMols)
                if len(tExcludedCif) !=0:
                    print "The following cif files have been excluded from calculations because their large R factors"
                    for aExCif in tExcludedCif:
                        print aExCif

        if self.workMode == 31 or self.workMode == 32 or self.workMode == 33 or self.workMode == 34 or self.workMode == 35 : 
            print "===================================================================" 
            print "| Generate atom types of Acedrg style and CCP4 style              |"
            print "===================================================================" 
            if self.workMode == 31 or self.workMode == 32:
                if tIn:
                    self.inMmCifName    = tIn
                self.outAtmTypeName = self.outRoot + "_atomTypes.txt"
                self._cmdline +=" -A yes -D %s -c %s  -o %s "%(self.acedrgTables, self.inMmCifName, self.outAtmTypeName)
                print self._cmdline
                self.subExecute()
                
        if self.workMode == 41 :

            self._cmdline += " -p %s -c %s "%(self.inPdbName, self.inMmCifName)
            self._cmdline += " -y y -o %s "%(self.outRstCifName)
            #print self._cmdline
            self.subExecute()
             
    def getBondsAndAngles(self, tFName, tMolTabs):
        
        for aMonTab in tMolTabs:
            try:
                f1 = open(aMonTab, "r")
            except IOError:
                print "%s has not been found for reading"%aMonTab
            else:
                f1_lines = f1.readlines()
                f1.close()
                aSetStrs = os.path.basename(aMonTab).strip().split("_")
                aFileIdx = aSetStrs[0]
                lBo = False
                lAn = False
                for aL in f1_lines:
                    strGrp = aL.strip().split()
                    if lBo and len(strGrp)==6:
                         # get sorted bonds
                         aElem = self.getElemFromAtomClass(strGrp[0])
                         bElem = self.getElemFromAtomClass(strGrp[1])
                         aSet  = [strGrp[0], aElem, strGrp[2]]                  
                         bSet  = [strGrp[1], bElem, strGrp[3]]   
                         tSets = [aSet, bSet]
                         tSets.sort(listComp)
                         if not self.allBondsAndAngles["bonds"].has_key(tSets[0][1]):         # class 1
                             self.allBondsAndAngles["bonds"][tSets[0][1]] = {}
                         if not self.allBondsAndAngles["bonds"][tSets[0][1]].has_key(tSets[1][1]): # class 2 
                             self.allBondsAndAngles["bonds"][tSets[0][1]][tSets[1][1]] = {}
                         if not self.allBondsAndAngles["bonds"][tSets[0][1]][tSets[1][1]].has_key(tSets[0][0]): # id 1
                             self.allBondsAndAngles["bonds"][tSets[0][1]][tSets[1][1]][tSets[0][0]] ={}
                         if not self.allBondsAndAngles["bonds"][tSets[0][1]][tSets[1][1]][tSets[0][0]].has_key(tSets[1][0]): # id 2
                             self.allBondsAndAngles["bonds"][tSets[0][1]][tSets[1][1]][tSets[0][0]][tSets[1][0]] = {}
                         if not self.allBondsAndAngles["bonds"][tSets[0][1]][tSets[1][1]][tSets[0][0]][tSets[1][0]].has_key(strGrp[5]):
                             self.allBondsAndAngles["bonds"][tSets[0][1]][tSets[1][1]][tSets[0][0]][tSets[1][0]][strGrp[5]] = {}
                             self.allBondsAndAngles["bonds"][tSets[0][1]][tSets[1][1]][tSets[0][0]][tSets[1][0]][strGrp[5]]["observations"] = []
                             self.allBondsAndAngles["bonds"][tSets[0][1]][tSets[1][1]][tSets[0][0]][tSets[1][0]][strGrp[5]]["stats"]        = {}

                         aTup = [tSets[0][2], tSets[1][2], float(strGrp[4]), aFileIdx]              # atom1_id, atom2_id, bond length and the file index
                         self.allBondsAndAngles["bonds"][tSets[0][1]][tSets[1][1]][tSets[0][0]][tSets[1][0]][strGrp[5]]["observations"].append(aTup) 
                

                         # Get sorted atom types
                         if not self.allBondsAndAngles["atomClasses"].has_key(tSets[0][1]):            # atom 1 element type
                             self.allBondsAndAngles["atomClasses"][tSets[0][1]] = {}
                         if not self.allBondsAndAngles["atomClasses"][tSets[0][1]].has_key(tSets[0][0]): # atom 1 class type
                             self.allBondsAndAngles["atomClasses"][tSets[0][1]][tSets[0][0]] = 1
                         else:
                             self.allBondsAndAngles["atomClasses"][tSets[0][1]][tSets[0][0]] +=1

                         if not self.allBondsAndAngles["atomClasses"].has_key(tSets[1][1]):            # atom 2 element type
                              self.allBondsAndAngles["atomClasses"][tSets[1][1]] = {}
                         if not self.allBondsAndAngles["atomClasses"][tSets[1][1]].has_key(tSets[1][0]): # atom 2 class type
                              self.allBondsAndAngles["atomClasses"][tSets[1][1]][tSets[1][0]] = 1
                         else:
                              self.allBondsAndAngles["atomClasses"][tSets[1][1]][tSets[1][0]] +=1
                    elif lAn and len(strGrp)==7:
                         # get sorted bond-angles
                         cenElem = self.getElemFromAtomClass(strGrp[0])
                         aElem   = self.getElemFromAtomClass(strGrp[1])
                         bElem   = self.getElemFromAtomClass(strGrp[2])
                         aSet  = [strGrp[0], cenElem, strGrp[3]]                  
                         bSet  = [strGrp[1], aElem, strGrp[4]]   
                         cSet  = [strGrp[2], bElem, strGrp[5]]   
                         tSets = [bSet, cSet]
                         tSets.sort(listComp)
                         if not self.allBondsAndAngles["angles"].has_key(aSet[1]):         # center atom element  
                             self.allBondsAndAngles["angles"][aSet[1]] = {}
                         if not self.allBondsAndAngles["angles"][aSet[1]].has_key(tSets[0][1]): # atom1 element 
                             self.allBondsAndAngles["angles"][aSet[1]][tSets[0][1]] = {}
                         if not self.allBondsAndAngles["angles"][aSet[1]][tSets[0][1]].has_key(tSets[1][1]) : # atom2 elment 
                             self.allBondsAndAngles["angles"][aSet[1]][tSets[0][1]][tSets[1][1]] = {}
                         if not self.allBondsAndAngles["angles"][aSet[1]][tSets[0][1]][tSets[1][1]].has_key(aSet[0]): # center atom class
                             self.allBondsAndAngles["angles"][aSet[1]][tSets[0][1]][tSets[1][1]][aSet[0]] = {}
                         if not self.allBondsAndAngles["angles"][aSet[1]][tSets[0][1]][tSets[1][1]][aSet[0]].has_key(tSets[0][0]): #  atom1 class
                             self.allBondsAndAngles["angles"][aSet[1]][tSets[0][1]][tSets[1][1]][aSet[0]][tSets[0][0]] = {}
                         if not self.allBondsAndAngles["angles"][aSet[1]][tSets[0][1]][tSets[1][1]][aSet[0]][tSets[0][0]].has_key(tSets[1][0]): #  atom1 class
                             self.allBondsAndAngles["angles"][aSet[1]][tSets[0][1]][tSets[1][1]][aSet[0]][tSets[0][0]][tSets[1][0]] = {}
                             self.allBondsAndAngles["angles"][aSet[1]][tSets[0][1]][tSets[1][1]][aSet[0]][tSets[0][0]][tSets[1][0]]["observations"] = []
                             self.allBondsAndAngles["angles"][aSet[1]][tSets[0][1]][tSets[1][1]][aSet[0]][tSets[0][0]][tSets[1][0]]["stats"]        = {}
                         aTup = [aSet[2], tSets[0][2], tSets[1][2], float(strGrp[6]), aFileIdx]              # cen_atom_id, atom1_id, atom2_id, bond length and the file index
                         self.allBondsAndAngles["angles"][aSet[1]][tSets[0][1]][tSets[1][1]][aSet[0]][tSets[0][0]][tSets[1][0]]["observations"].append(aTup)
                    elif aL.find("Bond_length") != -1:
                        lBo = True
                    elif aL.find("Angle_") !=-1:
                        lAn = True
                        lBo = False

        # output atom types, bonds and angles
        try:
            f2 = open(tFName, "w")
        except IOError:
            print "%s has not been found for writing"%tFName
        else:
            if len(self.allBondsAndAngles["atomClasses"]):
                f2.write("All unique atom types: \n")
                for aElem in sorted(self.allBondsAndAngles["atomClasses"].iterkeys()):
                    for aCl in sorted(self.allBondsAndAngles["atomClasses"][aElem].iterkeys()):
                        f2.write("%s        %s     \n"%(aElem,aCl))   
            if len(self.allBondsAndAngles["bonds"]):
                f2.write("All bond lengths: \n")  
                for aElem in sorted(self.allBondsAndAngles["bonds"].iterkeys()):
                    for bElem in sorted(self.allBondsAndAngles["bonds"][aElem].iterkeys()):
                        for aCl in sorted(self.allBondsAndAngles["bonds"][aElem][bElem].iterkeys()):
                            for bCl in sorted(self.allBondsAndAngles["bonds"][aElem][bElem][aCl].iterkeys()):
                                for rP in sorted(self.allBondsAndAngles["bonds"][aElem][bElem][aCl][bCl].iterkeys()):
                                    self.getStatsForOneBondClass(self.allBondsAndAngles["bonds"][aElem][bElem][aCl][bCl][rP]["observations"], \
                                                            self.allBondsAndAngles["bonds"][aElem][bElem][aCl][bCl][rP]["stats"])                                  
                                    for aSet in self.allBondsAndAngles["bonds"][aElem][bElem][aCl][bCl][rP]["observations"]:
                                        if self.testMode:
                                            f2.write("%s       %s        %s      %s      %s      %s    %s     %7.5f    %s\n" \
                                                     %(aElem, bElem, aCl, bCl, aSet[0],  aSet[1], rP, aSet[2], aSet[3]))   
                                        else:
                                            f2.write("%s       %s        %s      %s      %s      %s    %s     %7.5f\n" \
                                                     %(aElem, bElem, aCl, bCl, aSet[0],  aSet[1], rP, aSet[2]))   

                f2.write("\nAll bond lengths stats: \n")
                if self.testMode: 
                    bCaseIdx =0
                    casesDir = os.path.join(self.scrDir, "CasesNSTATS")
                    if not glob.glob(casesDir):
                        os.mkdir(casesDir)
                     
                
                for aElem in sorted(self.allBondsAndAngles["bonds"].iterkeys()):
                    for bElem in sorted(self.allBondsAndAngles["bonds"][aElem].iterkeys()):
                        for aCl in sorted(self.allBondsAndAngles["bonds"][aElem][bElem].iterkeys()):
                            for bCl in sorted(self.allBondsAndAngles["bonds"][aElem][bElem][aCl].iterkeys()):
                                for rP in sorted(self.allBondsAndAngles["bonds"][aElem][bElem][aCl][bCl].iterkeys()):
                                    ave = self.allBondsAndAngles["bonds"][aElem][bElem][aCl][bCl][rP]["stats"]["mean"]
                                    sig = self.allBondsAndAngles["bonds"][aElem][bElem][aCl][bCl][rP]["stats"]["sig"]
                                    num = self.allBondsAndAngles["bonds"][aElem][bElem][aCl][bCl][rP]["stats"]["nObs"]
                                    f2.write("%s      %s    %7.5f      %7.5f      %d\n" %(aCl, bCl, ave, sig, num)) 
                                    if self.testMode and num > 20 and aElem !="H" and bElem != "H" :
                                        bCaseIdx +=1
                                        tCasesNameR = aElem + "_" + bElem + str(bCaseIdx) + ".txt"
                                        tCasesName  = os.path.join(casesDir, tCasesNameR)
                                        tCases      = open(tCasesName, "w")
                                        for aSet in self.allBondsAndAngles["bonds"][aElem][bElem][aCl][bCl][rP]["observations"]:
                                            tCases.write("%s      %s      %s      %s    %s     %7.5f    %s\n" \
                                                     %(aCl, bCl, aSet[0],  aSet[1], rP, aSet[2], aSet[3]))        
                                        tCases.close()
                                        
  
            if len(self.allBondsAndAngles["angles"]):
                f2.write("\n\nAll bond angles: \n")
                for cenElem in sorted(self.allBondsAndAngles["angles"].iterkeys()):
                    for aElem in sorted(self.allBondsAndAngles["angles"][cenElem].iterkeys()):
                        for bElem in sorted(self.allBondsAndAngles["angles"][cenElem][aElem].iterkeys()):
                            for cenC in sorted(self.allBondsAndAngles["angles"][cenElem][aElem][bElem].iterkeys()):
                                for aC in sorted(self.allBondsAndAngles["angles"][cenElem][aElem][bElem][cenC].iterkeys()):
                                    for bC in sorted(self.allBondsAndAngles["angles"][cenElem][aElem][bElem][cenC][aC].iterkeys()):
                                        self.getStatsForOneAngleClass(self.allBondsAndAngles["angles"][cenElem][aElem][bElem][cenC][aC][bC]["observations"], \
                                                                      self.allBondsAndAngles["angles"][cenElem][aElem][bElem][cenC][aC][bC]["stats"])
                                        for aSet in self.allBondsAndAngles["angles"][cenElem][aElem][bElem][cenC][aC][bC]["observations"]:
                                            if self.testMode:
                                                f2.write("%s      %s       %s        %s      %s      %s      %s        %s      %s     %7.5f    %s\n" \
                                                          %(cenElem, aElem, bElem, cenC, aC, bC, aSet[0],  aSet[1], aSet[2], aSet[3], aSet[4]))   
                                            else:
                                                f2.write("%s      %s       %s        %s      %s      %s      %s        %s      %s     %7.5f\n" \
                                                          %(cenElem, aElem, bElem, cenC, aC, bC, aSet[0],  aSet[1], aSet[2], aSet[3]))   
                f2.write("\nAll bond angle stats : \n")
                for cenElem in sorted(self.allBondsAndAngles["angles"].iterkeys()):
                    for aElem in sorted(self.allBondsAndAngles["angles"][cenElem].iterkeys()):
                        for bElem in sorted(self.allBondsAndAngles["angles"][cenElem][aElem].iterkeys()):
                            for cenC in sorted(self.allBondsAndAngles["angles"][cenElem][aElem][bElem].iterkeys()):
                                for aC in sorted(self.allBondsAndAngles["angles"][cenElem][aElem][bElem][cenC].iterkeys()):
                                    for bC in sorted(self.allBondsAndAngles["angles"][cenElem][aElem][bElem][cenC][aC].iterkeys()):
                                        ave  = self.allBondsAndAngles["angles"][cenElem][aElem][bElem][cenC][aC][bC]["stats"]["mean"]
                                        sig  = self.allBondsAndAngles["angles"][cenElem][aElem][bElem][cenC][aC][bC]["stats"]["sig"]
                                        nObs = self.allBondsAndAngles["angles"][cenElem][aElem][bElem][cenC][aC][bC]["stats"]["nObs"]
                                        f2.write("%s      %s      %s   %7.5f    %7.5f   %d\n" \
                                                  %(cenC, aC, bC, ave, sig, nObs))   
            f2.close()
   
    def getElemFromAtomClass(self, tAtm):
        
        tElem = ""
        if len(tAtm) != 0:
            tStrs = tAtm.strip().split("(")
            if len(tStrs) != 0:
                if tStrs[0].find("[")==-1:
                    tElem = tStrs[0]    
                else :
                    tElem = tStrs[0].split("[")[0]
        return tElem         


    def getStatsForOneBondClass(self, tBondObjList, tBondStatsDict):

        
        numObjs = len(tBondObjList)

        if numObjs:
     
            tBondStatsDict["nObs"] = numObjs
            tBondStatsDict["max"]  = 0.0
            tBondStatsDict["min"]  = 100.0

            sum = 0.0
            for aTup in tBondObjList:
                if len(aTup)==4:
                    sum = sum + aTup[2]
                    if aTup[2] > tBondStatsDict["max"]:
                        tBondStatsDict["max"] = aTup[2]
                    if aTup[2] < tBondStatsDict["min"]:
                        tBondStatsDict["min"] = aTup[2]
                else: 
                    print "Bug. error in ", aTup
                    sys.exit(1)

            tBondStatsDict["mean"] = sum/numObjs

            sum_diff_sq = 0.0

            for aTup in tBondObjList:
                sum_diff_sq +=((aTup[2]- tBondStatsDict["mean"])*(aTup[2]- tBondStatsDict["mean"]))
                              
            if numObjs > 1:
                tBondStatsDict["sig"] = math.sqrt(sum_diff_sq/(numObjs-1))
            else:
                tBondStatsDict["sig"] = 0.0  
            
            
    def getStatsForOneAngleClass(self, tAngleObjList, tAngleStatsDict):
        
        numObjs = len(tAngleObjList)

        if numObjs:
     
            tAngleStatsDict["nObs"] = numObjs
            tAngleStatsDict["max"]  = 0.0
            tAngleStatsDict["min"]  = 360.0

            sum = 0.0
            for aTup in tAngleObjList:
                if len(aTup)==5:
                    if (aTup[3] > 180.0):
                        aTup[3] = 360.0-aTup[3]
                    elif (aTup[3] < 0.0):
                        aTup[3] = math.fabs(aTup[3])

                    sum = sum + aTup[3]
                    if aTup[3] > tAngleStatsDict["max"]:
                        tAngleStatsDict["max"] = aTup[3]
                    if aTup[3] < tAngleStatsDict["min"]:
                        tAngleStatsDict["min"] = aTup[3]
                else: 
                    print "Bug. error in ", aTup
                    sys.exit(1)

            tAngleStatsDict["mean"] = sum/numObjs

            sum_diff_sq = 0.0

            for aTup in tAngleObjList:
                sum_diff_sq +=((aTup[3]- tAngleStatsDict["mean"])*(aTup[3]- tAngleStatsDict["mean"]))

            if numObjs > 1:
                tAngleStatsDict["sig"] = math.sqrt(sum_diff_sq/(numObjs-1))
            else:
                tAngleStatsDict["sig"] = 0.0  
            
            
    def setLibcheckBat(self, tSmiName, tOutRoot):

        self._cmdline = self.libcheck 

        libcheckBatName = os.path.join(self.scrDir, self.baseRoot + "_libcheck.bat")
        try:
            libcheckBat = open(libcheckBatName,"w")
        except IOError:
            print libcheckBatName, " could not be opened for write "
            sys.exit()
        else:
            libcheckBat.write(" N \n")
            libcheckBat.write("FILE_SMILE %s\n"%tSmiName) 
            libcheckBat.write("FILE_O  %s \n\n"%tOutRoot)
            libcheckBat.close()

            self._cmdline +=" < %s "%(libcheckBatName)
            #print self.cmdline
    
    def runLibcheck(self, tSmiName=None):

        if tSmiName:
            self.inSmiName = tSmiName
        self._log_name        = os.path.join(self.scrDir, self.baseRoot + "_libcheck.log")
        self.libcheckOutRoot  = os.path.join(self.scrDir, self.baseRoot + "_libcheck")
        self.libcheckLibName  = self.libcheckOutRoot +".lib"
        self.setLibcheckBat(self.inSmiName,  self.libcheckOutRoot) 
        self.subExecute()
    
    def setRefmacCom(self, tPdbIn, tLibIn, tPdbOut, tStage=2):
     
        self._cmdline = self.refmac 
        if tStage==1:
            self._cmdline += "  xyzin %s libin %s xyzout %s <<eof\n"%(tPdbIn, tLibIn, tPdbOut) 
            self._cmdline += "end       \n"
	    self._cmdline += "eof       \n"
        elif tStage==2: 
            self._cmdline += "  xyzin %s libin %s xyzout %s <<eof\n"%(tPdbIn, tLibIn, tPdbOut)  
            self._cmdline += "mode newe \n"
            self._cmdline += "end       \n"
	    self._cmdline += "eof       \n"
        elif tStage==3: 
            self._cmdline += "  xyzin %s libin %s xyzout %s <<eof\n"%(tPdbIn, tLibIn, tPdbOut)  
            self._cmdline += "ncyc 1    \n"
            self._cmdline += "end       \n"
	    self._cmdline += "eof       \n"
        elif tStage==4: 
            self._cmdline += "  xyzin %s libin %s xyzout %s <<eof\n"%(tPdbIn, tLibIn, tPdbOut)  
            self._cmdline += "make hout yes    \n"
            self._cmdline += "ncyc 40    \n"
            self._cmdline += "end       \n"
	    self._cmdline += "eof       \n"
        else: 
            self._cmdline += "  xyzin %s libin %s xyzout %s <<eof\n"%(tPdbIn, tLibIn, tPdbOut)  
            self._cmdline += "mode newe \n"
            self._cmdline += "end       \n"
	    self._cmdline += "eof       \n"
        #print self._cmdline

    def runRefmac(self, tPdbIn, tLibIn, tStage=2):
      
        print "| Stage  %d        |"%tStage
        self._log_name    = os.path.join(self.scrDir, self.baseRoot + "_refmac_stage_"+ str(tStage) + ".log")
        self.refmacXYZOUTName = os.path.join(self.scrDir, self.baseRoot + "_refmac_stage_"+ str(tStage) + ".pdb")
        self.setRefmacCom(tPdbIn, tLibIn, self.refmacXYZOUTName, tStage)
        self.subExecute()
    
    def runGeoOpt(self):

        # Geometrical optimization
        if os.path.isfile(self.outRstCifName) and os.path.isfile(self.outRstPdbName):
            if os.path.getsize(self.outRstPdbName) > 1000 and os.path.getsize(self.outRstCifName) > 500:
                print "===================================================================" 
                print "| Geometrical Optimization                                        |"
                print "===================================================================" 
                stageNow = 1
                self.runRefmac(self.outRstPdbName, self.outRstCifName, stageNow)
                if os.path.isfile(self.refmacXYZOUTName):
                    tPdb1 = self.refmacXYZOUTName
                    stageNow = 2
                    self.runRefmac(tPdb1, self.outRstCifName, stageNow)
                    if os.path.isfile(self.refmacXYZOUTName):
                        tPdb1 = self.refmacXYZOUTName
                        stageNow = 3
                        self.runRefmac(tPdb1, self.outRstCifName, stageNow)
                        if os.path.isfile(self.refmacXYZOUTName):
                            tPdb1 = self.refmacXYZOUTName
                            stageNow = 4
                            self.runRefmac(tPdb1, self.outRstCifName, stageNow)
                            if os.path.isfile(self.refmacXYZOUTName):
                                finPdb = self.outRoot + ".pdb"
                                finRst = self.outRoot + ".cif"
                                #os.system("cp %s   %s "%(self.refmacXYZOUTName, finPdb))
                                shutil.copy(self.refmacXYZOUTName, finPdb)
                                #os.system("cp %s   %s "%(self.outRstCifName, finRst))
                                if os.path.isfile(finPdb):
                                    self.inPdbName        = finPdb 
                                    self.inMmCifName      = self.outRstCifName
                                    self.outRstCifName    = finRst
                                    self.transCoordsPdbToCif(self.inPdbName, self.inMmCifName, self.outRstCifName)
                                    print "===================================================================" 
                                else:
                                    print "Failed to produce %s after final geometrical optimization"%finPdb
                            else:
                                print "Failed to produce the final coordinates at stage 4 optimization" 
                        else:
                            print "Failed to produce the  coordinates at stage 3 optimization" 
                    else:
                        print "Failed to produce the coordinates at stage 2 optimization" 
                else:
                    print "Failed to produce the coordinates at stage 1 optimization" 
            else:
                print "No dictionary file produced ! " 
        else:
            print "No dictionary file produced ! " 
                        
    def transCoordsPdbToCif(self, tPdbInName, tCifInName, tCifOutName):

        cifCont = {}
        cifCont['head']   = []
        cifCont['atoms']  = []
        cifCont['others'] = []
        pdbAtoms = {}
        try:
            tPdbIn = open(tPdbInName, "r")
        except IOError:
            print "%s can not be opened for reading"%tPdbInName
            sys.exit()
        else:
            try: 
                tCifIn = open(tCifInName, "r")
            except IOError:
                print "%s can not be opened for reading"%tCifInName
                sys.exit()
            else:
                allPdbLines  = tPdbIn.readlines()
                tPdbIn.close()
                for aLine in allPdbLines:
                    aLine = aLine.strip()
                    if aLine.find("ATOM") !=-1 or aLine.find("HETATM") !=-1 :
                        tName = aLine[12:16].strip()
                        tX    = aLine[30:38].strip()
                        tY    = aLine[38:46].strip()
                        tZ    = aLine[46:54].strip()
                        pdbAtoms[tName] = []
                        pdbAtoms[tName].append(tX)
                        pdbAtoms[tName].append(tY)
                        pdbAtoms[tName].append(tZ)

                allCifLines = tCifIn.readlines()
                tCifIn.close()
                lStart = True
                lAtom  = False
                lOther = False

                for aLine in allCifLines:
                    if aLine.find("_chem_comp_atom.z") != -1:
                        cifCont['head'].append(aLine)
                        lStart = False
                        lAtom  = True
                    elif lAtom and aLine.find("loop") != -1:
                        lAtom  = False
                        lOther = True
                        cifCont['others'].append(aLine)
                    elif lStart:  
                        cifCont['head'].append(aLine)
                    elif lAtom:
                        strGrp = aLine.split()
                        tName  = ""
                        tID  = strGrp[1].strip()
                        if tID.find("\"") !=-1:
                            for aC in tID:
                                if aC !="\"":
                                    tName +=aC
                        else:
                            tName = tID
                            
                        if pdbAtoms.has_key(tName):
                            bLine = "%s%s%s%s%s%s%s%s\n"%(strGrp[0].ljust(8), tID.ljust(8), strGrp[2].ljust(8), \
                                                        strGrp[3].ljust(8), strGrp[4].ljust(8), \
                                                        pdbAtoms[tName][0].ljust(12), pdbAtoms[tName][1].ljust(12), \
                                                        pdbAtoms[tName][2].ljust(12)) 
                            cifCont['atoms'].append(bLine)
                        else: 
                            print "Bug. can not find atom %s in Pdb file %s "%(tName, tPdbInName) 
                            sys.exit()
                    elif lOther:
                        cifCont['others'].append(aLine)
  
                try:
                    tOutCif = open(tCifOutName, "w")
                except IOError:
                    print "%s can not be opened for reading"%tCifOutName
                    sys.exit()
                else:
                    for aL in cifCont['head']:
                        tOutCif.write(aL)
                    for aL in cifCont['atoms']:
                        tOutCif.write(aL)
                    for aL in cifCont['others']:
                        tOutCif.write(aL)
                    tOutCif.close()
                        
    def execute(self):
        
        self.printJobs()

        if self.workMode == 11 or self.workMode == 111:

            # Stage 1: dictionary generation using a mmcif file 
            if os.path.isfile(self.inMmCifName):
                self.runLibmol(self.inMmCifName)
            else:
                print "%s does not exist"%self.inMmCifName
                sys.exit()

            if self.workMode == 11 :
                # Stage 2: optimization
                self.runGeoOpt()    
        if self.workMode == 12 or self.workMode == 121:
            
            # Stage 1: Transfer the SMILE input into a mmcif file
            if os.path.isfile(self.inSmiName):
                self.runLibcheck(self.inSmiName)
            else:
                print "%s does not exist"%self.inSmiName
                sys.exit()

            # Stage 2: dictionary generation using a mmcif file
            if os.path.isfile(self.libcheckLibName):
                self.runLibmol(self.libcheckLibName)
            else:
                print "%s does not exist"%self.inMmCifName
                sys.exit()

            if self.workMode == 12:
                # Stage 3: optimization
                self.runGeoOpt()            

        if self.workMode == 13 or self.workMode == 131 :
            
            # Stage 1: dictionary generation using  a mdl file
            if os.path.isfile(self.inMdlName):
                self.runLibmol(self.inMdlName)
            else:
                print "can not find %s to read "%self.inMdlName
                sys.exit() 
            
            if self.workMode == 13 : 
                # Stage 2: optimization
                self.runGeoOpt()            
    
        if self.workMode == 14 or self.workMode == 141 :
            
            # Stage 1: dictionary generation using  a sdf file
            if os.path.isfile(self.inSdfName):
                self.runLibmol(self.inSdfName)
            else:
                print "%s does not exist"%self.inSdfName
                sys.exit()
           
            if self.workMode == 14: 
                # Stage 2: optimization
                self.runGeoOpt()            

        if self.workMode == 15 or self.workMode == 151 :
            
            # Stage 1: dictionary generation using  a mol2 file
            if os.path.isfile(self.inMol2Name):
                self.runLibmol(self.inMol2Name)
            else:
                print "%s does not exist"%self.inMol2Name
                sys.exit()
           
            if self.workMode == 15: 
                # Stage 2: optimization
                self.runGeoOpt()            

        if self.workMode == 21:
            
            # Stage 1: generate molecules and the associated bond and bond-angle values 
            # using a small molecule cif file
            if os.path.isfile(self.inStdCifName):
                self.runLibmol(self.inStdCifName)
            else:
                print "Can not find the input file ", self.inStdCifName 
            
        if self.workMode == 22:
            
            # 1. Generate molecules using the small molecule cif files at the input directory. 
            # 2. Generate atom classes for atoms in the molecules.
            # 3. Obtain unique bond lengths and angles and cluster them according to their 
            #    component atoms in tables.
            if os.path.isdir(self.inStdCifDir):
                self.runLibmol()
            else:
                print "Can not find the input directory ", self.inStdCifDir
                
        if self.workMode == 31:
            print "work mode ", self.workMode
            if os.path.isfile(self.inMmCifName):
                self.runLibmol()    
        
        if self.workMode == 32:
            print "work mode ", self.workMode
            if os.path.isfile(self.inSmiName):
                self.runLibcheck(self.inSmiName)
            else:
                print "%s does not exist"%self.inSmiName
                sys.exit()

            if os.path.isfile(self.libcheckLibName):
                self.runLibmol(self.libcheckLibName)
            else:
                print "%s does not exist"%self.inMmCifName
                sys.exit()
            
        if self.workMode == 33:
            print "work mode ", self.workMode
            if os.path.isfile(self.inMdlName):
                self.runLibmol()    
        

        if self.workMode == 34:
            print "work mode ", self.workMode
            if os.path.isfile(self.inSdfName):
                self.runLibmol()    

        if self.workMode == 35:
            print "work mode ", self.workMode
            if os.path.isfile(self.inStdCifName):
                self.runLibmol()    

        if self.workMode ==111 or self.workMode ==121 or self.workMode ==131 or self.workMode ==141:
            if os.path.isfile(self.outRstCifName):
                tCif = self.outRoot + ".cif"
                #os.system("cp %s %s"%(self.outRstCifName, tCif)) 
                shutil.copy(self.outRstCifName, tCif) 
            else:
                print "acedrg failed to generate a dictionary file"       


# Other supplement functions

def main():
    acedrgObj = Acedrg(sys.argv[1:])

if __name__ == "__main__":
    main()

