##
## Biskit, a toolkit for the manipulation of macromolecular structures
## Copyright (C) 2004-2012 Raik Gruenberg & Johan Leckner
##
## This program is free software; you can redistribute it and/or
## modify it under the terms of the GNU General Public License as
## published by the Free Software Foundation; either version 3 of the
## License, or any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
## General Public License for more details.
##
## You find a copy of the GNU General Public License in the file
## license.txt along with this program; if not, write to the Free
## Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
##
##
## $Revision: 1086 $
## last $Date: 2012-02-23 19:10:59 -0500 (Thu, 23 Feb 2012) $
## last $Author: graik $
"""
Interface to Xplor
"""
 
import tempfile
from time import time
import os
 
import settings
import Biskit.tools as T
from Biskit.Errors import BiskitError
from Biskit.Executor import Executor, RunError
 
class XplorerError( BiskitError ):
    pass
 
class Xplorer(Executor):
    """
    Prepare and run a xplor job.
 
    @see: L{Executor} for detailed documentation!
 
    @note: Command configuration in: biskit/Biskit/data/defaults/exe_xplor.dat
    """
 
    def __init__( self, template, pipes=None, **params):
        """
        Create an Executor instance for XPlor jobs. The only argument required
        is a template for the control script passed to Xplor. As usual, any
        keyword=value pairs given to this constructor, can be used to
        substitute place holders in the template (see L{Executor}!).
 
        @param template: template for Xplor input file. This can be the
                         template itself or the path to a file containing it.
        @type  template: str
        @param pipes: push input script directly through a pipe rather
                      than writing it to disc; If != None, this option
                      overrides the default setting from exe_xplor.dat.
                      The default in exe_xplor is pipes = 1 (True).
        @type  pipes: 1|0 or bool
 
        @param kw: additional key=value parameters for Executor
        for example::
          debug    - 0|1, keep all temporary files (default: 0)
          verbose  - 0|1, print progress messages to log (log != STDOUT)
          node     - str, host for calculation (None->local)
                          (default: None)
          nice     - int, nice level (default: 0)
          log      - Biskit.LogFile, program log (None->STOUT) (default: None)
        """
        Executor.__init__( self, 'xplor', template=template, **params )
 
        if pipes is not None:
            self.exe.pipes = pipes
 
        #: will contain copy of Xplor log file after run finished
        self.logLines = None
 
 
    def version( self ):
        """Version of class.
 
        @return: version
        @rtype: str
        """       
        return Executor.version(self) + '/ Xplorer $Revision: 1086 $'
 
 
    def postProcess( self ):
        """
        Parse xplor log into self.logLines
        """
        self.logLines = open( self.f_out ).readlines()
 
 
    def isFailed( self ):
        """
        Calculation completion check.
 
        @return: if xplor died
        @rtype: 1
        """
        return self.logLines == None or \
               self.logLines[-1].find("X-PLOR: exit time") == -1
 
 
    def fail( self ):
        """
        called after aborted calculation, override!
 
        @raise RunError: if Xplor terminated with an error
        """
        s = ''
        try:
            s += ''.join( self.logLines[-7:] )
        except:
            pass
 
        raise RunError, 'Xplor terminated with an error:\n' + s
 
 
    def saveXLog( self, fname ):
        """
        Save the content of the XPlor log file to a new file.
 
        @param fname: target file name
        @type  fname: str
 
        @raise XPlorerError: if logLines is empty (Xplor hasn't been run)
        """
        if not self.logLines:
            raise XplorerError('Xplor log file is (yet) empty.')
 
        f = open( T.absfile(fname), 'w')
        f.writelines( self.logLines )
        f.close()
 
 
 
#############
##  TESTING        
#############
import Biskit.test as BT
 
class Test(BT.BiskitTest):
    """Test"""
 
    TAGS = [ BT.EXE ]
 
    inp = """
!! short test  minimzation 
 
! ------------------------------------------------------------
! Place holders to be inserted by Python script
! ------------------------------------------------------------
evaluate ($ligandpsf = "%(lig_psf)s")
evaluate ($ligandpdb = "%(lig_pdb)s")
evaluate ($param19 = "%(param19)s" )
evaluate ($lig_out = "%(lig_out)s" )
 
! -------------------------------------
! read psf and pdb files for the ligand
! -------------------------------------
structure @@$ligandpsf end
coor @@$ligandpdb
delete selection= (resname TIP3) end
 
! ------------------
! set toplogies etc.
! ------------------
parameter
        reset
        @@$param19
end
 
! ------------------------------------------------------------
! minimize
! ------------------------------------------------------------
flags exclude * include bond angle impr elec end
minimize powell nstep=10            end
 
! ---------------------------------------------------
! write minimized PDBs
! ---------------------------------------------------
write coor output= $lig_out end
stop
"""
    def prepare(self):
        self.dir_out = tempfile.mkdtemp( '_test_Xplorer' )
 
        ## write a test template inp file to disc
        f = open( self.dir_out +'/test.inp', 'w')
        f.writelines( self.inp )
        f.close()
 
 
    def test_Xplorer( self ):
        """Xplorer test"""
 
        ## input template variables
        param = T.dataRoot() + '/xplor/toppar/param19.pro'
        pdb_out = self.dir_out +'/lig.pdb'
        log_out = self.dir_out +'/test.out'
        pdb_in = T.testRoot() + '/lig/1A19.pdb' 
        psf_in = T.testRoot() + '/lig/1A19.psf'
 
        self.x = Xplorer( self.dir_out +'/test.inp',
                     xout = log_out,
                     verbose = self.local,
                     lig_psf = psf_in,
                     lig_pdb = pdb_in,
                     param19 = param,
                     lig_out = pdb_out )
 
        self.x.run()
 
        if self.local:
            print """
The minimized structure and the X-Plor log file
have been written to %s and %s, respectively
            """ % (pdb_out, log_out)
 
            print """See x.logLines for the complete xplor log file!"""
 
 
    def cleanUp(self):
        T.tryRemove( self.dir_out, tree=1 )
 
if __name__ == '__main__':
 
    BT.localTest()