#!/usr/bin/python3
#
# Copyright (c) 2013-2014 The Khronos Group Inc.
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and/or associated documentation files (the
# "Materials"), to deal in the Materials without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Materials, and to
# permit persons to whom the Materials are furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Materials.
#
# THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.

import io, os, re, string, sys;

if __name__ == '__main__':
    if (len(sys.argv) != 5):
        print('Usage:', sys.argv[0], ' gendir srcdir accordfilename flatfilename', file=sys.stderr)
        exit(1)
    else:
        gendir = sys.argv[1]
        srcdir = sys.argv[2]
        accordfilename = sys.argv[3]
        flatfilename = sys.argv[4]
        # print(' gendir = ', gendir, ' srcdir = ', srcdir, 'accordfilename = ', accordfilename, 'flatfilename = ', flatfilename)
else:
    print('Unknown invocation mode', file=sys.stderr)
    exit(1)

# Various levels of indentation in generated HTML
ind1 = '    '
ind2 = ind1 + ind1
ind3 = ind2 + ind1
ind4 = ind2 + ind2

# Symbolic names
notAlias = False
isAlias = True

# Page title
pageTitle = 'OpenGL 4.x Reference Pages'

# Docbook source and generated HTML file extensions
srcext = '.xml'
genext = '.xhtml'

# List of generated files
files = os.listdir(gendir)

# Feature - class representing a command or function to be indexed, used
# as dictionary values keyed by the feature name to be indexed.
#
# Members
#   file - name of file containing the feature
#   feature - feature name for the index (basis for the dictionary key).
#   alias - True if this is an alias of another feature in the file.
#       Usually if alias is False, feature is the basename of file.
#   apiCommand - True if this is an API command, or should be grouped
#       like one
class Feature:
    def __init__(self,
                 file = None,
                 feature = None,
                 alias = False,
                 apiCommand = None):
        self.file = file
        self.feature = feature
        self.alias = alias
        self.apiCommand = apiCommand
    def makeKey(self):
        # Return dictionary / sort key based on the feature name 
        if (self.apiCommand and self.feature[0:2] == 'gl'): 
            return self.feature[2:]
        else:
            return self.feature

# Add dictionary entry for specified Feature.
# The key used is the feature name, with the leading 'gl' stripped 
#  off if this is an API command
def addkey(dict, feature):
    key = feature.makeKey()
    if (key in dict.keys()):
        print('Key', key, ' already exists in dictionary!')
    else:
        dict[key] = feature

# Create list of entry point names to be indexed.
# Unlike the old Perl script, this proceeds as follows:
# - Each .xhtml page with a parent .xml page gets an
#   index entry for its base name.
# - Additionally, each <function> tag inside a <funcdef>
#   in the parent page gets an aliased index entry.
# - Each .xhtml page *without* a parent is reported but
#   not indexed.
# - Each collision in index terms is reported.
# - Index terms are keys in a dictionary whose entries
#   are [ pagename, alias, glPrefix ] where pagename is 
#   the base name of the indexed page and alias is True
#   if this index isn't the same as pagename.
# - API keys have their glPrefix value set to True,
#   GLSL keys to False. There is a simplistic way of 
#   telling the files apart based on the file name:
#
#   * Everything starting with 'gl[A-Z]' is API
#   * 'removedTypes.*' is API (more may be added)
#   * Everything else is GLSL

def isGLfile(entrypoint):
    if (re.match('^gl[A-Z]', entrypoint) or entrypoint == 'removedTypes'):
        return True
    else:
        return False

# Dictionary of all keys mapped to Feature values
refIndex = {}

for file in files:
    # print('Processing file', file)
    (entrypoint,ext) = os.path.splitext(file)
    if (ext == genext):
        parent = srcdir + '/' + entrypoint + srcext
        # Determine if this is an API or GLSL page
        apiCommand = isGLfile(entrypoint)
        if (os.path.exists(parent)):
            addkey(refIndex, Feature(file, entrypoint, False, apiCommand))
            # Search parent file for <function> tags inside <funcdef> tags
            # This doesn't search for <varname> inside <fieldsynopsis>, because
            #   those aren't on the same line and it's hard.
            fp = open(parent)
            for line in fp.readlines():
                # Look for <function> tag contents and add as aliases
                # Don't add the same key twice
                for m in re.finditer(r"<funcdef>.*<function>(.*)</function>.*</funcdef>", line):
                    funcname = m.group(1)
                    if (funcname != entrypoint):
                        addkey(refIndex, Feature(file, funcname, True, apiCommand))
            fp.close()
        else:
            print('No parent page for', file, ', will not be indexed')

# Some utility functions for generating the navigation table
# Opencl_tofc.html uses style.css instead of style-index.css
# flatMenu - if True, don't include accordion JavaScript,
#   generating a flat (expanded) menu.
# letters - if not None, include per-letter links to within
#   the indices for each letter in the list.
# altMenu - if not None, the name of the alternate index to
#   link to.
def printHeader(fp, flatMenu = False, letters = None, altMenu = None):
    if (flatMenu):
        scriptInclude = '    <!-- Don\'t include accord.js -->'
    else:
        scriptInclude = '    <?php include \'accord.js\'; ?>'

    print('<html>',
          '<head>',
          '    <link rel="stylesheet" type="text/css" href="style-index.css" />',
          '    <title>' + pageTitle + '</title>',
               scriptInclude,
          '</head>',
          '<body>',
          sep='\n', file=fp)

    if (altMenu):
        if (flatMenu):
            altLabel = '(accordion-style)'
        else:
            altLabel = '(flat)'
        print('    <a href="' + altMenu + '">' + 
              'Use alternate ' + altLabel + ' index' +
              '</a>', file=fp)

    if (letters):
        print('    <center>\n<div id="container">', file=fp)
        for letter in letters:
            print('        <b><a href="#' + 
                  letter + 
                  '" style="text-decoration:none">' + 
                  letter + 
                  '</a></b> &nbsp;', file=fp)
        print('    </div>\n</center>', file=fp)

    print('    <div id="navwrap">',
          '    <ul id="containerul"> <!-- Must wrap entire list for expand/contract -->',
          '    <li class="Level1">',
          '        <a href="start.html" target="pagedisplay">Introduction</a>',
          '    </li>',
          sep='\n', file=fp)

def printFooter(fp, flatMenu = False):
    print('    </div> <!-- End containerurl -->', file=fp)
    if (not flatMenu):
        print('    <script type="text/javascript">initiate();</script>', file=fp)
    print('</body>',
          '</html>',
          sep='\n', file=fp)

# Add a nav table entry. key = link name, feature = Feature info for key
def addMenuLink(key, feature, fp):
    file = feature.file
    linkname = feature.feature

    print(ind4 + '<li><a href="' + file + '" target="pagedisplay">'
               + linkname + '</a></li>',
          sep='\n', file=fp)

# Begin index section for a letter, include an anchor to link to
def beginLetterSection(letter, fp):
    print(ind2 + '<a name="' + letter + '"></a>',
          ind2 + '<li>' + letter,
          ind3 + '<ul class="Level3">',
          sep='\n', file=fp)

# End index section for a letter
def endLetterSection(opentable, fp):
    if (opentable == 0):
        return
    print(ind3 + '</ul> <!-- End Level3 -->',
          ind2 + '</li>',
          sep='\n', file=fp)

# Return the keys in a dictionary sorted by name.
# Select only keys matching whichKeys (see genDict below)
def sortedKeys(dict, whichKeys):
    list = []
    for key in dict.keys():
        if (whichKeys == 'all' or
            (whichKeys == 'api' and dict[key].apiCommand) or
            (whichKeys == 'glsl' and not dict[key].apiCommand)):
            list.append(key)
    list.sort(key=str.lower)
    return list

# Generate accordion menu for this dictionary, titled as specified.
#
# If whichKeys is 'all', generate index for all features
# If whichKeys is 'api', generate index only for API features
# If whichKeys is 'glsl', generate index only for GLSL features
#
# fp is the file to write to
def genDict(dict, title, whichKeys, fp):
    print(ind1 + '<li class="Level1">' + title,
          ind2 + '<ul class="Level2">',
          sep='\n', file=fp)

    # Print links for sorted keys in each letter section
    curletter = ''
    opentable = 0

    # Determine which letters are in the table of contents for this
    # dictionary. If glPrefix is set, strip the 'gl' prefix from each
    # key containing it first.

    # Generatesorted list of page indexes. Select keys matching whichKeys.
    keys = sortedKeys(dict, whichKeys)

    # print('@ Sorted list of page indexes:\n', keys)

    for key in keys:
        # Character starting this key
        c = str.lower(key[0])

        if (c != curletter):
            endLetterSection(opentable, fp)
            # Start a new subtable for this letter
            beginLetterSection(c, fp)
            opentable = 1
            curletter = c
        addMenuLink(key, dict[key], fp)
    endLetterSection(opentable, fp)

    print(ind2 + '</ul> <!-- End Level2 -->',
          ind1 + '</li> <!-- End Level1 -->',
          sep='\n', file=fp)

######################################################################

# Generate the accordion menu, with separate API and GLSL sections
fp = open(accordfilename, 'w')
printHeader(fp, flatMenu = False, altMenu = flatfilename)

genDict(refIndex, 'API Entry Points', 'api', fp)
genDict(refIndex, 'GLSL Functions', 'glsl', fp)

printFooter(fp, flatMenu = False)
fp.close()

######################################################################

# Generate the non-accordion menu, with combined API and GLSL sections
fp = open(flatfilename, 'w')

# Set containing all index letters
indices = { key[0].lower() for key in refIndex.keys() }
letters = [c for c in indices]
letters.sort()

printHeader(fp, flatMenu = True, letters = letters, altMenu = accordfilename)

genDict(refIndex, 'API and GLSL Index', 'all', fp)

printFooter(fp, flatMenu = True)
fp.close()
