from CCP4ReportParser import *
import sys
from lxml import etree

import refmac_report

class prosmart_refmac_report(Report):
    # Specify which gui task and/or pluginscript this applies to
    TASKNAME = 'prosmart_refmac'
    RUNNING = True
    SEPARATEDATA=True
    
    def __init__(self,*args,**kw):
        super(prosmart_refmac_report,self).__init__(*args,**kw)
        #print 'prosmart_refmac_report',self,self.jobStatus
        self.outputXml = self.jobStatus is not None and self.jobStatus.lower().count('running')
        if self.jobStatus is not None and not self.jobStatus.lower().count('running'): self.outputXml = False
        if self.jobStatus is not None and self.jobStatus.lower() == 'nooutput': return
        if self.jobStatus is not None and self.jobStatus.lower() == 'unsatisfactory':
            self.drawUnsatisfactory(self)
        else:
            self.drawContents()

    def drawUnsatisfactory(self, parent=None):
        #Currently, the only "unsatisfactory" outcome is dur to ligand encountered whicl "make ligand exit" active
        if parent is None: parent=self
        parent.addDiv(style='clear:both;')
        parent.addText(text='Refmac died because it encountered a ligand not present in the monomer libraries it has read (i.e. the default CCP4 libraries plus any library you provided through the GUI). To continue, you have a number of options.')
        #parent.append('<br/>')
        #parent.addText(text='1) Review and accept the ligand dictionary autogenerated by refmac: if the following sketch , and the data presented by viewing the file "Pictures of ligand prepared by refmac" (see below)  suggest that the ligand has been appropriately characterised, then you can use the autogenerated library in a subsequent refmac job')
        parent.append('<br/>')
        parent.addText(text='1) Use the make ligand pipeline to generate an acedrg description of your ligand.  For now, the resulting ligand will need to be rebuilt into your model through Coot, since atom names may not match those in the original PDB file')
        parent.append('<br/>')
        parent.addText(text='2) View the input PDB file in Coot, and use COOT  to generate a ligand geometry dictionary.  This is a neat way to work and keep atom names consistent with the starting point.  Ligands generated through the coot ligand builder will be captured into the ccp4i2 database and can be exported from there on the coot task report page.')
        try:
            if len(self.xmlnode.xpath('//svg')) > 0:
                sketchDiv = parent.addDiv(style="width:350px; height:350px; border:1px solid black;")
                sketchDiv.append(etree.tostring(self.xmlnode.xpath('//svg')[0],pretty_print=True))
        except:
            pass

    def drawContents(self):
        xmlnode = self.xmlnode
        self.addDiv(style='clear:both;')
        #Found ligands
        xmlPath = '//LIGANDS'
        xmlNodes = xmlnode.xpath(xmlPath)
        if len(xmlNodes)>0:
          clearingDiv = self.addDiv(style="clear:both;")
          ligandFold = self.addFold(label='New ligand(s) found',brief='Ligand')
          text = '<span style="font-size:110%">Geometry restaints for the following novel ligands have been added to the project ligand geometry file:'
          ligandNodes = xmlNodes[0].xpath('ligand')
          for lig in ligandNodes:
            text = text + ' ' + str(lig.text)
          ligandFold.append(text + '</span>')
          
        #PROSMART results (if any)
        xmlPath = '//PROSMART'
        xmlNodes = xmlnode.xpath(xmlPath)
        if len(xmlNodes)>0:
            clearingDiv = self.addDiv(style="clear:both;")
            prosmartFold = self.addFold(label='Prosmart results',brief='Prosmart')
            prosmartFold.append('<span style="font-size:110%">HTML Results will be displayed in browser </span>')
            prosmartFold.append('<a href="job_1/ProSMART_Results.html">Open Results</a>')
        
        #ERRORLINES if any
        errorLineNodes = xmlnode.xpath('//ErrorLine')
        if len(errorLineNodes)>0:
            errorFold = self.addFold(label='Refmac reported errors', initiallyOpen=True,brief='Errors')
            errorFoldDiv = errorFold.addDiv()
            for errorLineNode in errorLineNodes:
                errorFoldDiv.addText(text = errorLineNode.text)
                errorFoldDiv.append('<br/>')
        
        #Raid the refmac report of finished jobs
        summaryFold = self.addFold(label='Refinement', initiallyOpen=True,brief='Default weight')
        clearingDiv = self.addDiv(style="clear:both;")
        try:
            weightText = self.xmlnode.xpath('//WeightUsed')[-1].text
            self.addPre(outputXml=self.outputXml, internalId="WeightUsed", text="Current weight applied to X-ray term is "+weightText,style="font-size:125%;")
        except:
            weightText = ""

        refmacReport = None
        try:
            refmacReportNode0 =xmlnode.xpath('//RefmacOptimiseWeight/RefmacWeight/REFMAC')[0]
        except:
            try:
                refmacReportNode0 =xmlnode.xpath('//RefmacOptimiseWeight/RefmacInProgress')[0]
            except:
                pass
        if refmacReportNode0 is not None:
            refmacReport = refmac_report.refmac_report(xmlnode=refmacReportNode0, jobStatus='nooutput', jobInfo=self.jobInfo)
        
        topElementsDiv = summaryFold.addDiv(style='width:800px; height:270px;overflow:auto;')
        if refmacReport is not None and not self.jobStatus.lower().count('running'):
            refmacReport.addScrollableDownloadableTable1(parent=topElementsDiv)
        else:
            self.addProgressTable(topElementsDiv, xmlnode)
        self.addProgressGraph(topElementsDiv, xmlnode)
        
        if refmacReport is not None: refmacReport.addTwinningAnalysis(self)

        refmacWeights = xmlnode.xpath('//RefmacOptimiseWeight/RefmacWeight')
        if len(refmacWeights) > 1:
            self.addByWeightResults(self, xmlnode)

        if not self.jobStatus.lower().count('running'):
            clearingDiv = self.addDiv(style="clear:both;")
            refmacReport.addTables(parent=self)
            if self.jobStatus.lower() != 'subjob':
                objectMap = {}
                #Use DICTOUT or DICTIN if they have been harvested to define monomer geometry in pictures
                if self.jobInfo['filenames'].get('DICTOUT', None) is not None:
                    objectMap['DICT'] = 'DICTOUT'
                elif self.jobInfo['filenames'].get('DICT', None) is not None:
                    objectMap['DICT'] = 'DICT'
                refmacReport.addRefinementPictures(jobInfo=self.jobInfo, parent=self, objectNameMap=objectMap)
            refmacReport.addSymmetryAnalysis(self)
            refmacReport.addOutlierAnalysis(self)
            refmacReport.addSmartieGraphs(self)

    def addProgressGraph(self, parent, xmlnode):
        if self.xmlnode.haspath("//RefmacInProgress"):
            progressGraph = parent.addFlotGraph(title="Running refmac",select="//RefmacOptimiseWeight/RefmacInProgress/Cycle",style="height:250px; width:400px;float:left;border:0px;",outputXml=self.outputXml,internalId="SummaryGraph")
            progressGraph.addData(title="Cycle",    select="number")
            progressGraph.addData(title="R_Factor", select="r_factor")
            progressGraph.addData(title="R_Free",  select="r_free")
            plot = progressGraph.addPlotObject()
            plot.append('title','Running refmac R-factors')
            plot.append('plottype','xy')
            plot.append('yrange', rightaxis='false')
            plot.append( 'xlabel', 'Cycle' )
            plot.append( 'xintegral', 'true' )
            plot.append( 'ylabel', 'R-factor' )
            plot.append( 'rylabel', 'Geometry' )
            for coordinate, colour in [(2,'blue'),(3,'green')]:
                plotLine = plot.append('plotline',xcol=1,ycol=coordinate,rightaxis='false',colour=colour)
            
            rmsBonds = self.xmlnode.xpath('//RefmacInProgress/Cycle/rmsBonds')
            if len(rmsBonds)> 0:
                plot.append('yrange', rightaxis='true')
                cycleNodes = self.xmlnode.xpath('//RefmacInProgress/Cycle')
                data = []
                for cycleNode in cycleNodes:
                    try: data.append(cycleNode.xpath('rmsBonds')[0].text)
                    except: data.append(None)
                progressGraph.addData(title="rmsBonds",  data=data)
                plotLine = plot.append('plotline',xcol=1,ycol=4,rightaxis='true',colour='red')

    def addProgressTable(self, parent, xmlnode):
        progressTableDiv = parent.addDiv(style='border:0px solid black; height:250px; width:260px; float:left; margin-top:1px; margin-right:0px;overflow:auto;')
        if xmlnode.haspath("//RefmacInProgress"):
            xmlPath = '//RefmacOptimiseWeight/RefmacInProgress/Cycle'
            xmlNodes = xmlnode.xpath(xmlPath)
            nCycles = len(xmlNodes)
            if len(xmlNodes)>0:
                selectString = "//RefmacOptimiseWeight/RefmacInProgress/Cycle[1] "
                if len(xmlNodes)>2:
                    selectString += " | //RefmacOptimiseWeight/RefmacInProgress/Cycle[%d]" % (nCycles-1)
                if len(xmlNodes)>1:
                    selectString += " | //RefmacOptimiseWeight/RefmacInProgress/Cycle[%d]" % nCycles
                progressTable = progressTableDiv.addTable(select=selectString, style="height:250px; width:260px;float:left;", outputXml=self.outputXml, internalId='SummaryTable')
                progressTable.addData(title="Cycle", select="number")
                progressTable.addData(title="R-factor", select="r_factor")
                progressTable.addData(title="R-free",   select="r_free",   expr="x if float(x)>=0.0 else '-'")
        
                rmsBonds = self.xmlnode.xpath('//RefmacInProgress/Cycle/rmsBonds')
                if len(rmsBonds)> 0:
                    cycleNodes = self.xmlnode.xpath('//RefmacInProgress/Cycle')
                    data = []
                    for iCycleNode, cycleNode in enumerate(cycleNodes):
                        if iCycleNode == 0 or iCycleNode == nCycles-2 or iCycleNode == nCycles-1:
                            try: data.append(cycleNode.xpath('rmsBonds')[0].text)
                            except: data.append('-')
                    progressTable.addData(title="RMS Deviation", subtitle="Bond", data=data)

    def addByWeightResults(self, parent, xmlnode):
        if parent is None: parent = self
        if xmlnode.haspath("//REFMAC"):
            #I *do not know* why This is needed
            clearingDiv = parent.addDiv(style="clear:both;")
            byWeightsFold = parent.addFold(label="Results by weight",initiallyOpen=True,brief='By weight')

            #Sort refmacweight nodes in order of increasing weight parameter
            refmacWeightNodes = list(self.xmlnode.xpath("//RefmacWeight"))
            sortedRefmacNodes = sorted(refmacWeightNodes, key=lambda residue: float(residue.xpath("weight")[-1].text))
            rootNode = self.xmlnode.xpath0("//RefmacOptimiseWeight")
            #Remove unsorted nodes and put in sorted ones
            for refmacWeightNode in refmacWeightNodes:
                rootNode.remove(refmacWeightNode)
            for sortedRefmacNode in sortedRefmacNodes:
                rootNode.append(sortedRefmacNode)

            endpointSummaryTable = byWeightsFold.addTable(select="//RefmacWeight",style="width:260px;height:250px; float:left;")
            endpointSummaryTable.addData(title="Weight", select="weight")
            endpointSummaryTable.addData(title="R-factor", select="REFMAC/Overall_stats/stats_vs_cycle/new_cycle[last()]/r_factor")
            endpointSummaryTable.addData(title="R-free",   select="REFMAC/Overall_stats/stats_vs_cycle/new_cycle[last()]/r_free",   expr="x if float(x)>=0.0 else '-'")
            endpointSummaryTable.addData(title="RMS Deviations", subtitle="Bond", select="REFMAC/Overall_stats/stats_vs_cycle/new_cycle[last()]/rmsBOND", expr="x if float(x)>=0.0 else '-'")
            endpointSummaryTable.addData(title="RMS Deviations", subtitle="Angles", select="REFMAC/Overall_stats/stats_vs_cycle/new_cycle[last()]/rmsANGLE", expr="x if float(x)>=0.0 else '-'")

            byWeightGraph = byWeightsFold.addFlotGraph(title='Stats by weight ', select="//RefmacWeight/REFMAC/Overall_stats/stats_vs_cycle/new_cycle[last()]",style="width:400px;height:250px;float:left;")
            byWeightGraph.addData(title="Weight",    select="//RefmacWeight/weight")
            byWeightGraph.addData(title="R_Factor", select="r_factor")
            byWeightGraph.addData(title="R_Free",  select="r_free")
            byWeightGraph.addData(title="RMS_Bondsx100", select="rmsBOND", expr="str(float(x)*100.)")
            byWeightGraph.addData(title="RMS_Angles",  select="rmsANGLE")
            
            plot = byWeightGraph.addPlotObject()
            plot.append('title','R Factors as a function of weight matrix term')
            plot.append('plottype','xy')
            for coordinate, colour in [(2,'blue'),(3,'red')]:
                #print coordinate, colour
                plotLine = plot.append('plotline',xcol=1,ycol=coordinate)
                plotLine.append('colour',colour)
            
            plot = byWeightGraph.addPlotObject()
            plot.append('title','Geometry as a function of weight matrix term')
            plot.append('plottype','xy')
            for coordinate, colour in [(4,'blue'),(5,'red')]:
                plotLine = plot.append('plotline',xcol=1,ycol=coordinate)
                plotLine.append('colour',colour)
        
            clearingFold = byWeightsFold.addDiv(style="width:760px; clear:both;")
                        
            graphsByWeightFold = byWeightsFold.addFold(label='Details for each weight',brief='Details')
            refmacWeightNodes = xmlnode.xpath('/RefmacOptimiseWeight/RefmacWeight')
            for refmacWeightNode in refmacWeightNodes:
                self.addOutputForWeight(refmacWeightNode=refmacWeightNode, parent=graphsByWeightFold)

    def addOutputForWeight(self, refmacWeightNode=None, parent=None):
        if parent is None: parent = self
        if refmacWeightNode is None: refmacWeightNode = self.xmlnode
        refmacReportNode = refmacWeightNode.xpath0('./REFMAC')
        if refmacReportNode is not None:
            refmacReport = refmac_report.refmac_report(xmlnode=refmacReportNode, jobStatus='nooutput')
        if refmacReport is not None:
            refmacReport.addSummary(parent = parent)

def test(xmlFile=None,jobId=None,reportFile=None):
    import sys,os
    try:
        text = open( xmlFile ).read()
        xmlnode = etree.fromstring( text, PARSER() )
    except:
        print 'FAILED loading XML file:', kw['xmlFile']
    if reportFile is None and xmlFile is not None:
        reportFile = os.path.join(os.path.split(xmlFile)[0],'report.html')
    r = prosmart_refmac_report(xmlFile=xmlFile,jobId=jobId, xmlnode=xmlnode)
    r.as_html_file(reportFile)

if __name__ == "__main__":
    import sys
    prosmart_refmac_report(xmlFile=sys.argv[1],jobId=sys.argv[2])
