#!/usr/bin/env python

#-----------------------------------------------------------
# Copyright Christian Arnault LAL-Orsay CNRS
# author: arnault@lal.in2p3.fr
# modified by: garonne@lal.in2p3.fr
# See the complete license in cmt_license.txt "http://www.cecill.info". 
#-----------------------------------------------------------

import sys, os, string, getopt, types
import xml.parsers.expat
import commands, glob
from   datetime import date


#------------------------------------------------------------
def checkPythonVersion (major=1,minor=5):    
    version      = float(str(major) +'.'+ str(minor))
    thisversion  = float(str(sys.version_info[0]) + '.' + str(sys.version_info[1]))
    if    version > thisversion:
    # try to find the good python version >= 2.3
        paths = string.split(os.environ['PATH'],':')
        print 'hmmm look for the appropriate python version, this one is',\
        thisversion,'and the required version is python >=', version    
        pythons = list()      
        for path in paths:
            pythons = glob.glob1(path, 'python*')
            for python in pythons:
                cmd = python +' -c "import sys; print sys.version_info"'
                status, thisversion = commands.getstatusoutput(cmd)
                thisversion         = eval (thisversion)
                thisversion         = float(str(thisversion[0]) +'.'+ str(thisversion[1]))
                if (thisversion >= version):
                    print 'found and load', python
                    sys.argv.insert (0, python)
                    os.execvp       (python, sys.argv)  
        print 'The appropriate version of python >=' + version + ' is not here, check your PATH variable or install it'
        sys.exit(-1)


#------------------------------------------------------------
# A Section holds
#   an  id     of the form n.m.p
#   a   title
#   the level
#
class Section :
  def __init__ (self, id, title, level) :
    self.id = id
    self.title = title
    self.level = level
  # end def

  # Format the index entry for this section
  def show_index_entry (self) :
    tab = string.rjust ("", 4*self.level)
    tab = tab.replace (" ", "&nbsp;")
    print '<tr>'
    print '<td width="100">' + self.id + '</td>'
    print '<td>' + tab + '<a href="#' + self.title + '">' + self.title + '</a></td>'
    print '</tr>'
  # end def

#------------------------------------------------------------
# A Book
#  It holds the history of sections and the definitions of internal entities
#
class Book :
  # Format one level of a section id as %2.2d
  def format_id (self, id) :
    r = repr (id)
    if id < 10 :
      r = '&nbsp;' + r
    return r
  # end def

  # Format a complete section id, with all individual level ids
  #  Currently the implementation is somewhat ugly. This is due
  #  to the strange behaviour of the ideal form (using a for-loop
  #  over the elements of self.level_ids). Once we can understand
  #  the reason of this unexpected behaviour we can improve it.
  def build_id (self) :
    id = '<tt>'
    n = len (self.level_ids)
    if n >= 1 :
      id += self.format_id (self.level_ids[0])
    if n >= 2 :
      id += '.' + self.format_id (self.level_ids[1])
    if n >= 3 :
      id += '.' + self.format_id (self.level_ids[2])
    if n >= 4 :
      id += '.' + self.format_id (self.level_ids[3])
    id += '</tt>'
    return id
  # end def

  # Create a new Section object
  #  Register the history of sections (with level hierarchy) 
  def open_section (self, name) :
    self.level_ids [self.level] += 1
    i = self.level_ids [self.level]
    id = self.build_id ()
    self.sections.append (Section (id, name, self.level))
    self.level += 1
    self.level_ids.append (0)
    return id
  # end def

  # Pop one level in the level hierarchy
  def close_section (self) :
    self.level_ids.pop ()
    self.level -= 1
  # end def

  # Register the definition of an internal entity in the entity dictionary
  def entity_decl (self, name, is_parameter, value, base, systemId, publicId, notation) :
    if value :
      self.entities['&' + name + ';'] = value
  # end def

  # Generic parser
  def generic_parse (self, p, name) :
    p.StartElementHandler = self.start_element
    p.EndElementHandler = self.end_element
    p.CharacterDataHandler = self.char_data
    p.ExternalEntityRefHandler = self.external_entity
    p.DefaultHandler = self.dummy
    p.EntityDeclHandler = self.entity_decl
    #file_name = os.path.join (sys.path[0], name)
    f = open (name)
    p.ParseFile (f)
    f.close ()
  # end def

  # Top level parser
  def parse (self, name) :
    self.p = xml.parsers.expat.ParserCreate ()
    self.generic_parse (self.p, name)
  # end def

  # Sub-parser for external entities
  def external_entity (self, context, base, system_id, public_id):
    #print "external entity " + repr(context) + " " + repr(system_id) + ' ' + sys.path[0]
    ep = self.p.ExternalEntityParserCreate (context)
    self.generic_parse (ep, system_id)
  # end def

  # Format a tabulation according to the stored tabulation level
  def tab (self) :
    return (string.rjust ("", self.tabulation))
  # end def

  # Flush the internal line buffer and display it using the tabulation
  def purge (self) :
    if self.line != '':
      if self.in_code == 0 :
        print self.tab () + self.line
        self.line = ''
  # end def


  #----------------------------------------------------------
  #
  #  All XML element handlers
  #
  #----------------------------------------------------------


  # any element : simply reproduce the element with its attributes
  #  (ie assume it is a true HTML element)
  def default_start (self, name, attrs) :
    self.line += '<' + name
    if len (attrs) > 0 :
      i = len (attrs)
      for k, v in attrs.items() :
        self.line += ' ' + k + '="' + v + '"'
        i -= 1
        if i <= 0 : break
    self.line += '>'
    #####  self.purge ()
    self.tabulation += 2
  # end def


  def default_end (self, name) :
    self.tabulation -= 2
    self.line += '</' + name + '>'
    self.purge ()
  # end def


  # <book>
  def book_start (self, attrs) :
    self.name = attrs['name']
    self.title = attrs['title']
    self.version = attrs['version']
    self.author = attrs['author']
    print '<!doctype html public "-//w3c//dtd html 4.0 transitional//en"> '
    print '<html>'
    print '<title>' + self.name + '</title>'
    print '<head>'
    print ' <style type="text/css">'
    print '   body {font-face: Arial, Helvetica, Comic Sans MS, Times; color: #000000}'
    ##print '   tt {color:#006600; font-weight: normal; background-color: #eeffee}'
    print '   tt {color:#00AA00; font-weight: normal; background-color: #eeeeee}'
    print '   pre {color:#FF0000; background-color: #eeeeee; border-style: solid; border-width: 1; border-color: black; padding: 4}'
    print '   pre.cmt {font-family: courier; color:#00AA00; background-color: #eeeeee; border-style: solid; border-width: 1; border-color: black; padding: 4}'
    print '   h2 {color:#0000FF}'
    print '   h3 {color:#00AA00}'
    print '   h4 {color:#997700}'
    print '   a {color:#0000FF; background-color: LightGoldenRodYellow; text-decoration: none}'
    print '   b {font-family: courier; color:#006600; font-weight: normal; %background-color: #eeffee}'
    print '   td.rule {padding-top: 10}'
    print ' </style>'
    print '</head>'
    print '<body bgcolor="#ffffff" link="#550088" alink="#007777" alink="#007777">'
    ##print '<font face="Arial, Helvetica" color="#000000">'
    ##print '<font face="Arial, Helvetica, Comic Sans MS, Times" color="#000000">'
    print '<h1><center>' + self.name + '</center>'
    print '<center>' + self.title + '</center></h1>'
    print '<h2><center>Version ' + self.version + '</center>'
    print '<center>' + self.author + '</center>'
    print '<center><tt>' + attrs['email'] + '</tt></center></h2>'
    self.line += '<center><i>Document revision date : ' + date.today().isoformat() + '</i></center>'
    self.line += '<hr><h2>'
    self.line += '<a href="#index">General index</a>'
    self.line += '</h2>'
    self.purge ()
  # end def

  def book_end (self) :
    print '<hr><h1><A NAME="index"></A>Contents</h1>'
    print '<blockquote>'
    print '<table cols="2">'
    for k in range (len (self.sections)) :
      self.sections[k].show_index_entry ()
    print '<tr><td colspan="2">&nbsp;</td></tr>'
    print '<tr><td colspan="2"><h2>Images</h2></td></tr>'
    for k in range (len (self.images)) :
      print '<tr>'
      print '<td width="100"><tt>' + repr(k+1) + '</tt></td>'
      print '<td><a href="#' + self.images[k] + '">' + self.images[k] + '</a></td>'
      print '</tr>'
    print '</table>'
    print '</blockquote>'
    ##print '</font>'
    print '<address>'
    print '<i>' + self.author + '</i>'
    print '</address>'
    print '</body>'
    print '</html>'
  # end def


  # <section>
  def section_start (self, attrs) :
    title = attrs['title']
    title = title.replace ('>', '&gt;')
    title = title.replace ('<', '&lt;')
    id = self.open_section (title)
    h_level = repr(self.level+1)
    self.line += '<hr><h' + h_level + '>'
    self.line += '<a name="' + title + '"></a>'
    self.line += '<a href="#index">' + id + '</a> - ' + title
    self.line += '</h' + h_level + '>'
    self.purge ()
    self.line = '<blockquote>'
    self.purge ()
  # end def

  def section_end (self) :
    self.purge ()
    self.line = '</blockquote>'
    self.purge ()
    self.close_section ()
  # end def


  # <code>
  def code_start (self, attrs) :
    self.purge ()
    self.in_code += 1
    self.line = '<pre>'
  # end def

  def code_end (self) :
    self.in_code -= 1
    print self.line + '</pre>'
    self.line = ''
  # end def


  # <cmtcode>
  def cmtcode_start (self, attrs) :
    self.purge ()
    self.in_code += 1
    self.line = '<pre class="cmt">'
  # end def

  def cmtcode_end (self) :
    self.in_code -= 1
    print self.line + '</pre>'
    self.line = ''
  # end def


  # <syntax>
  def syntax_start (self, attrs) :
    print '<center>'
    print '<table cols="3">'
    print '<tr>'
    if 'rule-width' in attrs :
      print '<td width="' + attrs['rule-width'] + '"></td>'
    else :
      print '<td></td>'
    if 'name' in attrs :
      self.ruleref_prefix = 'kw' + attrs['name'] + '-'
    else :
      self.ruleref_prefix = 'kw-'
    print '<td width="10"></td>'
    print '<td></td>'
    print '</tr>' 
  # end def

  def syntax_end (self) :
    print '</table>'
    print '</center>'
  # end def


  # <rule>
  def rule_start (self, attrs) :
    self.rule_name = attrs['name']
    self.rule_started = 0
  # end def

  def rule_end (self) :
    self.rule_name = ''
    self.rule_started = 0
  # end def


  # <alt>
  def alt_start (self, attrs) :
    print '<tr>'
    if self.rule_started == 0 :
      self.rule_started = 1
      print '<td class="rule"><font face="courier new, courier" COLOR="#770000"><i><a name="' + self.ruleref_prefix + self.rule_name + '"></a>' + self.rule_name + '</i></font></td>'
      print '<td class="rule">:</td>'
      print '<td class="rule">'
    else :
      print '<td></td>'
      print '<td>|</td>'
      print '<td>'
  # end def
      
  def alt_end (self) :
    print '</td>'
    print '</tr>'
  # end def


  # <continuation>
  def continuation_start (self, attrs) :
    print '<tr>'
    print '<td></td>'
    print '<td></td>'
    print '<td>&nbsp;&nbsp;&nbsp;'
  # end def
      
  def continuation_end (self) :
    print '</td>'
    print '</tr>'
  # end def


  # <kwd>
  def kwd_start (self, attrs) :
    print '<font face="courier new, courier" COLOR="#FF0000">'
    if 'name' in attrs :
      name = attrs['name']
    else :
      name = self.rule_name
    if 'value' in attrs :
      name += '='
      value = '<font face="courier new, courier" COLOR="#770000"><i>'
      value += attrs['value']
      value += '</i></font>'
    else :
      value = ''
    print name + value
  # end def

  def kwd_end (self) :
    print '</font>'
  # end def



  # <term>
  def term_start (self, attrs) :
    print '<font face="courier new, courier" COLOR="#770000"><i>'
    print attrs['name']
  # end def

  def term_end (self) :
    print '</i></font>'
  # end def



  # <ruleref>
  def ruleref_start (self, attrs) :
    print '<font face="courier new, courier" COLOR="#770000"><i>'
    print '<a href="#' + self.ruleref_prefix + attrs['name'] + '">' + attrs['name'] + '</a>'
  # end def

  def ruleref_end (self) :
    print '</i></font>'
  # end def


  # <option>
  def option_start (self, attrs) :
    print '[&nbsp;'
  # end def

  def option_end (self) :
    print '&nbsp;]&nbsp;'
  # end def


  # <seq>
  def seq_start (self, attrs) :
    i=0
  # end def

  def seq_end (self) :
    print '&nbsp;...&nbsp;'
  # end def


  # <optionseq>
  def optionseq_start (self, attrs) :
    print '[&nbsp;'
  # end def

  def optionseq_end (self) :
    print '&nbsp;...&nbsp;'
    print '&nbsp;]&nbsp;'
  # end def



  # <image>
  def image_start (self, attrs) :
    caption = attrs['caption']
    self.images.append (caption)
    n = len (self.images)
    print '<center>'
    print '<a name="' + caption + '"></a>'
    print '<img src="' + attrs['src'] + '"/>'
    print '</center>'
    print '<center>'
    print '<tt>' + repr(n) + '</tt> - <i>' + caption + '</i>'
  # end def

  def image_end (self) :
    print '</center>'
  # end def

  def blockquote_start (self, attrs) :
    print '<blockquote><hr>'
  # end def

  def blockquote_end (self) :
    print '<hr></blockquote>'
  # end def


  # Basic element parsing handler
  def start_element (self, name, attrs):
    #print 'start_element: name=' + repr(name)
    try :
      if self.start_handlers.has_key (name) :
        self.start_handlers [name] (attrs)
      else :
        self.default_start (name, attrs)
    except TypeError:
      print 'start_element: (error) name=' + repr(name)
      self.default_start (name, attrs)
  # end def

  def end_element (self, name) :
    #print 'end_element: name=' + repr(name)
    try :
      if self.end_handlers.has_key (name) :
        self.end_handlers [name] ()
      else :
        self.default_end (name)
    except TypeError:
      #print 'end_element: (error) name=' + repr(name)
      if name == u'cmt:book' :
        self.book_end ()
      else :
        self.default_end (name)
  # end def

  # Unhandled elements will be trapped here
  def dummy (self, data) :
    if self.entities.has_key (data) :
      self.char_data (self.entities[data])
    #print "dummy:[" + repr (data) + "]" + repr (type(data))
  # end def

  # CDATA handling inside code sections
  def code_char_data (self, data) :
    if data == u'\n' :
      self.line += data
    else :
      n = len (data)
      #
      if n > 0 :
        if data == u'<':
          self.line += '&lt;'
        elif data == u'>' :
          self.line += '&gt;'
        else :
          self.line += data
  # end def

  # CDATA handling outside code sections
  def plain_char_data (self, data) :
    if data == u'\n' :
      self.purge ()
    else :
      n = len (string.strip (data))
      #
      if n > 0 :
        if data == u'<':
          self.line += '&lt;'
        elif data == u'>' :
          self.line += '&gt;'
        else :
  #        self.line += string.strip (data)
          self.line += data
  # end def

  # CDATA handling
  def char_data (self, data) :
    #print '[' + repr(data) + ']' + repr (type (data)) + ' ' + repr(len (string.strip (data)))
    if self.in_code > 0 :
      self.code_char_data (data)
    else :
      self.plain_char_data (data)
  # end def

  def __init__ (self) :
    self.line = ''
    self.tabulation = 0
    self.in_code = 0
    self.sections = []
    self.level_ids = [0]
    self.level = 0
    self.images = []
    self.entities = {}
    #
    self.start_handlers = {}
    self.start_handlers['cmt:code'] = self.code_start
    self.start_handlers['cmt:cmtcode'] = self.cmtcode_start
    self.start_handlers['cmt:section'] = self.section_start
    self.start_handlers['cmt:book'] = self.book_start
    self.start_handlers['cmt:syntax'] = self.syntax_start
    self.start_handlers['cmt:rule'] = self.rule_start
    self.start_handlers['cmt:alt'] = self.alt_start
    self.start_handlers['cmt:continuation'] = self.continuation_start
    self.start_handlers['cmt:kwd'] = self.kwd_start
    self.start_handlers['cmt:term'] = self.term_start
    self.start_handlers['cmt:ruleref'] = self.ruleref_start
    self.start_handlers['cmt:option'] = self.option_start
    self.start_handlers['cmt:seq'] = self.seq_start
    self.start_handlers['cmt:optionseq'] = self.optionseq_start
    self.start_handlers['cmt:image'] = self.image_start
    self.start_handlers['cmt:blockquote'] = self.blockquote_start
    #
    self.end_handlers = {}
    self.end_handlers['cmt:code'] = self.code_end
    self.end_handlers['cmt:cmtcode'] = self.cmtcode_end
    self.end_handlers['cmt:section'] = self.section_end
    self.end_handlers['cmt:book'] = self.book_end
    self.end_handlers['cmt:syntax'] = self.syntax_end
    self.end_handlers['cmt:rule'] = self.rule_end
    self.end_handlers['cmt:alt'] = self.alt_end
    self.end_handlers['cmt:continuation'] = self.continuation_end
    self.end_handlers['cmt:kwd'] = self.kwd_end
    self.end_handlers['cmt:term'] = self.term_end
    self.end_handlers['cmt:ruleref'] = self.ruleref_end
    self.end_handlers['cmt:option'] = self.option_end
    self.end_handlers['cmt:seq'] = self.seq_end
    self.end_handlers['cmt:optionseq'] = self.optionseq_end
    self.end_handlers['cmt:image'] = self.image_end
    self.end_handlers['cmt:blockquote'] = self.blockquote_end
  # end def



#----------------------------------------------------------------------------------

#----------------------------------------------------------------------------------
#
#  Various top level functions
#
#----------------------------------------------------------------------------------
def usage() :
  print 'Usage:'
  print '  gendoc'
  print 'Try "gendoc --help" for more information.'
  sys.exit()
# end def

#----------------------------------------------------------------------------------
def help() :
  print "Generates the HTML documentation from Xml"
# end def

#----------------------------------------------------------------------------------
def main() :
  file_name = ''
  options = []
  for a in sys.argv[1:] :
    if a[0] == '-' :
      options = sys.argv[sys.argv.index(a):]
      break
    else :
      file_name = a
  try:
    opts, args = getopt.getopt(options, 'h', ['help'])
  except getopt.GetoptError:
    usage()
    sys.exit(2)
  for o, a in opts:
    if o in ('-h', '--help'):
      help()
      sys.exit()
  book = Book ()
  book.parse (file_name)
# end def

#---------------------------------------------------------------------
#print '__name__ = ' + __name__
if __name__ == "__main__":
  checkPythonVersion (major=2, minor=3)
  main()
# end def

