
from CCP4PluginScript import CPluginScript
import sys, os
import CCP4ErrorHandling
import CCP4Modules
from lxml import etree
import CCP4Utils
  
class phaser_pipeline(CPluginScript):

    TASKNAME = 'phaser_pipeline'                                  # Task name - should be same as class name
    TASKCOMMAND = ''                                     # The command to run the executable
    TASKVERSION= 0.0                                     # Version of this plugin
    COMTEMPLATE = None                                   # The program com file template
    COMTEMPLATEFILE = None                               # Name of file containing com file template
    ASYNCHRONOUS = False
    PERFORMANCECLASS = 'CRefinementPerformance'
    SEPARATEDATA=True
    INTERRUPTABLE=True

    ERROR_CODES = {  200 : { 'description' : 'Phaser exited with error statut' }, 202 : { 'description' : 'Failed in harvest operation' }, 203 : { 'description' : 'Columns not present' }, 204 : { 'description' : 'Failed in plugin:' },}
    WHATNEXT = ['prosmart_refmac','buccaneer_build_refine_mr','coot_rebuild']
    

    '''
    def __init__(self,parent=None,name=None,workDirectory=''):
      CPluginScript. __init__(self,parent=parent,name=name)
    '''
    
    def process(self):
        invalidFiles = self.checkInputData()
        if len(invalidFiles)>0:
            self.reportStatus(CPluginScript.FAILED)        
        self.checkOutputData()

        self.xmlroot = etree.Element('PhaserPipeline')

        self.phaserPlugin = self.makePluginObject('phaser_MR_AUTO')
        
        #This funky arrangement is the way to ensure that the plugin behaves the same
        #when it is a part of the ipeline as it does when it is run alone...something about defaults I guess
        for attrName in self.phaserPlugin.container.keywords.dataOrder():
            if hasattr(self.container.keywords,attrName):
                attr = getattr(self.container.keywords,attrName)
                if hasattr(attr,'isSet') and attr.isSet():
                    setattr(self.phaserPlugin.container.keywords,attrName,attr)
        self.phaserPlugin.container.inputData=self.container.inputData
        self.phaserPlugin.container.inputData.KILLFILEPATH.set(os.path.join(self.getWorkDirectory(),'INTERRUPT'))
        self.phaserPlugin.async = False
        #self.phaserPlugin.waitForFinished = -1
        #self.phaserPlugin.setFinishHandler(self.phaser_MR_AUTO_Finished)
        self.connectSignal(self.phaserPlugin,'finished', self.phaserFinished)
        self.oldXMLLength = 0
        self.phaserPlugin.callbackObject.addResponder(self.phaserXMLUpdated)
        print 'Off to see the wizard'
        rv = self.phaserPlugin.process()
        print 'Rv', rv
        if rv == CPluginScript.FAILED:
            print 'Oh'
            with open(self.phaserPlugin.makeFileName('LOG'),"r") as logFile:
                wasInterrupted = 'KILL-FILE DETECTED ERROR' in logFile.read()
                print 'Was interrupted',wasInterrupted
                if wasInterrupted:
                    self.reportStatus('CPluginScript.INTERRUPTED')
                    return CPluginScript.INTERRUPTED
                else:
                    self.reportStatus(rv)
                    return CPluginScript.FAILED
        return CPluginScript.SUCCEEDED

    def phaserXMLUpdated(self, newXML):
        for oldNode in self.xmlroot.xpath('PhaserMrResults'): self.xmlroot.remove(oldNode)
        from copy import deepcopy
        self.xmlroot.append(deepcopy(newXML))
        tmpFilename = self.makeFileName('PROGRAMXML')+'_tmp'
        with open(tmpFilename,'w') as xmlfile: xmlfile.write(etree.tostring(self.xmlroot,pretty_print=True))
        import os
        self.renameFile(tmpFilename,self.makeFileName('PROGRAMXML'))

    def phaserFinished(self, statusDict = {}):
        import sys
        sys.stdout.flush()
        print 'StatusDict',statusDict
        sys.stdout.flush()
        if len(self.phaserPlugin.container.outputData.XYZOUT) > 0:
            self.checkFinishStatus(statusDict=statusDict,failedErrCode=200,outputFile = self.phaserPlugin.container.outputData.XYZOUT[0] ,noFileErrCode=207)
        else:
            self.appendErrorReport(207,'No output files in list')
            self.reportStatus(CPluginScript.FAILED)
        self.harvestPhaserPlugin()
        self.appendXML(self.phaserPlugin.makeFileName('PROGRAMXML'),'PhaserMrResults')
        F_SIGF_TOUSE = self.container.inputData.F_SIGF
        FREERFLAG_TOUSE = self.container.inputData.FREERFLAG
        XYZIN_TOUSE = self.container.outputData.XYZOUT[0]
        
        if self.phaserPlugin.container.outputData.dataReindexed:
            self.runPointless()
            F_SIGF_TOUSE = self.container.outputData.F_SIGF_OUT
            FREERFLAG_TOUSE = self.container.outputData.FREERFLAG_OUT
        
        if self.container.inputData.XYZIN_TARGET.isSet():
            self.runCsymmatch()
            XYZIN_TOUSE = self.container.outputData.XYZOUT_CSYMMATCH

        if self.container.inputData.RUNCOOT:
            self.runCoot(MAPIN=self.container.outputData.MAPOUT[0], XYZIN=XYZIN_TOUSE)
            XYZIN_TOUSE = self.container.outputData.XYZOUT_COOT
        
        if self.container.inputData.RUNREFMAC:
            self.runRefmac(F_SIGF=F_SIGF_TOUSE, FREERFLAG=FREERFLAG_TOUSE, XYZIN=XYZIN_TOUSE)

        self.reportStatus(CPluginScript.SUCCEEDED)

    def harvestPhaserPlugin(self):
        pluginOutputs=self.phaserPlugin.container.outputData
        pipelineOutputs = self.container.outputData
        self.harvestFile(pluginOutputs.SOLOUT, pipelineOutputs.SOLOUT)
        for outputListType in ['XYZOUT', 'MAPOUT', 'DIFMAPOUT','PHASEOUT']:
            pluginOutputList = getattr(pluginOutputs, outputListType, None)
            pipelineOutputList = getattr(pipelineOutputs, outputListType, None)
            self.harvestList(pluginOutputList, pipelineOutputList)

    def runPointless(self):
        try:
            pointlessPlugin = self.makePluginObject('pointless_reindexToMatch')
            pointInp = pointlessPlugin.container.inputData
            pointlessPlugin.container.controlParameters.REFERENCE = 'HKLIN_FMAP_REF'
            pointInp.HKLIN_FMAP_REF = self.container.outputData.MAPOUT[0]
            pointInp.F_SIGF = self.container.inputData.F_SIGF
            if self.container.inputData.FREERFLAG.isSet():
                pointInp.FREERFLAG = self.container.inputData.FREERFLAG
            rv = pointlessPlugin.process()
            if rv != CPluginScript.SUCCEEDED: self.reportStatus(rv)
            
            pluginOutputs = pointlessPlugin.container.outputData
            pipelineOutputs = self.container.outputData
            
            self.harvestFile(pluginOutputs.F_SIGF_OUT, pipelineOutputs.F_SIGF_OUT)
            if self.container.inputData.FREERFLAG.isSet():
                self.harvestFile(pluginOutputs.FREERFLAG_OUT, pipelineOutputs.FREERFLAG_OUT)
        except:
            self.appendErrorReport(204,'pointless_reindexToMatch')
            self.reportStatus(CPluginScript.FAILED)
        return CPluginScript.SUCCEEDED

    def runCsymmatch(self):
        try:
            csymmatchPlugin = self.makePluginObject('csymmatch')
            csymmatchInp = csymmatchPlugin.container.inputData
            csymmatchInp.XYZIN_QUERY = self.container.outputData.XYZOUT[0]
            csymmatchInp.XYZIN_TARGET = self.container.inputData.XYZIN_TARGET
            rv = csymmatchPlugin.process()
            if rv != CPluginScript.SUCCEEDED: self.reportStatus(rv)
            
            pluginOutputs = csymmatchPlugin.container.outputData
            pipelineOutputs = self.container.outputData

            self.harvestFile(pluginOutputs.XYZOUT, pipelineOutputs.XYZOUT_CSYMMATCH)
            self.appendXML(csymmatchPlugin.makeFileName('PROGRAMXML'),'Csymmatch')
            pipelineOutputs.XYZOUT_CSYMMATCH.annotation='Coordinates moved to match reference structure'
        except:
            self.appendErrorReport(204,'csymmatch')
            self.reportStatus(CPluginScript.FAILED)
        return CPluginScript.SUCCEEDED

    def runCoot(self, MAPIN=None, XYZIN=None):
        try:
            try:
                cootPlugin = self.makePluginObject('coot_script_lines')
                xyzinList = cootPlugin.container.inputData.XYZIN
                xyzinList.append(xyzinList.makeItem())
                xyzinList[-1].set(XYZIN)
                fphiinList = cootPlugin.container.inputData.FPHIIN
                fphiinList.append(fphiinList.makeItem())
                fphiinList[-1].set(MAPIN)
                cootPlugin.container.controlParameters.SCRIPT = '''fill_partial_residues(MolHandle_1)
fit_protein(MolHandle_1)
write_pdb_file(MolHandle_1,os.path.join(dropDir,"output.pdb"))
'''
            except:
                self.appendErrorReport(204,'coot_script_lines -setup')
                self.reportStatus(CPluginScript.FAILED)
            try:
                cootPlugin.async=False
                rv = cootPlugin.process()
                if rv != CPluginScript.SUCCEEDED: self.reportStatus(rv)
            except:
                self.appendErrorReport(204,'coot_script_lines -execute')
                self.reportStatus(CPluginScript.FAILED)
            try:
                pluginOutputs = cootPlugin.container.outputData
                pipelineOutputs = self.container.outputData

                self.harvestFile(pluginOutputs.XYZOUT[0], pipelineOutputs.XYZOUT_COOT)
                self.appendXML(cootPlugin.makeFileName('PROGRAMXML'),'coot_script_lines')
                pipelineOutputs.XYZOUT_COOT.annotation='Coordinates filled and fitted by COOT'
            except:
                self.appendErrorReport(204,'coot_script_lines -postprocess')
                self.reportStatus(CPluginScript.FAILED)
        except:
            self.appendErrorReport(204,'coot_script_lines')
            self.reportStatus(CPluginScript.FAILED)
        return CPluginScript.SUCCEEDED

    def runRefmac(self,F_SIGF=None,FREERFLAG=None,XYZIN=None):
        try:
            # refmac wrapper run with 10 cycles
            self.refmacPlugin = self.makePluginObject('refmac')
            if XYZIN is not None: self.refmacPlugin.container.inputData.XYZIN.set( XYZIN)
            if F_SIGF is not None: self.refmacPlugin.container.inputData.F_SIGF.set(F_SIGF)
            if FREERFLAG is not None: self.refmacPlugin.container.inputData.FREERFLAG.set(FREERFLAG)
            self.refmacPlugin.container.controlParameters.HYDROGENS = 'NO'
            self.refmacPlugin.container.controlParameters.NCYCLES = 10
            self.refmacPlugin.container.controlParameters.PHOUT = False
            self.refmacPlugin.container.controlParameters.USE_JELLY = True
            self.refmacPlugin.container.controlParameters.JELLY_SIGMA=0.05
            self.refmacPlugin.container.controlParameters.MAKE_NEW_LIGAND_EXIT = False
            self.refmacPlugin.async=False
            rv = self.refmacPlugin.process()
            if rv == CPluginScript.FAILED: self.reportStatus(rv)
            
            pluginOutputs=self.refmacPlugin.container.outputData
            pipelineOutputs = self.container.outputData
            self.harvestFile(pluginOutputs.FPHIOUT, pipelineOutputs.MAPOUT_REFMAC)
            self.harvestFile(pluginOutputs.DIFFPHIOUT, pipelineOutputs.DIFMAPOUT_REFMAC)
            self.harvestFile(pluginOutputs.XYZOUT, pipelineOutputs.XYZOUT_REFMAC)
            
            self.appendXML(self.refmacPlugin.makeFileName('PROGRAMXML'),'REFMAC')
        except:
            self.appendErrorReport(204,'refmac Preamble')
            self.reportStatus(CPluginScript.FAILED)
        try:
            self.container.outputData.PERFORMANCEINDICATOR = self.refmacPlugin.container.outputData.PERFORMANCEINDICATOR
        except:
            self.appendErrorReport(204,'refmac PI')
            self.reportStatus(CPluginScript.FAILED)
        return CPluginScript.SUCCEEDED

    def harvestList(self, pluginOutputList, pipelineOutputList):
        for pluginOutputListItem in pluginOutputList:
            pipelineOutputList.append(pipelineOutputList.makeItem())
            pipelineOutputListItem = pipelineOutputList[-1]
            pipelineOutputListItem.fullPath = os.path.join(self.workDirectory,os.path.basename(pluginOutputListItem.fullPath))
            self.harvestFile(pluginOutputListItem, pipelineOutputListItem)

    def harvestFile(self, pluginOutputItem, pipelineOutputItem):
        import shutil
        try:
            shutil.copyfile(str(pluginOutputItem.fullPath), str(pipelineOutputItem.fullPath))
            pipelineOutputItem.annotation = pluginOutputItem.annotation
            pipelineOutputItem.contentFlag = pluginOutputItem.contentFlag
            pipelineOutputItem.subType = pluginOutputItem.subType
        except:
            self.appendErrorReport(202,str(pluginOutputItem.fullPath)+' '+str(pipelineOutputItem.fullPath))
            self.reportStatus(CPluginScript.FAILED)

    def appendXML(self, changedFile, replacingElementOfType=None):
        newXML = CCP4Utils.openFileToEtree(changedFile)
        destNode = self.xmlroot
        for oldNode in self.xmlroot.xpath(replacingElementOfType):
            self.xmlroot.remove(oldNode)
        destNode.append(newXML)
        with open(self.makeFileName('PROGRAMXML'),'w') as xmlfile:
            xmlfile.write(etree.tostring(self.xmlroot,pretty_print=True))

    def checkFinishStatus( self, statusDict,failedErrCode,outputFile = None,noFileErrCode= None):
        import os
        if len(statusDict)>0 and statusDict['finishStatus'] == CPluginScript.FAILED:
            self.appendErrorReport(failedErrCode)
            self.reportStatus(statusDict['finishStatus'])
        try:
            assert outputFile.exists(),'Entity provided is not CDataFile or does not exist'
        except:
            self.appendErrorReport(noFileErrCode,'Expected file: '+str(outputFile))
            self.reportStatus(CPluginScript.FAILED)

