from CCP4ReportParser import *
import sys
from lxml import etree

## @package arcimboldo_report
# The initialisation method creates the whole report.
# Offers functionalised report components for use on
# pipeline reports.


class arcimboldo_report ( Report ) :
    # Specify which gui task and/or pluginscript this applies to
    TASKNAME = 'arcimboldo'
    
    
    ## The constructor, which generates the whole report.
    def __init__(self,xmlnode=None,jobInfo={},**kw):
        Report.__init__(self,xmlnode=xmlnode,jobInfo=jobInfo,**kw)

        #if jobStatus is None or jobStatus.lower() is 'nooutput': return
        
        results = self.addResults()
        self.summary ( parent = results )
        self.b_factor_graph ( parent = results )
        
        b_fact_fold = results.addFold ( label="Summary of B-factor analysis", initiallyOpen=True )
        self.b_factor_tables ( parent = b_fact_fold )
        rama_fold   = results.addFold ( label="Ramachandran analysis", initiallyOpen=True )
        self.rama_graph ( parent = rama_fold )
    
    
    ## Creates a textual description of the result, suitable for a header.
    #  @param self The object pointer.
    #  @param parent A container to which the component will be added

    def summary ( self, parent = None ) :


        if parent is None :
            parent = self
        
        mean_b_all = mean_b_main = mean_b_side = mean_b_waters = mean_b_ligands = "0.0"
        
        mean_b_all = self.xmlnode.xpath ( "//B_averages/Totals/Aminoacids/Mean_B")[0].text
        mean_b_main = self.xmlnode.xpath ( "//B_averages/Totals/Aminoacids/Main_Chain_Mean_B")[0].text

        sidechains = self.xmlnode.select ( "//B_averages/Totals/Aminoacids/Side_Chain_Mean_B" )

        if sidechains != "" :
            mean_b_side = self.xmlnode.xpath ( "//B_averages/Totals/Aminoacids/Side_Chain_Mean_B")[0].text

        nwaters = self.xmlnode.select ( "//B_averages/Totals/Waters" )
    
        if nwaters != "" :
            mean_b_waters = self.xmlnode.xpath ( "//B_averages/Totals/Waters/Mean_B")[0].text

        nligands = self.xmlnode.select ( "//B_averages/Totals/Ligands" )

        if nligands != "" :
            mean_b_ligands = self.xmlnode.xpath ( "//B_averages/Totals/Ligands/Mean_B")[0].text

        n_residues = self.xmlnode.xpath ( "//Ramachandran_maps/Totals/Residues")[0].text
        n_favoured = self.xmlnode.xpath ( "//Ramachandran_maps/Totals/Favoured")[0].text
        n_allowed  = self.xmlnode.xpath ( "//Ramachandran_maps/Totals/Allowed")[0].text
        n_outliers = self.xmlnode.xpath ( "//Ramachandran_maps/Totals/Outliers")[0].text

        text = "The average B-factor for the protein part is %.2f, " % float(mean_b_all)
        text +="(%.2f for the main-chain, " % float(mean_b_main)
        
        if sidechains != "" :
            text +="%.2f for the side-chain " % float(mean_b_side)

        text += ")"

        if nligands != "" :
            text +=", %.2f for the ligands" % float(mean_b_ligands)
        if nwaters != "" :
            text +=" and %.2f for the waters" % float(mean_b_waters)

        text += "."
        
        parent.append ( text )
        
        text ="%s residues have been analysed using Ramachandran's criterion, with " % n_residues
        text +="%s in favoured regions, " % n_favoured
        text +="%s in allowed regions and " % n_allowed
        text +="%s outliers." % n_outliers
    
        parent.append ( text )
            
    ## Creates one B-factor plot per Chain ID, separated by main and side chain.
    #  @param self The object pointer.
    #  @param parent A container to which the component will be added
    
    def b_factor_graph ( self, parent = None ) :

        if parent is None:
            parent = self

        ch_graph = parent.addFlotGraph ( title="Per-chain analysis", select="//B_averages/By_Residue", style="height:330px; width:585px; border:0px; float:left; padding:10px; padding-left:15px;" )

        n_col = 1

        for chain in self.xmlnode.xpath("//B_averages/By_Residue/Chain") :
            ch_graph.addData ( title = "Numbers_chain_%s" % chain.get('id') , select = "Chain[@id='%s']/Aminoacid/Number" % chain.get('id') )
            ch_graph.addData ( title = "Main&nbsp;chain" , select = "Chain[@id='%s']/Aminoacid/Main_Chain_Mean_B" % chain.get('id') )
            ch_graph.addData ( title = "Side&nbsp;chain" , select = "Chain[@id='%s']/Aminoacid/Side_Chain_Mean_B" % chain.get('id'), expr="-x")

            residues = chain.select ( "Aminoacid" )
            
            if residues != "" :
  
                p = ch_graph.addPlotObject()
                p.append( 'description', 'This graph shows separate plots for main- and side-chains in each chain.')
                p.append( 'title', "Average B-factors for chain %s" % chain.get('id') )
                
                plot_hist = p.append( 'barchart', col=n_col, tcol = n_col + 1 )
                plot_hist.append ( 'width' ).text = '1'
                plot_hist.append ( 'colour' ).text = '#4682B4'
                
                plot_hist = p.append( 'barchart', col=n_col, tcol = n_col + 2 )
                plot_hist.append ( 'width' ).text = '1'
                plot_hist.append ( 'colour' ).text = '#FF4500'
            
                p.append ( 'xlabel' ).text = "Number of residue"
                p.append ( 'ylabel' ).text = "Average B-factor"
            
            n_col += 3

        parent.addDiv ( style="clear:both;" )
        parent.append ( "This graph uses the representation introduced by Z. Dauter, L.C. Sieker and K.S. Wilson. (1992). Acta Cryst. B48, 42-59 ")
        
        return ch_graph
        

    ## Creates an itemised table with the average B-factors, suitable for inclusion in Table 1
    #  @param self The object pointer.
    #  @param parent A container to which the component will be added
        
    def b_factor_tables ( self, parent = None ) :
        
        if parent is None :
            parent = self
        
        aminoacid_div = parent.addDiv ( style="float:left; border:0px; min-width:110px; max-width: 150px; padding-left:20px;" )
        aminoacid_div.append ('<p style="text-align:left; margin-left: 5px; font-weight:bold; font-size:12px;">Aminoacids</p>' )
        aminoacid_table = aminoacid_div.addTable ( select="//B_averages/Totals/Aminoacids", transpose=True, downloadable=True, id='aminoacids_table' )
        aminoacid_table.addData ( title = "Atom count", select="Atom_count" )
        aminoacid_table.addData ( title = "&lt;B<sub>fact</sub>&gt;", select="Mean_B", expr="round(x,2)" )
        aminoacid_table.addData ( title = "B<sub>fact</sub> StdDev", select="All_StdDev", expr="round(x,2)" )
        aminoacid_table.addData ( title = "Main chain &lt;B<sub>fact</sub>&gt;", select="Main_Chain_Mean_B", expr="round(x,2)" )
        aminoacid_table.addData ( title = "Main chain StdDev", select="Main_Chain_StdDev", expr="round(x,2)" )
        aminoacid_table.addData ( title = "Side chain &lt;B<sub>fact</sub>&gt;", select="Side_Chain_Mean_B", expr="round(x,2)" )
        aminoacid_table.addData ( title = "Side chain StdDev", select="Side_Chain_StdDev", expr="round(x,2)" )
        
        n_ligands = self.xmlnode.select("//B_averages/Totals/Ligands")
        
        if n_ligands != "" :

            ligands_div = parent.addDiv ( style="float:left; min-width:110px; max-width: 150px; border:0px; padding-left:20px;" )
            ligands_div.append ('<p style="text-align:left; margin-left: 5px;  font-weight:bold; font-size:12px;">Ligands</p>' )
            ligands_table = ligands_div.addTable ( select="//B_averages/Totals/Ligands", transpose=True, downloadable=True, id='ligands_table' )
            ligands_table.addData ( title = "Atom count", select="Atom_count" )
            ligands_table.addData ( title = "&lt;B<sub>fact</sub>&gt;", select="Mean_B", expr="round(x,2)" )
            ligands_table.addData ( title = "Ligands StdDev", select="Ligands_StdDev", expr="round(x,2)" )

        n_waters = self.xmlnode.select("//B_averages/Totals/Waters")
        
        if n_waters != "" :

            waters_div = parent.addDiv ( style="float:left; min-width:110px; max-width: 150px; border:0px; padding-left:20px;" )
            waters_div.append ('<p style="text-align:left; margin-left: 5px;  font-weight:bold; font-size:12px;">Waters</p>')
            waters_table = waters_div.addTable ( select="//B_averages/Totals/Waters", transpose=True, downloadable=True, id='waters_table' )
            waters_table.addData ( title = "Atom count", select="Atom_count" )
            waters_table.addData ( title = "&lt;B<sub>fact</sub>&gt;", select="Mean_B", expr="round(x,2)" )
            waters_table.addData ( title = "Waters StdDev", select="Waters_StdDev", expr="round(x,2)" )
        
        parent.addDiv ( style="clear:both;" )

        return aminoacid_div



    ## Creates a Ramachandran style graph
    #  @param self The object pointer.
    #  @param parent A container to which the component will be added

    def rama_graph ( self, parent = None ) :
        
        if parent is None:
            parent = self

        import CCP4Utils,os
        
        background_PRO = os.path.join(CCP4Utils.getCCP4I2Dir(),'wrappers/arcimboldo/script/rama_pro.png')
        background_GLY = os.path.join(CCP4Utils.getCCP4I2Dir(),'wrappers/arcimboldo/script/rama_gly.png')
        background_RST = os.path.join(CCP4Utils.getCCP4I2Dir(),'wrappers/arcimboldo/script/rama_rst.png')
        
        rama_graph = parent.addFlotGraph ( title="Ramachandran analysis", select="//Ramachandran_maps", style="height:500px; width:500px; border:0px; float:left; padding:10px; padding-left:15px;" )
    
        rama_graph.addData ( title="PRO_Favoured_Phi", select="Favoured/Residue[@type='PRO']/Phi" )
        rama_graph.addData ( title="PRO_Favoured_Psi", select="Favoured/Residue[@type='PRO']/Psi" )
        rama_graph.addData ( title="PRO_Allowed_Phi" , select="Allowed/Residue[@type='PRO']/Phi" )
        rama_graph.addData ( title="PRO_Allowed_Psi" , select="Allowed/Residue[@type='PRO']/Psi" )
        rama_graph.addData ( title="PRO_Outliers_Phi", select="Outliers/Residue[@type='PRO']/Phi" )
        rama_graph.addData ( title="PRO_Outliers_Psi", select="Outliers/Residue[@type='PRO']/Psi" )

        rama_graph.addData ( title="GLY_Favoured_Phi", select="Favoured/Residue[@type='GLY']/Phi" )
        rama_graph.addData ( title="GLY_Favoured_Psi", select="Favoured/Residue[@type='GLY']/Psi" )
        rama_graph.addData ( title="GLY_Allowed_Phi" , select="Allowed/Residue[@type='GLY']/Phi" )
        rama_graph.addData ( title="GLY_Allowed_Psi" , select="Allowed/Residue[@type='GLY']/Psi" )
        rama_graph.addData ( title="GLY_Outliers_Phi", select="Outliers/Residue[@type='GLY']/Phi" )
        rama_graph.addData ( title="GLY_Outliers_Psi", select="Outliers/Residue[@type='GLY']/Psi" )

        rama_graph.addData ( title="RST_Favoured_Phi", select="Favoured/Residue[@type!='PRO'][@type!='GLY']/Phi" )
        rama_graph.addData ( title="RST_Favoured_Psi", select="Favoured/Residue[@type!='PRO'][@type!='GLY']/Psi" )
        rama_graph.addData ( title="RST_Allowed_Phi" , select="Allowed/Residue[@type!='PRO'][@type!='GLY']/Phi" )
        rama_graph.addData ( title="RST_Allowed_Psi" , select="Allowed/Residue[@type!='PRO'][@type!='GLY']/Psi" )
        rama_graph.addData ( title="RST_Outliers_Phi", select="Outliers/Residue[@type!='PRO'][@type!='GLY']/Phi" )
        rama_graph.addData ( title="RST_Outliers_Psi", select="Outliers/Residue[@type!='PRO'][@type!='GLY']/Psi" )


        # Add rest graph
        p = rama_graph.addPlotObject()
        p.append( 'description', "This graph shows the dihedral Phi and Psi angles for all residues, coloured according to Ramachandran's criterion. Source: Richardsons' Top 500 structures.")
        
        p.append ( 'background' ).text = background_RST
        
        p.append( 'title', "Ramachandran plot [Non-Pro/Gly]" )
        p.append( 'plottype', 'xy' )
        p.append( 'showlegend', 'false' )
        p.append( 'xintegral', 'true' )
        p.append( 'yintegral', 'true' )
        p.append( 'xlabel', 'Phi' )
        p.append( 'ylabel', 'Psi' )
        p.append( 'xrange', min=-180.0, max=180.0 )
        p.append( 'yrange', min=-180.0, max=180.0 )
        
        l = p.append( 'plotline', xcol = 13, ycol = 14 )
        l.append( 'colour', 'green' )
        l.append( 'linestyle', '.' )
        l.append( 'symbolsize', '1' )
        l.append( 'symbol', '.' )
        
        l = p.append( 'plotline', xcol = 15, ycol = 16 )
        l.append( 'colour', 'yellow' )
        l.append( 'linestyle', '.' )
        l.append( 'symbolsize', '3' )
        l.append( 'symbol', 'o' )
        
        l = p.append( 'plotline', xcol = 17, ycol = 18 )
        l.append( 'colour', 'red' )
        l.append( 'linestyle', '.' )
        l.append( 'symbolsize', '10' )
        l.append( 'symbol', 'x' )


        # Add Proline graph
        p = rama_graph.addPlotObject()
        p.append( 'description', "This graph shows the dihedral Phi and Psi angles for all residues, coloured according to Ramachandran's criterion. Source: Richardsons' Top 500 structures.")
        
        p.append ( 'background' ).text = background_PRO
        
        p.append( 'title', "Ramachandran plot [Proline]" )
        p.append( 'plottype', 'xy' )
        p.append( 'showlegend', 'false' )
        p.append( 'xintegral', 'true' )
        p.append( 'yintegral', 'true' )
        p.append( 'xlabel', 'Phi' )
        p.append( 'ylabel', 'Psi' )
        p.append( 'xrange', min=-180.0, max=180.0 )
        p.append( 'yrange', min=-180.0, max=180.0 )
        
        l = p.append( 'plotline', xcol = 1, ycol = 2 )
        l.append( 'colour', 'green' )
        l.append( 'linestyle', '.' )
        l.append( 'symbolsize', '1' )
        l.append( 'symbol', '.' )
        
        l = p.append( 'plotline', xcol = 3, ycol = 4 )
        l.append( 'colour', 'yellow' )
        l.append( 'linestyle', '.' )
        l.append( 'symbolsize', '3' )
        l.append( 'symbol', 'o' )

        l = p.append( 'plotline', xcol = 5, ycol = 6 )
        l.append( 'colour', 'red' )
        l.append( 'linestyle', '.' )
        l.append( 'symbolsize', '10' )
        l.append( 'symbol', 'x' )


        # Add Glycine graph
        p = rama_graph.addPlotObject()
        p.append( 'description', "This graph shows the dihedral Phi and Psi angles for all residues, coloured according to Ramachandran's criterion. Source: Richardsons' Top 500 structures.")
        
        p.append ( 'background' ).text = background_GLY
        
        p.append( 'title', "Ramachandran plot [Glycine]" )
        p.append( 'plottype', 'xy' )
        p.append( 'showlegend', 'false' )
        p.append( 'xintegral', 'true' )
        p.append( 'yintegral', 'true' )
        p.append( 'xlabel', 'Phi' )
        p.append( 'ylabel', 'Psi' )
        p.append( 'xrange', min=-180.0, max=180.0 )
        p.append( 'yrange', min=-180.0, max=180.0 )
        
        l = p.append( 'plotline', xcol = 7, ycol = 8 )
        l.append( 'colour', 'green' )
        l.append( 'linestyle', '.' )
        l.append( 'symbolsize', '1' )
        l.append( 'symbol', '.' )
        
        l = p.append( 'plotline', xcol = 9, ycol = 10 )
        l.append( 'colour', 'yellow' )
        l.append( 'linestyle', '.' )
        l.append( 'symbolsize', '3' )
        l.append( 'symbol', 'o' )
        
        l = p.append( 'plotline', xcol = 11, ycol = 12 )
        l.append( 'colour', 'red' )
        l.append( 'linestyle', '.' )
        l.append( 'symbolsize', '10' )
        l.append( 'symbol', 'x' )


        parent.addDiv ( style="clear:both;" )
        
        n_residues = self.xmlnode.xpath ( "//Ramachandran_maps/Totals/Residues")[0].text
        n_favoured = self.xmlnode.xpath ( "//Ramachandran_maps/Totals/Favoured")[0].text
        n_allowed  = self.xmlnode.xpath ( "//Ramachandran_maps/Totals/Allowed")[0].text
        n_outliers = self.xmlnode.xpath ( "//Ramachandran_maps/Totals/Outliers")[0].text

        text ="%s residues have been analysed, with " % n_residues
        text +="%s in favoured regions, " % n_favoured
        text +="%s in allowed regions and " % n_allowed
        text +="%s outliers." % n_outliers
        
        parent.append ( text )


        return rama_graph


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

