#!/usr/bin/env python
"""
Web Vulcan: a web-based interface to Vulcan.

This relies on mod_python and VulcanClient

"""

from mod_python import apache, Cookie
import time
import os

from VulcanClient import VulcanClient
FormAction = "http://cars9.uchicago.edu/~newville/py/webvulcan.py"

from HTMLData import CSSData, PTable,make_ptable

class WebVulcan:
    def __init__(self,port=None):
        self.buffer = []
        self.vc     = VulcanClient(port=port)
        self.port   = self.vc.port
        self.elem   = ''
        self.title  = 'X-ray Properties of the Elements'
        self.energy_in  = 30000.0
        self.energy_min =  1000.0

    def write(self,s):
        if s is None: s = '<None>'
        self.buffer.append(s)
        return
    
    def _get_attr(self,attr,**kw):
        elem = kw.get('elem',self.elem)
        return getattr(self.vc,attr)(element=elem, **kw)        
    
    def has(self,**kw):        return self._get_attr('has',**kw)
    def weight(self,**kw):     return self._get_attr('weight',**kw)
    def density(self,**kw):    return self._get_attr('density',**kw)
    def energy(self,**kw):     return self._get_attr('energy',**kw)
    def gamma(self,**kw):      return self._get_attr('gamma',**kw)
    def edge_jump(self,**kw):  return self._get_attr('edge_jump',**kw)
    def fluor_yield(self,**kw): return self._get_attr('fluor_yield',**kw)
    def intensity(self,**kw):  return self._get_attr('intensity',**kw)
    def siegbahn(self,**kw):   return self._get_attr('siegbahn',**kw)
    def iupac(self,**kw):      return self._get_attr('iupac',**kw)
    def xsec(self,**kw):       return self._get_attr('xsec',**kw)
    def photo(self,**kw):      return self._get_attr('photo',**kw)
    def coherent(self,**kw):   return self._get_attr('coherent',**kw)
    def incoherent(self,**kw): return self._get_attr('incoherent',**kw)    
    
    def at_name(self,sym=None):
        _dat = ['unknown','hydrogen','helium','lithium','beryllium','boron','carbon',
                'nitrogen','oxygen','fluorine','neon','sodium','magnesium',
                'aluminum','silicon','phosphorus','sulfur','chlorine','argon',
                'potassium','calcium','scandium','titanium','vanadium','chromium',
                'manganese','iron','cobalt','nickel','copper','zinc','gallium',
                'germanium','arsenic','selenium','bromine','krypton','rubidium',
                'strontium','yttrium','zirconium','niobium','molybdenum',
                'technetium','ruthenium','rhodium','palladium','silver','cadmium',
                'indium','tin','antimony','tellurium','iodine','xenon','cesium',
                'barium','lanthanum','cerium','praseodymium','neodymium','promethium',
                'samarium','europium','gadolinium','terbium','dysprosium',
                'holmium','erbium','thulium','ytterbium','lutetium','hafnium',
                'tantalum','tungsten','rhenium','osmium','iridium','platinum',
                'gold','mercury','thallium','lead','bismuth','polonium','astatine',
                'radon','francium','radium','actinium','thorium','protactinium',
                'uranium','neptunium','plutonium','americium','curium','berkelium',
                'californium','einsteinium','fermium','mendelevium','nobelium','lawerencium']

        if sym is None: sym = self.elem
        if isinstance(sym,str):
            iz = self.at_number(sym)
        elif isinstance(sym,int):
            iz = sym
        else:
            iz = 0
        if iz < 0 or iz >= len(_dat): iz = 0
        return _dat[iz].title()


    def at_number(self,sym=None):
        _dat = [None,'H','He','Li','Be','B','C','N','O','F','Ne',
               'Na','Mg','Al','Si','P','S','Cl','Ar','K','Ca','Sc',
               'Ti','V','Cr','Mn','Fe','Co','Ni','Cu','Zn','Ga','Ge',
               'As','Se','Br','Kr','Rb','Sr','Y','Zr','Nb','Mo','Tc',
               'Ru','Rh','Pd','Ag','Cd','In','Sn','Sb','Te','I','Xe',
               'Cs','Ba','La','Ce','Pr','Nd','Pm','Sm','Eu','Gd','Tb',
               'Dy','Ho','Er','Tm','Yb','Lu','Hf','Ta','W','Re','Os',
               'Ir','Pt','Au','Hg','Tl','Pb','Bi','Po','At','Rn','Fr',
               'Ra','Ac','Th','Pa','U','Np','Pu','Am','Cm','Bk','Cf',
               'Bk','Cf','Es','Fm','Md','No','Lr']
        
        if sym is None: sym = self.elem
        try:
            return _dat.index(sym.strip().title())
        except:
            return 0
        
    def start_html(self):
        self.write('<head><title>%s:</title></head><body>' % self.title)
        self.write(CSSData)
        
    def get_html(self):
        out = '\n'.join(self.buffer)
        self.buffer = []
        return out

    def show_emissiontable(self,edge,lineslist):

        # don't show edges above excitation energy
        edge_energy = self.energy(edge=edge)
        if (self.energy_in < edge_energy) or (self.energy_min > edge_energy):
            return
        
        self.write("<table cellpadding=3><tr><td colspan=5></td></tr>")

        self.write("<tr><td colspan=5>%s-Edge (E = %.1f) Emission Lines</td></tr>" % (edge.title(),edge_energy))

        self.write("""<tr><td></td><td>Line</td><td>Transition</td><td>Energy&nbsp;</td><td>Strength</td>
        </tr><tr><td>&nbsp;&nbsp;</td><td colspan=4><hr></td></tr>""")#  % edge.title())

        eupper = edge.upper()
        for lines in lineslist:
            needspace = False
            for key,en,iupac,int in lines:
                if en > 1 and int > 0.0005 and iupac.startswith(eupper):
                    sty = ''
                    if en < self.energy_min:  sty = 'id=grayentry'
                    self.write("""<tr %s><td></td><td>%s</td><td>%s</td><td align=right>%.1f</td>
                    <td align=right>%.3f</td></tr>""" % (sty,key,iupac,en,int))
                    # needspace = True
            #if needspace and lines != lineslist[-1]:
            #    self.write("<tr><td colspan=2>&nbsp;&nbsp;</td><td colspan=2></td></tr>")

        self.write("<tr><td>&nbsp;&nbsp;</td><td colspan=4><hr></td></tr></table>")

    def show_energy_data(self):
        def line(s):  return (self.siegbahn(line=s),self.energy(line=s),self.iupac(line=s),self.intensity(line=s))
        def edge(s):  return {'title':s.title(),'energy':self.energy(edge=s),
                              'gamma':self.gamma(edge=s), 'jump': self.edge_jump(edge=s),
                              'fyield': self.fluor_yield(edge=s)}


        kalphas = (line('ka1'),line('ka2'),line('ka3'))
        kbetas  = (line('kb1'),line('kb2'),line('kb3'),line('kb4'),line('kb5'))


        lalphas = (line('la1'),line('la2'))
        lbetas  = (line('lb1'),line('lb2'),line('lb3'),line('lb4'),line('lb5'),line('lb6'))
        lgammas = (line('lg1'),line('lg2'),line('lg3'),line('lg4'),line('lg5'),line('lg6'))
        lothers = (line('ll'),line('lnu'))
        mlines  = (line('ma'),line('mb'),line('mg'),line('mz'))

        sym = self.elem
        ename = self.at_name()
        iz = self.at_number()
        atwt  = self.weight()
        self.write("""<h4>%s: %s &nbsp;&nbsp;
        (Z=%i) &nbsp;&nbsp;  Atomic Weight=%.1f</h4>\n""" % (ename,sym,iz,atwt))
        
        self.write("""<table cellpadding=2><tr valign=top><td width=35%%>
        <h4>Excitation Energies (eV):</h4>
        <table><tr><td>Edge&nbsp;</td><td>Energy&nbsp;</td><td>Width&nbsp;</td>
        <td>Edge Jump&nbsp;</td><td>Fluor Yield&nbsp;</td></tr>
        <tr><td colspan=5><hr></td></tr>
        """)
        format = """<tr><td>%(title)s</td><td align=right>%(energy).1f</td>
        <td align=right>%(gamma).2f</td><td align=center>%(jump).2f</td><td align=center>%(fyield).3f</td>"""

        for e in ('k','l3','l2','l1'):  self.write(format % edge(e))        
                   

        self.write("""<tr><td colspan=5><hr></td></tr></table>
        </td><td width=60%%>
        <h4>Emission Energies (eV):</h4>""")
        
        self.show_emissiontable('k',  [kalphas,kbetas])
        self.show_emissiontable('l3', [lalphas,lbetas,lgammas,lothers])
        self.show_emissiontable('l2', [lalphas,lbetas,lgammas,lothers])
        self.show_emissiontable('l1', [lalphas,lbetas,lgammas,lothers])        
        self.write("</td></tr></table>")
        

    def draw_ptable(self):
        wr = self.write
        wr("WebVulcan 0.1:  &nbsp; X-ray Excitation and Emission Energies for Elements<p><hr>")
        # wr(PTable % (self.energy_max,self.energy_min,self.elem))
        linkargs = '&energy_in=%.1f&energy_min=%1.f' % (self.energy_in, self.energy_min)
        make_ptable(writer=wr, form_action=FormAction,linkargs=linkargs,
                    form_elem=self.elem, energy_max = self.energy_in,
                    energy_min = self.energy_min)
   

def connect(req):
    """connect to a WebVulcan instance, including a port to a VulcanServer
    uses a web cookie to hold the Port to the VulcanServer in state
    
    """

    # look for cookie, use held port if available
    has_cookie = False
    port = None
    try:
        cookies= Cookie.get_cookies(req, Cookie.MarshalCookie, secret='foo')
        if cookies.has_key('VulcanPort'):
            cookie = cookies['VulcanPort']
            port = int(cookie.value)
            if port < 12000: port = None
            has_cookie = True
    except:
        pass
        
    # create a WebVulcan instance with this port
    w    = WebVulcan(port=port)

    # if needed, save this port in a cookie to be read next time
    if not has_cookie:
        cookie = Cookie.MarshalCookie('VulcanPort',w.port,'foo')
        cookie.expires = time.time() + 1800
        Cookie.add_cookie(req,cookie)

    return w
    
def isnull(s):
    if isinstance(s,str): s = s.strip()
    return s in ('','None',None,False,'False')
    
def energies(req, elem='', submit='',debug=False,
          energy_in=None,energy_min=None, form_elem='',   **kw):
    """main interaction for WebVulcan
    """

    # connect to WebVulcan
    c = connect(req)

    atname = c.at_name()
    if atname != 'unknown':
        c.title = '%s - %s' % (c.title,atname)

    c.start_html()

    if not isnull(debug):
        c.write("Args<br>")
        args = locals()
        for k in ('elem','form_elem','submit','energy_in','energy_min'):
            v = args.get(k,'')
            c.write("%s = '%s'<br>" % (k,v))

        c.write("Extr Args<br>")
        for k,v in kw.items():
            c.write("%s = '%s'<br>" % (k,v))


    if isnull(elem) and not isnull(form_elem):       elem = form_elem

    if not isnull(elem) and isinstance(elem,str):    c.elem = elem.strip()

    if isnull(c.elem) and not isnull(form_elem):
        c.elem = form_elem.strip()

    try:
        c.energy_in = float(energy_in)
    except:
        pass
    try:
        c.energy_min = float(energy_min)
    except:
        pass


    c.draw_ptable()
    c.write("""<table><tr><td>Excitation Energy (eV):</td>
    <td><input name='energy_in' type=text value='%.1f' size=13></td><td></td></tr>
    <tr><td>Minimum Energy(eV):</td>
    <td><input name='energy_min' type=text value='%.1f' size=13></td>
    <td> (lowest energy reported)</td></tr>
    <tr><td><input type=submit  name='Submit'      value='Submit'> </td><td></td></tr>
    </table>
    <input type=hidden name='form_elem'    value='%s'>
    </form>
    """ % (c.energy_in,c.energy_min,c.elem))

    if c.elem != '':
        c.show_energy_data()
    c.write('<hr></body></html>')
    return c.get_html()



def index(req,**kw):
    return energies(req,**kw)
