##############################################################################
# @file  doxyfilter-bash.py
# @brief Doxygen filter for BASH scripts.
#
# Copyright (c) 2011 University of Pennsylvania. All rights reserved.<br />
# See http://www.rad.upenn.edu/sbia/software/license.html or COPYING file.
#
# Contact: SBIA Group <sbia-software at uphs.upenn.edu>
#
# @ingroup Tools
##############################################################################

import sys
import re
import sqlite3

if __name__ == "__main__":
    conn = sqlite3.connect('doxygen.sqlite')
    sqlc = conn.cursor()
    #c.execute('''DROP TABLE IF EXISTS funcs''')
    sqlc.execute('''CREATE TABLE IF NOT EXISTS funcs (funcname text PRIMARY KEY)''') # table funcs created
    # parse arguments
    if len(sys.argv) != 2:
        sys.stderr.write("No file specified to process!\n")
        sys.stderr.close()
        sys.exit(1)
    fileName = sys.argv [1]
    # open input file
    fh = open(fileName, 'r')
    if not fh:
        sys.stderr.write("Failed to open file " + fileName + " for reading!\n")
        sys.stderr.close()
        sys.exit(1)
    # compile regular expressions
    reShaBang       = re.compile(r"#!\s*/usr/bin/env\s+bash$|#!\s*/bin/bash$")
    reInclude       = re.compile(r"(source|\.)\s+[\"']?(\$SIUXBIN/)?(?P<module>[^#|&]+)[\"']?(\s*#.*)?$")
    reFunctionStart = re.compile(r"function\s*(?P<name1>[\w\.-]+)\s*{?|(?P<name2>\w+)\s*\(\s*\)\s*{?\s*")
    reFunctionEnd   = re.compile(r"^}$")
    reCommentStart  = re.compile(r"##+(?P<comment>.*)$")
    reCommentLine   = re.compile(r"#+(?P<comment>.*)$")
    reParamDoc      = re.compile(r"[\@\\]param\s*(\[\s*(in|out|in\s*,\s*out|out\s*,\s*in)\s*\]|\s*)\s+(?P<param>\w+)")
    reIfClauseStart = re.compile(r"if\s*\[")
    reIfClauseEnd   = re.compile(r"else[\s$]|elif\s*\[|;?fi$")

    # parse line-by-line and output pseudo C++ code to stdout
    ifClauseDepth = 0     # current depth of if-clauses
    commentDepth  = 0     # if-clause depth where comment was encountered
    previousBlock = ''    # name of previous code block
    currentBlock  = ''    # name of current code block
    params        = []
    for line in fh:
        oline = line
        line = line.strip()
        #sys.stdout.write("=== %s === %s" % (currentBlock, line));
        if reShaBang.match(line) is not None: # skip sha-bang directive
            sys.stdout.write("\n")
            continue
        if currentBlock == 'comment': # next comment line,
            m = reCommentLine.match(line)
            if m is not None:
                comment = m.group('comment')
                sys.stdout.write("///" + comment + "\n")
                m = reParamDoc.search(line)
                if m is not None:
                    param = m.group('param')
                    params.append(param)
                continue
            else:
                previousBlock = currentBlock
                currentBlock = ''
                # continue processing of this (yet unhandled) line
        if currentBlock == 'function': # inside function definition
            m = reFunctionEnd.match(oline)
            if m is not None:
                previousBlock = currentBlock
                currentBlock = ''
                sys.stdout.write("}\n")
            else:
                sys.stdout.write("// %s" % oline)
            continue
        if currentBlock == '': # look for new comment block or block following a comment
            m = reInclude.match(line) # include
            if m is not None:
                module = m.group('module')
                module = module.replace("\"", "")
                module = module.replace("/./", "/")
                module = module.replace("${_BASIS_DIR}/", "")
                module = module.replace("$(get_executable_directory)/", "")
                module = module.replace("$exec_dir/", "")
                module = module.replace("${exec_dir}/", "")
                sys.stdout.write("#include \"" + module + "\"\n")
                continue
            m = reIfClauseStart.match(line) # enter if-clause
            if m is not None:
                ifClauseDepth = ifClauseDepth + 1
                sys.stdout.write("\n") # TODO ae not understood
                continue
            if ifClauseDepth > 0: # leave if-clause
                m = reIfClauseEnd.match(line)
                if m is not None:
                    ifClauseDepth = ifClauseDepth - 1
                    if commentDepth > ifClauseDepth:
                        previousBlock = ''
                        currentBlock  = ''
                    sys.stdout.write("\n")
                    continue
            m = reCommentStart.match(line) # Doxygen comment
            if m is not None:
                comment = m.group('comment')
                sys.stdout.write("///" + comment + "\n")
                currentBlock = 'comment'
                commentDepth = ifClauseDepth
                m = reParamDoc.search(line)
                if m is not None:
                    param = m.group('param')
                    params.append(param)
                continue
            # if previous block was a Doxygen comment process
            # supported following blocks such as variable setting
            # and function definition optionally only the one
            # inside the if-case of an if-else-clause
            if True:
                # function
                m = reFunctionStart.match(line)
                if m is not None:
                    name = m.group('name1')
                    if not name:
                        name = m.group('name2')
                    sqlc.execute("INSERT OR IGNORE INTO funcs VALUES (?)", (name,))
                    #sys.stdout.write("function " + name.replace('.', '_DOT_').replace('-', '_MINUS_') + "(")
                    #sys.stdout.write("function " + name + " (")
                    #sys.stdout.write("function <" + name + "> (")
                    sys.stdout.write("function %s (" % name.replace('.', '\x023'))
                    for i in range(0, len(params)):
                        if i > 0:
                            sys.stdout.write(", ")
                        sys.stdout.write("in " + params [i])
                    sys.stdout.write(") {\n")
                    currentBlock = 'function'
                    params = []
                    continue
            if line != '': # unhandled lines...
                if previousBlock == 'comment':
                    # prevent comments that are not associated with any
                    # processed block to be merged with subsequent comments
                    sys.stdout.write("class COMMENT_DUMPED_BY_DOXYGEN_FILTER;\n")
                else:
                    sys.stdout.write("\n")
                previousBlock = ''
            else:
                sys.stdout.write("\n")
        else:
            sys.stdout.write("\n")
    fh.close() # close input file
    #
    conn.commit() # close db
    conn.close()
    #
    sys.exit(0) # done
