"""
    buccaneer_build_refine_mr.py: CCP4 GUI Project
     Copyright (C) 2010 University of York

     This library is free software: you can redistribute it and/or
     modify it under the terms of the GNU Lesser General Public License
     version 3, modified in accordance with the provisions of the 
     license to address the requirements of UK law.
 
     You should have received a copy of the modified GNU Lesser General 
     Public License along with this library.  If not, copies may be 
     downloaded from http://www.ccp4.ac.uk/ccp4license.php
 
     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 Lesser General Public License for more details.
"""

import sys, time
import os,shutil
from lxml import etree
from copy import deepcopy
from CCP4PluginScript import CPluginScript
import CCP4Utils

r_work_essentially_complete_model = 0.25

class buccaneer_build_refine_mr(CPluginScript):

    TASKMODULE = 'model_building'
    TASKTITLE = 'Autobuild protein (Buccaneer pipeline)'
    TASKNAME = 'buccaneer_build_refine_mr'
    TASKVERSION= 0.0
    INTERRUPTABLE = True
    GUINAME = 'buccaneer_build_refine_mr'
    RESTARTABLE = True
    ASYNCHRONOUS = True
    PERFORMANCECLASS = 'CModelBuildPerformance'
    MAINTAINER = 'jon.agirre@york.ac.uk'
    WHATNEXT = ['coot_rebuild','prosmart_refmac','buccaneer_build_refine_mr']
    PURGESEARCHLIST = [ [ 'buccaneer_mr%*/XYZOUT.*' , 5 , 'XYZOUT' ],
                      [ 'refmac%*/ABCDOUT.mtz', 5, 'ABCDOUT' ],
                      [ 'refmac%*/FPHIOUT.mtz', 5, 'FPHIOUT' ],
                      [ 'refmac%*/DIFFPHIOUT.mtz', 5, 'DIFFPHIOUT' ],
                      [ 'refmac%*/XYZOUT.pdb', 5, 'XYZOUT' ],
                      [ 'refmac%*/COOTSCRIPTOUT.scm', 5, 'COOTSCRIPTOUT' ],
                      [ 'prosmart_refmac%*/ABCDOUT.mtz', 5, 'ABCDOUT' ],
                      [ 'prosmart_refmac%*/FPHIOUT.mtz', 5, 'FPHIOUT' ],
                      [ 'prosmart_refmac%*/DIFFPHIOUT.mtz', 5, 'DIFFPHIOUT' ],
                      [ 'prosmart_refmac%*/XYZOUT.pdb', 5, 'XYZOUT' ],
                      [ 'prosmart_refmac%*/COOTSCRIPTOUT.scm', 5, 'COOTSCRIPTOUT' ],
                      [ 'prosmart_refmac%*/refmac%*/ABCDOUT.mtz', 5, 'ABCDOUT' ],
                      [ 'prosmart_refmac%*/refmac%*/FPHIOUT.mtz', 5, 'FPHIOUT' ],
                      [ 'prosmart_refmac%*/refmac%*/DIFFPHIOUT.mtz', 5, 'DIFFPHIOUT' ],
                      [ 'prosmart_refmac%*/refmac%*/XYZOUT.pdb', 5, 'XYZOUT' ],
                      [ 'prosmart_refmac%*/refmac%*/COOTSCRIPTOUT.scm', 5, 'COOTSCRIPTOUT' ]
                      ]
    ERROR_CODES = {  200 : { 'description' : 'Buccaneer task failed' },
                     201 : { 'description' : 'Refmac task failed' },
                     202 : { 'description' : 'Buccaneer output coordinate file not found' },
                     203 : { 'description' : 'Refmac output coordinate file not found' },
                     204 : { 'description' : 'Coot realspace operation output coordinate file not found' },
                     205 : { 'description' : 'Coot realspace operation output coordinate file not found' },
                     206 : { 'description' : 'Accessory refmac job failed' },
                     207 : { 'description' : 'Accessory refmac job output reflection file not found' }
                  }
                    
# For a pipeline it is only necessary to reimplement CPluginScript.process() - it must create and control the
# running of sub-jobs.  There are two different ways for process() to work:
#    ---  blocking mode: after starting a sub-job process it waits for the job to finish
#    ---  asynchronous mode: there is no waiting for a sub-job to finish and a signals-slots mechanism
#                            must be used to specify a 'handler' method for when a subjob finishes
# The blocking mode approach is more straightforward. The asynchronous approach is described more fully in
# the documentation ccp4i2/docs/developers/pipelines.html#running processes

# This demonstration pipeline is implemented in both approaches - the process() method calls either processInBlockingMode()
# or startAsynchronousProcess() dependent on the class parameter ASYNCHRONOUS
# 
    def process(self):
      if self.ASYNCHRONOUS:
        self.startAsynchronousProcess()
      else:
        self.processInBlockingMode()

    def setProgramVersion(self):
        print 'autobuild.getProgramVersion'
        return CPluginScript.setProgramVersion(self,'Autobuild protein')


# ====  Run in straight-forward 'blocking' mode ( this is simplified script that does not use prosmart or coot  )================================
# This version alternates running buccaneer and refmac wrappers and does not use the prosmart-refmac pipeline or have
# an option to run Coot.

    def processInBlockingMode(self):
        # set up first cycle and number of cycles - beware we could be restarting
        restarted = self.container.interruptStatus.LASTCYCLE.isSet()
        if restarted:
            self.cycle = int(self.container.interruptStatus.LASTCYCLE) + 1
        else:
            self.cycle = 0
        self.ncycles = int( self.container.controlParameters.ITERATIONS )
        # Keep track of the current best coordinate file in self.currentCoordinates
        # For this simple case of running buccaneer and refmac tracking the currentCoordinates
        # is not absolutely necessary but it clarifies coding when there is an optional coot plugin

        # If restarting job then look for the coordinate file in the last sub-job
        if self.cycle > 0 and os.path.exists(os.path.join(self.getSubDirectories()[-1],'XYZOUT.pdb')):
          # Look for the coordinate file in the last sub-job
          # this sets currentCoordinates to a string but that is OK
          self.currentCoordinates = os.path.join(self.getSubDirectories()[-1],'XYZOUT.pdb')
          print 'Restarting with coordinates in file:',self.currentCoordinates
        else:
          self.currentCoordinates = self.container.inputData.XYZIN    
          
        # Initialise the XML output file
        self.initialiseXML(restarted=restarted)

        # Do ncycles repetitions of buccaneer-refmac
        while self.cycle < self.ncycles:
            self.xmlcyc = etree.SubElement(self.xmlroot,"BuildRefineCycle")
            etree.SubElement(self.xmlcyc,"Number").text = str(self.cycle+1)
            # Create, run and check the buccaneer process
            self.buccaneerPlugin = self.makeBuccaneerPlugin(restarted=restarted)
            finishStatus = self.buccaneerPlugin.process()          
            self.checkFinishStatus(statusDict={'finishStatus':finishStatus},failedErrCode=200,
                               outputFile = self.buccaneerPlugin.container.outputData.XYZOUT,noFileErrCode=202)
            self.currentCoordinates = self.buccaneerPlugin.container.outputData.XYZOUT

            # Copy some buccaneer data to the xml file
            self.copyBuccaneerXML()
            xmlFile = self.buccaneerPlugin.makeFileName('PROGRAMXML')
            if os.path.exists(xmlFile):
                bxml = CCP4Utils.openFileToEtree(xmlFile)
                self.xmlcyc.append(bxml)


            # Create, run and check the refmac process
            self.refmacPlugin = self.makeRefmacPlugin()
            finishStatus = self.refmacPlugin.process()
            self.checkFinishStatus(statusDict={'finishStatus':finishStatus},failedErrCode=201,
                             outputFile=self.refmacPlugin.container.outputData.XYZOUT ,noFileErrCode=203)
            #self.currentCoordinates = self.refmacPlugin.container.outputData.XYZOUT

            # process XML from refmac and append to the pipeline XMLOUT
            self.copyRefmacXML()
            # Save the pipeline xml on every cycle
            open( self.pipelinexmlfile,'w').write( etree.tostring(self.xmlroot,pretty_print=True) )
        
            # Test for interrupt
            if self.testForInterrupt() and self.cycle<self.ncycles:
                shutil.copyfile( str(self.refmacPlugin.container.outputData.XYZOUT), str(self.container.outputData.XYZOUT.fullPath) )
                self.container.outputData.XYZOUT.annotation.set('Model built with ')
                self.container.interruptStatus.LASTCYCLE = self.cycle
                self.reportStatus(CPluginScript.INTERRUPTED)

            self.cycle += 1
            print 'On cycle',self.cycle,'of',self.ncycles
        
        # Copy final files into the pipeline directory
        self.copyPipelineOutputFiles()
        self.setPerformanceData()
        # THE END! - Terminate the process by calling  reportStatus with SUCCEEDED flag
        self.reportStatus(CPluginScript.SUCCEEDED)

# ====  Methods to run asynchronously =============================================================
#  Starts with startAsynchronousProcess() that starts the first buccaneer job. The connectSignal() call
#  at the end of this method ensures that the 'finished' signal from the buccaneer plugin is
#  handled by the buccaneerFinished() method. buccaneerFinished() starts the next job and uses connectSignal()
#  to set up the next link in the chain... and so on.
#  The final method in the chain (refmacFinished()) increments the cycle count and either starts the next cycle
#  with a buccaneer job or terminates the pipeline if we've reached the required number of cycles.
#

    def startAsynchronousProcess(self):
        
        # set up first cycle - beware we could be restarting
        restarted = self.container.interruptStatus.LASTCYCLE.isSet()
        if restarted:
            self.cycle = int(self.container.interruptStatus.LASTCYCLE) + 1
        else:
            self.cycle = 0
        self.ncycles = int( self.container.controlParameters.ITERATIONS )

        self.rwork = 0.9
        self.rfree = 0.9
        self.n_fragments = 1 

        self.initialiseXML(restarted=restarted)
        
        # If restarting job then look for the coordinate file in the last sub-job
        if self.cycle > 0 and os.path.exists(os.path.join(self.getSubDirectories()[-1],'XYZOUT.pdb')):
            # Look for the coordinate file in the last sub-job
            # this sets currentCoordinates to a string but that is OK
            self.container.controlParameters.XYZIN_MODE.set( 'partial' )
            self.currentCoordinates = os.path.join(self.getSubDirectories()[-1],'XYZOUT.pdb')
            print 'Restarting with coordinates in file:',self.currentCoordinates
        else:
#           self.container.controlParameters.XYZIN_MODE = 'partial'
            self.currentCoordinates = self.container.inputData.XYZIN    

        if self.container.controlParameters.BUCCANEER_PHSIN_TYPE == "mr" and not self.container.inputData.ABCD.isSet() :
            self.mrmodel = self.container.inputData.BUCCANEER_MR_MODE_XYZIN
            self.refmacABCD = self.makeRefmacABCD(self.mrmodel, False)

            error = self.refmacABCD.process()

            running = True
            
            while running:
                if self.refmacABCD.container.outputData.ABCDOUT.isSet() :
                    running = False
                    time.sleep(0.5)

            if error == CPluginScript.FAILED:
                self.reportStatus ( error )

            self.container.inputData.ABCD = self.refmacABCD.container.outputData.ABCDOUT
            self.container.inputData.ABCD.annotation.set ( 'Initial MR phases' )

            self.container.inputData.BUCCANEER_MR_MODE_XYZIN = self.refmacABCD.container.outputData.XYZOUT
            self.container.inputData.BUCCANEER_MR_MODE_XYZIN.annotation.set ( 'Refined molecular replacement solution' )
        

        if self.container.controlParameters.XYZIN_MODE and self.cycle == 0 :

            self.refmacABCD2 = self.makeRefmacABCD(self.currentCoordinates, False)

            error = self.refmacABCD2.process()

            running = True
            
            while running:
                if self.refmacABCD2.container.outputData.ABCDOUT.isSet() :
                    running = False
                    time.sleep(0.5)

            if error == CPluginScript.FAILED:
                self.reportStatus ( error )

            self.container.inputData.XYZIN = self.refmacABCD2.container.outputData.XYZOUT
            self.container.inputData.XYZIN.annotation.set ( 'Refined initial model' )

        # Start first buccaneer
        self.buccaneerPlugin = self.makeBuccaneerPlugin(restarted=restarted)
        self.connectSignal(self.buccaneerPlugin,'finished',self.buccaneerFinished)
        rv = self.buccaneerPlugin.process()
        return rv

    def buccaneerFinished(self, statusDict={}):
        # Check buccaneer finished and start either refmacPlugin0 (which is preparation for running coot) or go directly to refmacPlugin
        self.checkFinishStatus(statusDict=statusDict,failedErrCode=200,outputFile = self.buccaneerPlugin.container.outputData.XYZOUT,noFileErrCode=202)
        try:
          # Copy some data to the xml file
          xmlFile = self.buccaneerPlugin.makeFileName('PROGRAMXML')
          if os.path.exists(xmlFile):
              bxml = CCP4Utils.openFileToEtree(xmlFile)
              self.xmlcyc.append(bxml)
              open( self.pipelinexmlfile,'w').write( etree.tostring(self.xmlroot,pretty_print=True) )

          self.n_fragments = int(bxml.xpath('//BuccaneerResult/Final/FragmentsBuilt')[-1].text) 
          print '\nNumber of fragments built is ', self.n_fragments
          if self.n_fragments == 0: self.reportStatus(CPluginScript.UNSATISFACTORY)

          self.currentCoordinates = self.buccaneerPlugin.container.outputData.XYZOUT
          #Depending on whether there is/is not a cootPlugin defined, jump into refmacPlugin0 or prosmartRefmacPlugin
          if self.container.controlParameters.COOT_REALSPACE_OPERATION.__str__() != "none" and self.rwork < self.container.controlParameters.BUCCANEER_RSR_RWORK_LIMIT :
              self.refmacPlugin0 = self.makeRefmac0Plugin()
              self.refmacPlugin0.connectSignal(self.refmacPlugin0,'finished',self.refmac0Finished)
              rv = self.refmacPlugin0.process()
              if rv == CPluginScript.FAILED:
                 self.reportStatus(rv)
          else:
              self.refmacPlugin = self.makeProsmartRefmacPlugin()
              self.refmacPlugin.connectSignal(self.refmacPlugin,'finished',self.refmacFinished)
              rv = self.refmacPlugin.process()
              if rv == CPluginScript.FAILED:
                self.reportStatus(rv)
        except Exception as e:
          print e
          self.appendErrorReport(CPluginScript,39,str(e))
 

    def refmac0Finished(self, statusDict={}):
        # Check refmac status and then start coot
        self.checkFinishStatus(statusDict=statusDict,failedErrCode=206,outputFile = self.refmacPlugin0.container.outputData.ABCDOUT,noFileErrCode=207)
        try:
          print "Trying to access XML"
          self.accessRefmac0XML ( )
          print "XML accessed"
          
          self.currentCoordinates = self.refmacPlugin0.container.outputData.XYZOUT
          print "Current XYZOUT: ", self.currentCoordinates

          if self.rwork < self.container.controlParameters.BUCCANEER_RSR_RWORK_LIMIT :
              
              self.cootPlugin = self.makeCootPlugin()     
              self.cootPlugin.connectSignal(self.cootPlugin,'finished',self.cootFinished)
              
              rv = self.cootPlugin.process()
              
              if rv == CPluginScript.FAILED: 
                  self.reportStatus(rv)

          else :
              
              self.refmacPlugin = self.makeProsmartRefmacPlugin()
              self.refmacPlugin.connectSignal(self.refmacPlugin,'finished',self.refmacFinished)
              
              rv = self.refmacPlugin.process()
              
              if rv == CPluginScript.FAILED: 
                  self.reportStatus(rv)

        except Exception as e:
          print e
          self.appendErrorReport(CPluginScript,39,str(e))
 


    def cootFinished(self, statusDict={}):
        # Check coot status and start refmac
        if len(self.cootPlugin.container.outputData.XYZOUT) == 0:
            self.appendErrorReport(205,'Coot failed to produce an output file')
            self.reportStatus(CPluginScript.FAILED)
        self.checkFinishStatus(statusDict=statusDict,failedErrCode=204, outputFile= self.cootPlugin.container.outputData.XYZOUT[0],noFileErrCode=205)
        try:
          self.cootPlugin.container.outputData.XYZOUT[0].subType = 1
          self.currentCoordinates = self.cootPlugin.container.outputData.XYZOUT[0]
          
          self.refmacPlugin = self.makeProsmartRefmacPlugin()
          self.refmacPlugin.connectSignal(self.refmacPlugin,'finished',self.refmacFinished)
          rv = self.refmacPlugin.process()
          if rv == CPluginScript.FAILED: self.reportStatus(rv)
        except Exception as e:
          print e
          self.appendErrorReport(CPluginScript,39,str(e))

        
    def refmacFinished(self, statusDict={}):
        # Check refmac status and either start on next cycle with a run of buccaneer
        # or, if we have done required number of cycles, copy data from last jobs
        # to be output for the pipeline
        self.checkFinishStatus(statusDict=statusDict,failedErrCode=201, outputFile=self.refmacPlugin.container.outputData.XYZOUT ,noFileErrCode=203)

        try:
          # process XML from refmac and append to the pipeline XMLOUT
          
          # Copy xml from refmac to pipeline and write out
          self.copyRefmacXML()
          open( self.pipelinexmlfile,'w').write( etree.tostring(self.xmlroot,pretty_print=True) )
          
          # Test for interrupt or that we have done last cycle
          if self.testForInterrupt() and self.cycle<self.ncycles:
              shutil.copyfile( str(self.refmacPlugin.container.outputData.XYZOUT), str(self.container.outputData.XYZOUT.fullPath) )
              self.container.outputData.XYZOUT.annotation.set('Model built by Autobuild protein - cycle '+str( self.cycle))
              self.container.interruptStatus.LASTCYCLE = self.cycle
              self.reportStatus(CPluginScript.INTERRUPTED)

          self.cycle += 1
          
          # We need to update currentCoordinates so that the next Buccaneer wrapper iteration can get it as XYZIN
          self.currentCoordinates = self.refmacPlugin.container.outputData.XYZOUT
          
          self.rwork = self.container.outputData.PERFORMANCE.RFactor = float(self.xmlcyc.xpath('//BuildRefineCycle/RefmacResult/r_factor')[-1].text)
          self.rfree = float(self.xmlcyc.xpath('//BuildRefineCycle/RefmacResult/r_free')[-1].text)
          print 'Rwork=',self.rwork, " Rfree=",self.rfree

          print 'Finished cycle',self.cycle,'of',self.ncycles
          if (self.cycle < self.ncycles):
              self.xmlcyc = etree.SubElement(self.xmlroot,"BuildRefineCycle")
              etree.SubElement(self.xmlcyc,"Number").text = str(self.cycle+1)
              # Start the next cycle with a buccaneer run
              self.buccaneerPlugin = self.makeBuccaneerPlugin()
              self.buccaneerPlugin.connectSignal(self.buccaneerPlugin,'finished',self.buccaneerFinished)
              rv = self.buccaneerPlugin.process()
              if rv == CPluginScript.FAILED: self.reportStatus(rv)
          else:
              # The last cycle - we're nearly done! Copy final output files into the pipeline directory
              self.copyPipelineOutputFiles()
              self.setPerformanceData()
              # THE END! - Terminate the process by calling  reportStatus with SUCCEEDED flag
              self.reportStatus(CPluginScript.SUCCEEDED)

        except Exception as e:
          print e
          self.appendErrorReport(CPluginScript,39,str(e))

# =======  Methods to instantiate the subJob plugin objects and copy input data into their containers =============

    def makeBuccaneerPlugin(self,restarted=False):
        # Create an instance of buccaneer plugin and copy input file names and control parameters from the pipeline
        # container to the buccaneer plugin container
        buccaneerPlugin = self.makePluginObject('buccaneer_mr')
        
        # The input coordinates and phases come from either the pipeline input or the last (refmac) 
        # job of the last cycle or (if we are restarting interrupted job) the previous job sub-directory

        if hasattr(self,'refmacPlugin'):
            buccaneerPlugin.container.inputData.copyData(otherContainer=self.refmacPlugin.container.outputData,
                                                    dataList= (('ABCDOUT','ABCD'),) )
            
            buccaneerPlugin.container.inputData.copyData(otherContainer=self.refmacPlugin.container.outputData,
                                                    dataList= (('FPHIOUT', 'FWT_PHWT_IN'),) )

        elif restarted and os.path.exists(os.path.join(self.getSubDirectories()[-2],'ABCDOUT.mtz')):
            buccaneerPlugin.container.inputData.ABCD.set(os.path.join(self.getSubDirectories()[-2],'ABCDOUT.mtz'))
        else:
            buccaneerPlugin.container.inputData.copyData(otherContainer=self.container.inputData,
                                                    dataList= ('ABCD',))

        if self.cycle > 0:
            buccaneerPlugin.container.controlParameters.CYCLES = self.container.controlParameters.BUCCANEER_CYCLES_NEXT
        else:
            buccaneerPlugin.container.controlParameters.CYCLES = self.container.controlParameters.BUCCANEER_CYCLES

        # the other input data is the same pipleine input for all cycles

        if self.cycle > 0 and self.rwork < 0.30 :
            self.container.controlParameters.BUCCANEER_MR_MODE = 'nothing'
            
        if self.container.controlParameters.BUCCANEER_USE_FREER :
            buccaneerPlugin.container.inputData.copyData(otherContainer=self.container.inputData,
                                            dataList=  ( 'F_SIGF','FREERFLAG','SEQIN','XYZIN_SEQ', 'XYZIN_MODE',
                                                         ( 'BUCCANEER_MR_MODE_XYZIN', 'MR_MODE_XYZIN' ) , 'XYZIN' ) )
        else :
            buccaneerPlugin.container.inputData.copyData(otherContainer=self.container.inputData,
                                            dataList=  ( 'F_SIGF', 'SEQIN', 'XYZIN_SEQ', 'XYZIN_MODE',
                                                         ( 'BUCCANEER_MR_MODE_XYZIN', 'MR_MODE_XYZIN' ) , 'XYZIN' ) )
        
        buccaneerPlugin.container.controlParameters.copyData(otherContainer=self.container.controlParameters, 
                               dataList= (  ( 'BUCCANEER_ANISOTROPY_CORRECTION', 'ANISOTROPY_CORRECTION' ),
                                            ( 'BUCCANEER_BUILD_SEMET','BUILD_SEMET' ),
                                            ( 'BUCCANEER_FAST','FAST' ),
                                            ( 'BUCCANEER_FIX_POSITION', 'FIX_POSITION' ),
                                            ( 'BUCCANEER_RESOLUTION', 'RESOLUTION' ),
                                            ( 'BUCCANEER_SEQUENCE_RELIABILITY', 'SEQUENCE_RELIABILITY' ),
                                            ( 'BUCCANEER_NEW_RESIDUE_NAME', 'NEW_RESIDUE_NAME' ),
                                            ( 'BUCCANEER_MODEL_SIGMA', 'MODEL_SIGMA' ),
                                            ( 'BUCCANEER_JOBS', 'JOBS' ),
                                            ( 'BUCCANEER_VERBOSE', 'VERBOSE' ),
                                            ( 'BUCCANEER_PHSIN_TYPE', 'PHSIN_TYPE' ),
                                            ( 'BUCCANEER_MR_MODE', 'MR_MODE' ), 
                                            ( 'BUCCANEER_MR_MODE_SIGMA', 'MR_MODE_SIGMA' ),
                                              'F_SIGF_REF','ABCD_REF', 'XYZIN_REF','KNOWN_STRUCTURE' ) )
                                               
        if self.cycle > 0:
            print 'This is cycle number ',self.cycle
            # get the partially built model from the previous cycle and extend it
            buccaneerPlugin.container.inputData.XYZIN_MODE.set( True )
            buccaneerPlugin.container.inputData.XYZIN.set( self.currentCoordinates )
        
        elif self.container.inputData.XYZIN.isSet():
            buccaneerPlugin.container.inputData.XYZIN_MODE.set( True )
            buccaneerPlugin.container.inputData.XYZIN.set( self.container.inputData.XYZIN )

        return buccaneerPlugin
    
    def makeRefmacABCD(self, coordinates=None, jelly=False):
        # generate phases and figures of merit from MR model and reflection data
        refmacPlugin = self.makePluginObject('refmac')
        
        refmacPlugin.container.inputData.XYZIN.set( coordinates )
        
        refmacPlugin.container.inputData.copyData(otherContainer=self.container.inputData,dataList=('F_SIGF','FREERFLAG','DICT'))
        print 'makeRefmacABCD self',self.container.controlParameters.EXTRAREFMACKEYWORDS
        refmacPlugin.container.controlParameters.copyData(otherContainer=self.container.controlParameters,dataList=('EXTRAREFMACKEYWORDS',))
        print 'makeRefmacABCD refmacPlugin',refmacPlugin.container.controlParameters.EXTRAREFMACKEYWORDS
        refmacPlugin.container.controlParameters.HYDROGENS = 'NO'

        refmacPlugin.container.controlParameters.PHOUT = True
        refmacPlugin.container.controlParameters.USE_LOCAL_SYMMETRY = False
        
        if jelly :
            refmacPlugin.container.controlParameters.JELLY_SIGMA = 0.05
            refmacPlugin.container.controlParameters.USE_JELLY = True
            refmacPlugin.container.controlParameters.NCYCLES = 50
        else :
            refmacPlugin.container.controlParameters.NCYCLES = 10
        
        return refmacPlugin

    def makeRefmac0Plugin(self):
        # refmac wrapper run to generate 2mFo-DFc map coefficients and more accurate B-factors for coot
        refmacPlugin0 = self.makePluginObject('refmac')
        
        refmacPlugin0.container.inputData.XYZIN.set( self.currentCoordinates )
        
        if self.container.controlParameters.BUCCANEER_PHSIN_TYPE == "mr" and self.container.controlParameters.REFMAC_MR_USEPHI:
            refmacPlugin0.container.inputData.copyData(otherContainer=self.container.inputData,dataList=('F_SIGF','ABCD','FREERFLAG','DICT'))
        elif self.container.controlParameters.BUCCANEER_PHSIN_TYPE == "experimental" and self.container.controlParameters.REFMAC_EXP_USEPHI:
            refmacPlugin0.container.inputData.copyData(otherContainer=self.container.inputData,dataList=('F_SIGF','ABCD','FREERFLAG','DICT'))
        else: 
            refmacPlugin0.container.inputData.copyData(otherContainer=self.container.inputData,dataList=('F_SIGF','FREERFLAG','DICT'))

        refmacPlugin0.container.controlParameters.copyData(otherContainer=self.container.controlParameters,dataList=('EXTRAREFMACKEYWORDS',))
        refmacPlugin0.container.controlParameters.HYDROGENS = 'NO'
        refmacPlugin0.container.controlParameters.NCYCLES = 20
        refmacPlugin0.container.controlParameters.PHOUT = True

        if self.container.controlParameters.REFMAC_LOCAL_NCS and self.n_fragments < 14 :
            refmacPlugin0.container.controlParameters.USE_LOCAL_SYMMETRY = True
        else: 
            refmacPlugin0.container.controlParameters.USE_LOCAL_SYMMETRY = False


        return refmacPlugin0
    
    def makeRefmacPlugin(self):
        refmacPlugin = self.makePluginObject('refmac')
        
        refmacPlugin.container.inputData.XYZIN.set( self.currentCoordinates )

        if self.container.controlParameters.BUCCANEER_PHSIN_TYPE == "mr" and self.container.controlParameters.REFMAC_MR_USEPHI:
            refmacPlugin.container.inputData.copyData(otherContainer=self.container.inputData,dataList=('F_SIGF','ABCD','FREERFLAG','DICT'))
        elif self.container.controlParameters.BUCCANEER_PHSIN_TYPE == "experimental" and self.container.controlParameters.REFMAC_EXP_USEPHI:
            refmacPlugin.container.inputData.copyData(otherContainer=self.container.inputData,dataList=('F_SIGF','ABCD','FREERFLAG','DICT'))
        else: 
            refmacPlugin.container.inputData.copyData(otherContainer=self.container.inputData,dataList=('F_SIGF','FREERFLAG','DICT'))
            refmacPlugin.container.inputData.FREERFLAG = self.container.inputData.FREERFLAG

        refmacPlugin.container.controlParameters.copyData(otherContainer=self.container.controlParameters,dataList=('EXTRAREFMACKEYWORDS',))
        refmacPlugin.container.controlParameters.HYDROGENS = 'NO'
        refmacPlugin.container.controlParameters.NCYCLES = self.container.controlParameters.REFMAC_CYCLES
        refmacPlugin.container.controlParameters.PHOUT = True

        
        if self.rwork > r_work_essentially_complete_model :
            refmacPlugin.container.controlParameters.EXTRAREFMACKEYWORDS += '''\nsolvent NO
scal type BULK LSSC ANIS EXPE FIXBulk BBULk 200.0
'''
        else :
            refmacPlugin.container.controlParameters.EXTRAREFMACKEYWORDS += '''\nsolvent yes VDWProb 1.4 IONProb 0.8 RSHRink 0.8
scal type SIMPLE LSSC ANIS EXPE
'''
        
        if self.container.controlParameters.REFMAC_LOCAL_NCS and self.n_fragments < 14 :
            refmacPlugin.container.controlParameters.USE_LOCAL_SYMMETRY = True
        else: 
            refmacPlugin.container.controlParameters.USE_LOCAL_SYMMETRY = False

        return refmacPlugin


    def makeProsmartRefmacPlugin(self):
        # Create an instance of the prosmart-refmac pipeline
        refmacPlugin = self.makePluginObject('prosmart_refmac')

        # coordinates are those saved from preceeding coot run or buccaneer run
        refmacPlugin.container.inputData.XYZIN.set(  self.currentCoordinates )

        # all other data from the input to the pipeline
        
        if self.container.controlParameters.BUCCANEER_PHSIN_TYPE == "mr" and self.container.controlParameters.REFMAC_MR_USEPHI:
            refmacPlugin.container.inputData.copyData(otherContainer=self.container.inputData,dataList=('F_SIGF','ABCD','FREERFLAG','TARGET','DICT'))
        elif self.rwork > self.container.controlParameters.BUCCANEER_MLHL_RWORK_LIMIT and self.container.controlParameters.BUCCANEER_PHSIN_TYPE == "experimental" and self.container.controlParameters.REFMAC_EXP_USEPHI:
            refmacPlugin.container.inputData.copyData(otherContainer=self.container.inputData,dataList=('F_SIGF','ABCD','FREERFLAG','TARGET','DICT'))
        else: 
            refmacPlugin.container.inputData.copyData(otherContainer=self.container.inputData,dataList=('F_SIGF','FREERFLAG','TARGET','DICT'))
            refmacPlugin.container.inputData.FREERFLAG = self.container.inputData.FREERFLAG
        
        if self.container.inputData.TARGET.isSet(): 
            refmacPlugin.container.inputData.TARGET = self.container.inputData.TARGET

        refmacPlugin.container.controlParameters.copyData(otherContainer=self.container.controlParameters,dataList=('EXTRAREFMACKEYWORDS',))
        refmacPlugin.container.controlParameters.HYDROGENS = 'NO'
        refmacPlugin.container.controlParameters.MAKE_NEW_LIGAND_EXIT.set(False)
        refmacPlugin.container.controlParameters.NCYCLES = self.container.controlParameters.REFMAC_CYCLES
        refmacPlugin.container.controlParameters.PHOUT = True
        
        if self.rwork > r_work_essentially_complete_model :
            refmacPlugin.container.controlParameters.EXTRAREFMACKEYWORDS += '''\nsolvent NO
scal type BULK LSSC ANIS EXPE FIXBulk BBULk 200.0 
'''
        else :
            refmacPlugin.container.controlParameters.EXTRAREFMACKEYWORDS += '''\nsolvent yes VDWProb 1.4 IONProb 0.8 RSHRink 0.8
scal type SIMPLE LSSC ANIS EXPE
'''

        if self.container.controlParameters.REFMAC_LOCAL_NCS and self.n_fragments < 14 :
            refmacPlugin.container.controlParameters.USE_LOCAL_SYMMETRY = True
        else: 
            refmacPlugin.container.controlParameters.USE_LOCAL_SYMMETRY = False

        return refmacPlugin
    
    def makeCootPlugin(self):
        cootPlugin = self.makePluginObject('coot_script_lines')
        xyzinList = cootPlugin.container.inputData.XYZIN
        xyzinList.append(xyzinList.makeItem())
        xyzinList[-1].set(self.currentCoordinates)
        fphiinList = cootPlugin.container.inputData.FPHIIN
        fphiinList.append(fphiinList.makeItem())
        fphiinList[-1].set(self.refmacPlugin0.container.outputData.FPHIOUT)
        #coot_stepped_refine,coot_fit_residues,coot_script_lines
        rso = self.container.controlParameters.COOT_REALSPACE_OPERATION.__str__()
        if rso == "coot_script_lines":
            cootPlugin.container.controlParameters.SCRIPT = self.container.controlParameters.SCRIPT
        elif rso == "coot_fit_residues":
            cootPlugin.container.controlParameters.SCRIPT = '''fill_partial_residues(MolHandle_1)
fit_protein(MolHandle_1)
write_pdb_file(MolHandle_1,os.path.join(dropDir,"output.pdb"))
'''
        elif rso == "coot_stepped_refine":
            if self.container.controlParameters.USERAMA.isSet() and self.container.controlParameters.USERAMA:
                cootPlugin.container.controlParameters.SCRIPT = '''fill_partial_residues(MolHandle_1)
stepped_refine_protein_for_rama(MolHandle_1)
write_pdb_file(MolHandle_1,os.path.join(dropDir,"output.pdb"))
'''
            else:
                cootPlugin.container.controlParameters.SCRIPT = '''fill_partial_residues(MolHandle_1)
stepped_refine_protein(MolHandle_1)
write_pdb_file(MolHandle_1,os.path.join(dropDir,"output.pdb"))
'''
        elif rso == "coot_add_waters":
            cootPlugin.container.controlParameters.SCRIPT = '''set_ligand_water_to_protein_distance_limits(2.2, 3.3)
execute_find_waters_real(MapHandle_1,MolHandle_1,0,1.3)
write_pdb_file(MolHandle_1,os.path.join(dropDir,"output.pdb"))
'''
        elif rso == "none":
            cootPlugin.container.controlParameters.SCRIPT = '''write_pdb_file(MolHandle_1,os.path.join(dropDir,"output.pdb"))
'''
        return cootPlugin

# ===== Utility methods ========================================================================================

    def initialiseXML(self,restarted=False):
        # Set up the xml output
        self.pipelinexmlfile = self.makeFileName(format='PROGRAMXML')
        if restarted and os.path.exists(self.pipelinexmlfile):
           try:
             f = open(self.pipelinexmlfile)
             self.xmlroot = etree.fromstring(f.read())
             f.close()
           except:
             self.xmlroot = etree.Element("BuccaneerBuildRefineResult")
        else:
           self.xmlroot = etree.Element("BuccaneerBuildRefineResult")
        self.xmlcyc = etree.SubElement(self.xmlroot,"BuildRefineCycle")
        etree.SubElement(self.xmlcyc,"Number").text = str(self.cycle+1)
        

    def copyRefmacXML(self):
        rxml = CCP4Utils.openFileToEtree(self.refmacPlugin.makeFileName('PROGRAMXML'))
        rstats = rxml.xpath("//REFMAC/Overall_stats/stats_vs_cycle")
        #print 'refmac xml rstats',len(rstats)
        if len(rstats)>0:
            refele = etree.Element('RefmacResult')
            for node in rstats[0].xpath("new_cycle[last()]/r_factor | new_cycle[last()]/r_free | new_cycle[last()]/rmsBOND |  new_cycle[last()]/rmsANGLE"):
                node.text = str(node.text).strip()
                if node.tag == 'rmsBOND':
                    node.text = str(100*float(node.text))
                    node.tag='rmsBONDx100'
                #print 'refmac xml',etree.tostring(node,pretty_print=True)
                refele.append( deepcopy( node ) )
            self.xmlcyc.append(refele)
        self.xmlroot.append(self.xmlcyc)


    def accessRefmac0XML(self) :
        
        r0xml = CCP4Utils.openFileToEtree ( self.refmacPlugin0.makeFileName ( 'PROGRAMXML' ) )
        statistics = r0xml.xpath ( "//REFMAC/Overall_stats/stats_vs_cycle " )

        rwork_tmp = 1.0
        rfree_tmp = 1.0

        if len ( statistics ) > 0 :
            print "We have stats"
            rwork_tmp = float ( statistics[0].xpath ( "new_cycle[last()]/r_factor" )[0].text )
            print "assignment does not crash"
            rfree_tmp = float ( statistics[0].xpath ( "new_cycle[last()]/r_free"   )[0].text )

        #print "Rwork: ", self.rwork, "Rfree: ", self.rfree

        if rwork_tmp > 0 and rwork_tmp < 0.7 :
            self.rwork = rwork_tmp 
            print "New Rwork: ", self.rwork
        if rfree_tmp > 0 and rfree_tmp < 0.7 :
            self.rfree = rfree_tmp
            print "New Rfree: ", self.rfree


    def copyBuccaneerXML(self):
        xmlFile = str(self.buccaneerPlugin.makeFileName('PROGRAMXML'))
        if os.path.exists(xmlFile):
            bxml = CCP4Utils.openFileToEtree(xmlFile)
            self.xmlcyc.append(bxml)


    def checkFinishStatus( self,statusDict,failedErrCode,outputFile = None,noFileErrCode= None):       
        if len(statusDict)>0 and statusDict['finishStatus'] == CPluginScript.FAILED:
            self.appendErrorReport(failedErrCode)
            self.reportStatus(statusDict['finishStatus'])
        if outputFile is not None and not outputFile.exists():
            self.appendErrorReport(noFileErrCode,'Expected file: '+str(outputFile))
            self.reportStatus(CPluginScript.FAILED)
        return

    def copyPipelineOutputFiles(self):
        # CCP4i2 has strict conventions for the path and filename of output files. The pipeline output file names
        # (i.e. self.container.outputData.XXXXOUT) have been set automatically.  We need to physically copy refmac
        # output files to the right place for them to be recognised as the pipeline output.
        shutil.copyfile( str(self.refmacPlugin.container.outputData.XYZOUT), str(self.container.outputData.XYZOUT.fullPath) )
        shutil.copyfile( str(self.refmacPlugin.container.outputData.FPHIOUT), str(self.container.outputData.FPHIOUT.fullPath) )
        shutil.copyfile( str(self.refmacPlugin.container.outputData.DIFFPHIOUT), str(self.container.outputData.DIFFPHIOUT.fullPath) )
        shutil.copyfile( str(self.refmacPlugin.container.outputData.ABCDOUT), str(self.container.outputData.ABCDOUT.fullPath) )

        # Tag the output data objects with the subType and annotation that we know
        self.container.outputData.XYZOUT.annotation.set('Model built by Autobuild protein')
        self.container.outputData.XYZOUT.subType=1
        self.container.outputData.FPHIOUT.subType=1
        self.container.outputData.FPHIOUT.annotation.set('2mFo-DFc map coefficients')
        self.container.outputData.DIFFPHIOUT.annotation.set('mFo-DFc map coefficients')
        self.container.outputData.DIFFPHIOUT.subType=2
        
        return

    def setPerformanceData(self):
    # Set the outputData.PERFORMANCE data by extracting from self.xmlcyc (the XML output for the latest cycle)
        #print 'setPerformanceData',etree.tostring(self.xmlcyc, pretty_print=True)
        self.rwork = self.container.outputData.PERFORMANCE.RFactor = float(self.xmlcyc.xpath('//BuildRefineCycle/RefmacResult/r_factor')[-1].text)
        self.rfree = float(self.xmlcyc.xpath('//BuildRefineCycle/RefmacResult/r_free')[-1].text)
        print 'Rwork=',self.rwork, " Rfree=",self.rfree
        self.container.outputData.PERFORMANCE.completeness = float(self.xmlcyc.xpath('//BuildRefineCycle/BuccaneerResult/Final/CompletenessByResiduesBuilt')[-1].text)
        
        return

#=======================================================================================================
import unittest

class test_buccaneer_build_refine_mr(unittest.TestCase):

  def setUp(self):
    # make all background jobs wait for completion
    from CCP4Modules import QTAPPLICATION,PROCESSMANAGER
    self.app = QTAPPLICATION()
    PROCESSMANAGER().setWaitForFinished(10000)

  def tearDown(self):
    from CCP4Modules import PROCESSMANAGER
    PROCESSMANAGER().setWaitForFinished(-1)

  def test_1(self):
    from CCP4Modules import QTAPPLICATION
    import os
    from CCP4Utils import getCCP4I2Dir

    # Run the pipeline
    wrapper = buccaneer_build_refine_mr(parent=QTAPPLICATION(),name='buccaneer_build_refine_mr')
    wrapper.container.loadDataFromXml(os.path.join(getCCP4I2Dir(),'pipelines','buccaneer_build_refine_mr','test_data','test_1.params.xml'))
    # Ensure no output file exists already
    xyzout = wrapper.container.outputData.XYZOUT.fullPath.get()
    if xyzout is not None and os.path.exists(xyzout): os.remove(xyzout)
    xmlout = wrapper.makeFileName('PROGRAMXML')
    if xmlout is not None and os.path.exists(xmlout): os.remove(xmlout)
    pid = wrapper.process()

    #test if output file created
    self.assertEqual(os.path.exists( xyzout),1,'Failed to create copied pdb file '+xyzout)                             


def TESTSUITE():
  suite = unittest.TestLoader().loadTestsFromTestCase(test_buccaneer_build_refine_mr)
  return suite

def testModule():
  suite = TESTSUITE()
  unittest.TextTestRunner(verbosity=2).run(suite)
