# Copyright (c) 2011 The Chromium Embedded Framework Authors. All rights
# reserved. Use of this source code is governed by a BSD-style license that
# can be found in the LICENSE file.

from date_util import *
from file_util import *
import os
import re
import shutil
import string
import sys
import textwrap
import time


def notify(msg):
    """ Display a message. """
    sys.stdout.write('  NOTE: '+msg+'\n')

def wrap_text(text, indent = '', maxchars = 80):
    """ Wrap the text to the specified number of characters. If
    necessary a line will be broken and wrapped after a word.
    """
    result = ''
    lines = textwrap.wrap(text, maxchars - len(indent))
    for line in lines:
        result += indent+line+'\n'
    return result

def wrap_code(code, indent = '    ', maxchars = 80, splitchars = '(=,'):
    """ Wrap the code lines to the specified number of characters. If
    necessary a line will be broken and wrapped after one of the split
    characters.
    """
    output = ''

    # normalize line endings
    code = code.replace("\r\n", "\n")

    # break the code chunk into lines
    lines = string.split(code, '\n')
    for line in lines:
        if len(line) <= maxchars:
            # line is short enough that it doesn't need to be wrapped
            output += line + '\n'
            continue

        # retrieve the whitespace at the beginning of the line for later use
        # as padding
        ws = ''
        for char in line:
            if char.isspace():
                ws += char
            else:
                break

        # iterate over all characters in the string keeping track of where the
        # last valid break character was found and wrapping the line
        # accordingly
        lastsplit = 0
        nextsplit = -1
        splitct = 0
        pos = 0
        for char in line:
            if splitchars.find(char) >= 0:
                # a new split position has been found
                nextsplit = pos
            size = pos - lastsplit + 1
            if splitct > 0:
                size += len(ws) + len(indent)
            if size >= maxchars:
                # the line is too long
                section = line[lastsplit:nextsplit+1]
                if len(section) > 0:
                    # output the line portion between the last split and the
                    # next split
                    if splitct > 0:
                        # start a new line and trim the line section
                        output += '\n'+ws+indent
                        section = string.strip(section)
                    output += section
                    lastsplit = nextsplit + 1
                    splitct += 1
            pos += 1
        if len(line) - lastsplit > 0:
            # output the remainder of the line
            section = line[lastsplit:]
            if splitct > 0:
                # start a new line and trim the line section
                output += '\n'+ws+indent
                section = string.strip(section)
            output += section
        output += '\n'

    return output

def get_capi_file_name(cppname):
    """ Convert a C++ header file name to a C API header file name. """
    return cppname[:-2]+'_capi.h'

def get_capi_name(cppname, isclassname, prefix = None):
    """ Convert a C++ CamelCaps name to a C API underscore name. """
    result = ''
    lastchr = ''
    for chr in cppname:
        # add an underscore if the current character is an upper case letter
        # and the last character was a lower case letter
        if len(result) > 0 and not chr.isdigit() \
            and string.upper(chr) == chr \
            and not string.upper(lastchr) == lastchr:
            result += '_'
        result += string.lower(chr)
        lastchr = chr

    if isclassname:
        result += '_t'

    if not prefix is None:
        if prefix[0:3] == 'cef':
            # if the prefix name is duplicated in the function name
            # remove that portion of the function name
            subprefix = prefix[3:]
            pos = result.find(subprefix)
            if pos >= 0:
                result = result[0:pos]+ result[pos+len(subprefix):]
        result = prefix+'_'+result

    return result

def get_wrapper_type_enum(cppname):
    """ Returns the wrapper type enumeration value for the specified C++ class
        name. """
    return 'WT_'+get_capi_name(cppname, False)[4:].upper()

def get_prev_line(body, pos):
    """ Retrieve the start and end positions and value for the line immediately
    before the line containing the specified position.
    """
    end = string.rfind(body, '\n', 0, pos)
    start = body.rfind('\n', 0, end)+1
    line = body[start:end]
    return { 'start' : start, 'end' : end, 'line' : line }

def get_comment(body, name):
    """ Retrieve the comment for a class or function. """
    result = []

    pos = body.find(name)
    while pos > 0:
        data = get_prev_line(body, pos)
        line = string.strip(data['line'])
        pos = data['start']
        if len(line) == 0:
            # check if the next previous line is a comment
            prevdata = get_prev_line(body, pos)
            if string.strip(prevdata['line'])[0:2] == '//':
                result.append(None)
            else:
                break
        elif line[0:2] == '/*' or line[-2:] == '*/':
            continue
        elif line[0:2] == '//':
            # keep the comment line including any leading spaces
            result.append(line[2:])
        else:
            break

    result.reverse()
    return result

def validate_comment(file, name, comment):
    """ Validate the comment array returned by get_comment(). """
    # Verify that the comment contains beginning and ending '///' as required by
    # CppDoc (the leading '//' from each line will already have been removed by
    # the get_comment() logic). There may be additional comments proceeding the
    # CppDoc block so we look at the quantity of lines equaling '/' and expect
    # the last line to be '/'.
    docct = 0
    for line in comment:
        if not line is None and len(line) > 0 and line == '/':
            docct = docct + 1
    if docct != 2 or len(comment) < 3 or comment[len(comment)-1] != '/':
        raise Exception('Missing or incorrect comment in %s for: %s' % \
            (file, name))

def format_comment(comment, indent, translate_map = None, maxchars = 80):
    """ Return the comments array as a formatted string. """
    result = ''
    wrapme = ''
    hasemptyline = False
    for line in comment:
        # if the line starts with a leading space, remove that space
        if not line is None and len(line) > 0 and line[0:1] == ' ':
            line = line[1:]
            didremovespace = True
        else:
            didremovespace = False

        if line is None or len(line) == 0 or line[0:1] == ' ' \
            or line[0:1] == '/':
            # the previous paragraph, if any, has ended
            if len(wrapme) > 0:
                if not translate_map is None:
                    # apply the translation
                    for key in translate_map.keys():
                        wrapme = wrapme.replace(key, translate_map[key])
                # output the previous paragraph
                result += wrap_text(wrapme, indent+'// ', maxchars)
                wrapme = ''

        if not line is None:
            if len(line) == 0 or line[0:1] == ' ' or line[0:1] == '/':
                # blank lines or anything that's further indented should be
                # output as-is
                result += indent+'//'
                if len(line) > 0:
                    if didremovespace:
                        result += ' '+line
                    else:
                        result += line
                result += '\n'
            else:
                # add to the current paragraph
                wrapme += line+' '
        else:
            # output an empty line
            hasemptyline = True
            result += '\n'

    if len(wrapme) > 0:
        if not translate_map is None:
            # apply the translation
            for key in translate_map.keys():
                wrapme = wrapme.replace(key, translate_map[key])
        # output the previous paragraph
        result += wrap_text(wrapme, indent+'// ', maxchars)

    if hasemptyline:
        # an empty line means a break between comments, so the comment is
        # probably a section heading and should have an extra line before it
        result = '\n' + result
    return result

def format_translation_changes(old, new):
    """ Return a comment stating what is different between the old and new
    function prototype parts.
    """
    changed = False
    result = ''

    # normalize C API attributes
    oldargs = [x.replace('struct _', '') for x in old['args']]
    oldretval = old['retval'].replace('struct _', '')
    newargs = [x.replace('struct _', '') for x in new['args']]
    newretval = new['retval'].replace('struct _', '')

    # check if the prototype has changed
    oldset = set(oldargs)
    newset = set(newargs)
    if len(oldset.symmetric_difference(newset)) > 0:
        changed = True
        result += '\n  // WARNING - CHANGED ATTRIBUTES'

        # in the implementation set only
        oldonly = oldset.difference(newset)
        for arg in oldonly:
            result += '\n  //   REMOVED: '+arg

        # in the current set only
        newonly = newset.difference(oldset)
        for arg in newonly:
            result += '\n  //   ADDED:   '+arg

    # check if the return value has changed
    if oldretval != newretval:
        changed = True
        result += '\n  // WARNING - CHANGED RETURN VALUE'+ \
                  '\n  //   WAS: '+old['retval']+ \
                  '\n  //   NOW: '+new['retval']

    if changed:
        result += '\n  #pragma message("Warning: "__FILE__": '+new['name']+ \
                  ' prototype has changed")\n'

    return result

def format_translation_includes(header, body):
    """ Return the necessary list of includes based on the contents of the
    body.
    """
    result = ''

    # <algorithm> required for VS2013.
    if body.find('std::min') > 0 or body.find('std::max') > 0:
        result += '#include <algorithm>\n'

    if body.find('cef_api_hash(') > 0:
        result += '#include "include/cef_version.h"\n'

    # identify what CppToC classes are being used
    p = re.compile('([A-Za-z0-9_]{1,})CppToC')
    list = sorted(set(p.findall(body)))
    for item in list:
        directory = ''
        if item != 'CefBase':
            cls = header.get_class(item)
            dir = cls.get_file_directory()
            if not dir is None:
                directory = dir+'/'
        result += '#include "libcef_dll/cpptoc/'+directory+ \
                  get_capi_name(item[3:], False)+'_cpptoc.h"\n'

    # identify what CToCpp classes are being used
    p = re.compile('([A-Za-z0-9_]{1,})CToCpp')
    list = sorted(set(p.findall(body)))
    for item in list:
        directory = ''
        if item != 'CefBase':
            cls = header.get_class(item)
            dir = cls.get_file_directory()
            if not dir is None:
                directory = dir+'/'
        result += '#include "libcef_dll/ctocpp/'+directory+ \
                  get_capi_name(item[3:], False)+'_ctocpp.h"\n'

    if body.find('transfer_') > 0:
        result += '#include "libcef_dll/transfer_util.h"\n'

    return result

def str_to_dict(str):
    """ Convert a string to a dictionary. If the same key has multiple values
        the values will be stored in a list. """
    dict = {}
    parts = string.split(str, ',')
    for part in parts:
        part = string.strip(part)
        if len(part) == 0:
            continue
        sparts = string.split(part, '=')
        if len(sparts) > 2:
            raise Exception('Invalid dictionary pair format: '+part)
        name = string.strip(sparts[0])
        if len(sparts) == 2:
            val = string.strip(sparts[1])
        else:
            val = True
        if name in dict:
            # a value with this name already exists
            curval = dict[name]
            if not isinstance(curval, list):
                # convert the string value to a list
                dict[name] = [curval]
            dict[name].append(val)
        else:
            dict[name] = val
    return dict

def dict_to_str(dict):
    """ Convert a dictionary to a string. """
    str = []
    for name in dict.keys():
        if not isinstance(dict[name], list):
            if dict[name] is True:
                # currently a bool value
                str.append(name)
            else:
                # currently a string value
                str.append(name+'='+dict[name])
        else:
            # currently a list value
            for val in dict[name]:
                str.append(name+'='+val)
    return string.join(str, ',')


# regex for matching comment-formatted attributes
_cre_attrib = '/\*--cef\(([A-Za-z0-9_ ,=:\n]{0,})\)--\*/'
# regex for matching class and function names
_cre_cfname = '([A-Za-z0-9_]{1,})'
# regex for matching class and function names including path separators
_cre_cfnameorpath = '([A-Za-z0-9_\/]{1,})'
# regex for matching function return values
_cre_retval = '([A-Za-z0-9_<>:,\*\&]{1,})'
# regex for matching typedef value and name combination
_cre_typedef = '([A-Za-z0-9_<>:,\*\& ]{1,})'
# regex for matching function return value and name combination
_cre_func   = '([A-Za-z][A-Za-z0-9_<>:,\*\& ]{1,})'
# regex for matching virtual function modifiers
_cre_vfmod  = '([A-Za-z0-9_]{0,})'
# regex for matching arbitrary whitespace
_cre_space  = '[\s]{1,}'
# regex for matching optional virtual keyword
_cre_virtual = '(?:[\s]{1,}virtual){0,1}'

# Simple translation types. Format is:
#   'cpp_type' : ['capi_type', 'capi_default_value']
_simpletypes = {
    'void' : ['void', ''],
    'void*' : ['void*', 'NULL'],
    'int' : ['int', '0'],
    'int32' : ['int32', '0'],
    'uint32' : ['uint32', '0'],
    'int64' : ['int64', '0'],
    'uint64' : ['uint64', '0'],
    'double' : ['double', '0'],
    'float' : ['float', '0'],
    'long' : ['long', '0'],
    'unsigned long' : ['unsigned long', '0'],
    'long long' : ['long long', '0'],
    'size_t' : ['size_t', '0'],
    'bool' : ['int', '0'],
    'char': ['char', '0'],
    'char* const': ['char* const', 'NULL'],
    'cef_color_t': ['cef_color_t', '0'],
    'cef_json_parser_error_t': ['cef_json_parser_error_t', 'JSON_NO_ERROR'],
    'cef_plugin_policy_t': ['cef_plugin_policy_t', 'PLUGIN_POLICY_ALLOW'],
    'CefCursorHandle' : ['cef_cursor_handle_t', 'kNullCursorHandle'],
    'CefEventHandle' : ['cef_event_handle_t', 'kNullEventHandle'],
    'CefWindowHandle' : ['cef_window_handle_t', 'kNullWindowHandle'],
    'CefTextInputContext' : ['cef_text_input_context_t' ,'NULL'],
    'CefPoint' : ['cef_point_t', 'CefPoint()'],
    'CefRect' : ['cef_rect_t', 'CefRect()'],
    'CefSize' : ['cef_size_t', 'CefSize()'],
    'CefRange' : ['cef_range_t', 'CefRange()'],
    'CefDraggableRegion' : ['cef_draggable_region_t', 'CefDraggableRegion()'],
    'CefThreadId' : ['cef_thread_id_t', 'TID_UI'],
    'CefTime' : ['cef_time_t', 'CefTime()'],
}

def get_function_impls(content, ident):
    """ Retrieve the function parts from the specified contents as a set of
    return value, name, arguments and body. Ident must occur somewhere in
    the value.
    """
    # extract the functions
    p = re.compile('\n'+_cre_func+'\((.*?)\)([A-Za-z0-9_\s]{0,})'+
                   '\{(.*?)\n\}',
                   re.MULTILINE | re.DOTALL)
    list = p.findall(content)

    # build the function map with the function name as the key
    result = []
    for retval, argval, vfmod, body in list:
        if retval.find(ident) < 0:
            # the identifier was not found
            continue

        # remove the identifier
        retval = string.replace(retval, ident, '')
        retval = string.strip(retval)

        # retrieve the function name
        parts = string.split(retval, ' ')
        name = parts[-1]
        del parts[-1]
        retval = string.join(parts, ' ')

        # parse the arguments
        args = []
        for v in string.split(argval, ','):
            v = string.strip(v)
            if len(v) > 0:
                args.append(v)

        result.append({
            'retval' : string.strip(retval),
            'name' : name,
            'args' : args,
            'vfmod' : string.strip(vfmod),
            'body' : body
        })

    return result

def get_next_function_impl(existing, name):
    result = None
    for item in existing:
        if item['name'] == name:
            result = item
            existing.remove(item)
            break
    return result

def get_copyright():
    result = \
"""// Copyright (c) $YEAR$ The Chromium Embedded Framework Authors. All rights
// reserved. Use of this source code is governed by a BSD-style license that
// can be found in the LICENSE file.
//
// ---------------------------------------------------------------------------
//
// This file was generated by the CEF translator tool. If making changes by
// hand only do so within the body of existing method and function
// implementations. See the translator.README.txt file in the tools directory
// for more information.
//

"""
    # add the copyright year
    return result.replace('$YEAR$', get_year())


class obj_header:
    """ Class representing a C++ header file. """

    def __init__(self):
        self.filenames = []
        self.typedefs = []
        self.funcs = []
        self.classes = []
        self.root_directory = None

    def set_root_directory(self, root_directory):
        """ Set the root directory. """
        self.root_directory = root_directory

    def add_directory(self, directory, excluded_files = []):
        """ Add all header files from the specified directory. """
        files = get_files(os.path.join(directory, '*.h'))
        for file in files:
            if len(excluded_files) == 0 or \
                not os.path.split(file)[1] in excluded_files:
                self.add_file(file)

    def add_file(self, filepath):
        """ Add a header file. """

        if self.root_directory is None:
            filename = os.path.split(filepath)[1]
        else:
            filename = os.path.relpath(filepath, self.root_directory)
            filename = filename.replace('\\', '/')

        # read the input file into memory
        self.add_data(filename, read_file(filepath))

    def add_data(self, filename, data):
        """ Add header file contents. """

        added = False

        # remove space from between template definition end brackets
        data = data.replace("> >", ">>")

        # extract global typedefs
        p = re.compile('\ntypedef'+_cre_space+_cre_typedef+';',
                       re.MULTILINE | re.DOTALL)
        list = p.findall(data)
        if len(list) > 0:
            # build the global typedef objects
            for value in list:
                pos = value.rfind(' ')
                if pos < 0:
                  raise Exception('Invalid typedef: '+value)
                alias = value[pos+1:]
                value = value[:pos]
                self.typedefs.append(obj_typedef(self, filename, value, alias))

        # extract global functions
        p = re.compile('\n'+_cre_attrib+'\n'+_cre_func+'\((.*?)\)',
                       re.MULTILINE | re.DOTALL)
        list = p.findall(data)
        if len(list) > 0:
            added = True

            # build the global function objects
            for attrib, retval, argval in list:
                comment = get_comment(data, retval+'('+argval+');')
                validate_comment(filename, retval, comment)
                self.funcs.append(obj_function(self, filename, attrib, retval,
                                               argval, comment))

        # extract includes
        p = re.compile('\n#include \"include/'+_cre_cfnameorpath+'.h')
        includes = p.findall(data)

        # extract forward declarations
        p = re.compile('\nclass'+_cre_space+_cre_cfname+';')
        forward_declares = p.findall(data)

        # extract classes
        p = re.compile('\n'+_cre_attrib+
                       '\nclass'+_cre_space+_cre_cfname+_cre_space+
                       ':'+_cre_space+'public'+_cre_virtual+
                       _cre_space+_cre_cfname+_cre_space+
                       '{(.*?)};', re.MULTILINE | re.DOTALL)
        list = p.findall(data)
        if len(list) > 0:
            added = True

            # build the class objects
            for attrib, name, parent_name, body in list:
                comment = get_comment(data, name+' : public')
                validate_comment(filename, name, comment)
                self.classes.append(
                    obj_class(self, filename, attrib, name, parent_name, body,
                              comment, includes, forward_declares))

        if added:
            # a global function or class was read from the header file
            self.filenames.append(filename)

    def __repr__(self):
        result = ''

        if len(self.typedefs) > 0:
            strlist = []
            for cls in self.typedefs:
                strlist.append(str(cls))
            result += string.join(strlist, "\n") + "\n\n"

        if len(self.funcs) > 0:
            strlist = []
            for cls in self.funcs:
                strlist.append(str(cls))
            result += string.join(strlist, "\n") + "\n\n"

        if len(self.classes) > 0:
            strlist = []
            for cls in self.classes:
                strlist.append(str(cls))
            result += string.join(strlist, "\n")

        return result

    def get_file_names(self):
        """ Return the array of header file names. """
        return self.filenames

    def get_typedefs(self):
        """ Return the array of typedef objects. """
        return self.typedefs

    def get_funcs(self, filename = None):
        """ Return the array of function objects. """
        if filename is None:
            return self.funcs
        else:
            # only return the functions in the specified file
            res = []
            for func in self.funcs:
                if func.get_file_name() == filename:
                    res.append(func)
            return res

    def get_classes(self, filename = None):
        """ Return the array of class objects. """
        if filename is None:
            return self.classes
        else:
            # only return the classes in the specified file
            res = []
            for cls in self.classes:
                if cls.get_file_name() == filename:
                    res.append(cls)
            return res

    def get_class(self, classname, defined_structs = None):
        """ Return the specified class or None if not found. """
        for cls in self.classes:
            if cls.get_name() == classname:
                return cls
            elif not defined_structs is None:
                defined_structs.append(cls.get_capi_name())
        return None

    def get_class_names(self):
        """ Returns the names of all classes in this object. """
        result = []
        for cls in self.classes:
            result.append(cls.get_name())
        return result

    def get_types(self, list):
        """ Return a dictionary mapping data types to analyzed values. """
        for cls in self.typedefs:
            cls.get_types(list)

        for cls in self.classes:
            cls.get_types(list)

    def get_alias_translation(self, alias):
        """ Return a translation of alias to value based on typedef
            statements. """
        for cls in self.typedefs:
            if cls.alias == alias:
                return cls.value
        return None

    def get_analysis(self, value, named = True):
        """ Return an analysis of the value based the header file context. """
        return obj_analysis([self], value, named)

    def get_defined_structs(self):
        """ Return a list of names already defined structure names. """
        return ['cef_print_info_t', 'cef_window_info_t', 'cef_base_t']

    def get_capi_translations(self):
        """ Return a dictionary that maps C++ terminology to C API terminology.
        """
        # strings that will be changed in C++ comments
        map = {
            'class' : 'structure',
            'Class' : 'Structure',
            'interface' : 'structure',
            'Interface' : 'Structure',
            'true' : 'true (1)',
            'false' : 'false (0)',
            'empty' : 'NULL',
            'method' : 'function'
        }

        # add mappings for all classes and functions
        funcs = self.get_funcs()
        for func in funcs:
            map[func.get_name()+'()'] = func.get_capi_name()+'()'

        classes = self.get_classes()
        for cls in classes:
            map[cls.get_name()] = cls.get_capi_name()

            funcs = cls.get_virtual_funcs()
            for func in funcs:
                map[func.get_name()+'()'] = func.get_capi_name()+'()'

            funcs = cls.get_static_funcs()
            for func in funcs:
                map[func.get_name()+'()'] = func.get_capi_name()+'()'

        return map


class obj_class:
    """ Class representing a C++ class. """

    def __init__(self, parent, filename, attrib, name, parent_name, body,
                 comment, includes, forward_declares):
        if not isinstance(parent, obj_header):
            raise Exception('Invalid parent object type')

        self.parent = parent
        self.filename = filename
        self.attribs = str_to_dict(attrib)
        self.name = name
        self.parent_name = parent_name
        self.comment = comment
        self.includes = includes
        self.forward_declares = forward_declares

        # extract typedefs
        p = re.compile('\n'+_cre_space+'typedef'+_cre_space+_cre_typedef+';',
                       re.MULTILINE | re.DOTALL)
        list = p.findall(body)

        # build the typedef objects
        self.typedefs = []
        for value in list:
            pos = value.rfind(' ')
            if pos < 0:
              raise Exception('Invalid typedef: '+value)
            alias = value[pos+1:]
            value = value[:pos]
            self.typedefs.append(obj_typedef(self, filename, value, alias))

        # extract static functions
        p = re.compile('\n'+_cre_space+_cre_attrib+'\n'+_cre_space+'static'+
                       _cre_space+_cre_func+'\((.*?)\)',
                       re.MULTILINE | re.DOTALL)
        list = p.findall(body)

        # build the static function objects
        self.staticfuncs = []
        for attrib, retval, argval in list:
            comment = get_comment(body, retval+'('+argval+')')
            validate_comment(filename, retval, comment)
            self.staticfuncs.append(
                obj_function_static(self, attrib, retval, argval, comment))

        # extract virtual functions
        p = re.compile('\n'+_cre_space+_cre_attrib+'\n'+_cre_space+'virtual'+
                       _cre_space+_cre_func+'\((.*?)\)'+_cre_space+_cre_vfmod,
                       re.MULTILINE | re.DOTALL)
        list = p.findall(body)

        # build the virtual function objects
        self.virtualfuncs = []
        for attrib, retval, argval, vfmod in list:
            comment = get_comment(body, retval+'('+argval+')')
            validate_comment(filename, retval, comment)
            self.virtualfuncs.append(
                obj_function_virtual(self, attrib, retval, argval, comment,
                                     vfmod))

    def __repr__(self):
        result = '/* '+dict_to_str(self.attribs)+' */ class '+self.name+"\n{"

        if len(self.typedefs) > 0:
            result += "\n\t"
            strlist = []
            for cls in self.typedefs:
                strlist.append(str(cls))
            result += string.join(strlist, "\n\t")

        if len(self.staticfuncs) > 0:
            result += "\n\t"
            strlist = []
            for cls in self.staticfuncs:
                strlist.append(str(cls))
            result += string.join(strlist, "\n\t")

        if len(self.virtualfuncs) > 0:
            result += "\n\t"
            strlist = []
            for cls in self.virtualfuncs:
                strlist.append(str(cls))
            result += string.join(strlist, "\n\t")

        result += "\n};\n"
        return result

    def get_file_name(self):
        """ Return the C++ header file name. Includes the directory component,
            if any. """
        return self.filename

    def get_capi_file_name(self):
        """ Return the CAPI header file name. Includes the directory component,
            if any. """
        return get_capi_file_name(self.filename)

    def get_file_directory(self):
        """ Return the file directory component, if any. """
        pos = self.filename.rfind('/')
        if pos >= 0:
            return self.filename[:pos]
        return None

    def get_name(self):
        """ Return the class name. """
        return self.name

    def get_capi_name(self):
        """ Return the CAPI structure name for this class. """
        return get_capi_name(self.name, True)

    def get_parent_name(self):
        """ Return the parent class name. """
        return self.parent_name

    def get_parent_capi_name(self):
        """ Return the CAPI structure name for the parent class. """
        return get_capi_name(self.parent_name, True)

    def has_parent(self, parent_name):
        """ Returns true if this class has the specified class anywhere in its
            inheritance hierarchy. """
        # Every class has CefBase as the top-most parent.
        if parent_name == 'CefBase' or parent_name == self.parent_name:
            return True
        if self.parent_name == 'CefBase':
            return False

        cur_cls = self.parent.get_class(self.parent_name)
        while True:
            cur_parent_name = cur_cls.get_parent_name()
            if cur_parent_name == 'CefBase':
                break
            elif cur_parent_name == parent_name:
                return True
            cur_cls = self.parent.get_class(cur_parent_name)

        return False

    def get_comment(self):
        """ Return the class comment as an array of lines. """
        return self.comment

    def get_includes(self):
        """ Return the list of classes that are included from this class'
            header file. """
        return self.includes

    def get_forward_declares(self):
        """ Return the list of classes that are forward declared for this
            class. """
        return self.forward_declares

    def get_attribs(self):
        """ Return all attributes as a dictionary. """
        return self.attribs

    def has_attrib(self, name):
        """ Return true if the specified attribute exists. """
        return name in self.attribs

    def get_attrib(self, name):
        """ Return the first or only value for specified attribute. """
        if name in self.attribs:
            if isinstance(self.attribs[name], list):
                # the value is a list
                return self.attribs[name][0]
            else:
                # the value is a string
                return self.attribs[name]
        return None

    def get_attrib_list(self, name):
        """ Return all values for specified attribute as a list. """
        if name in self.attribs:
            if isinstance(self.attribs[name], list):
                # the value is already a list
                return self.attribs[name]
            else:
                # convert the value to a list
                return [self.attribs[name]]
        return None

    def get_typedefs(self):
        """ Return the array of typedef objects. """
        return self.typedefs

    def has_typedef_alias(self, alias):
        """ Returns true if the specified typedef alias is defined in the scope
            of this class declaration. """
        for typedef in self.typedefs:
          if typedef.get_alias() == alias:
            return True
        return False

    def get_static_funcs(self):
        """ Return the array of static function objects. """
        return self.staticfuncs

    def get_virtual_funcs(self):
        """ Return the array of virtual function objects. """
        return self.virtualfuncs

    def get_types(self, list):
        """ Return a dictionary mapping data types to analyzed values. """
        for cls in self.typedefs:
            cls.get_types(list)

        for cls in self.staticfuncs:
            cls.get_types(list)

        for cls in self.virtualfuncs:
            cls.get_types(list)

    def get_alias_translation(self, alias):
        for cls in self.typedefs:
            if cls.alias == alias:
                return cls.value
        return None

    def get_analysis(self, value, named = True):
        """ Return an analysis of the value based on the class definition
        context.
        """
        return obj_analysis([self, self.parent], value, named)

    def is_library_side(self):
        """ Returns true if the class is implemented by the library. """
        return self.attribs['source'] == 'library'

    def is_client_side(self):
        """ Returns true if the class is implemented by the client. """
        return self.attribs['source'] == 'client'


class obj_typedef:
    """ Class representing a typedef statement. """

    def __init__(self, parent, filename, value, alias):
        if not isinstance(parent, obj_header) \
            and not isinstance(parent, obj_class):
            raise Exception('Invalid parent object type')

        self.parent = parent
        self.filename = filename
        self.alias = alias
        self.value = self.parent.get_analysis(value, False)

    def __repr__(self):
        return 'typedef '+self.value.get_type()+' '+self.alias+';'

    def get_file_name(self):
        """ Return the C++ header file name. """
        return self.filename

    def get_capi_file_name(self):
        """ Return the CAPI header file name. """
        return get_capi_file_name(self.filename)

    def get_alias(self):
        """ Return the alias. """
        return self.alias

    def get_value(self):
        """ Return an analysis of the value based on the class or header file
        definition context.
        """
        return self.value

    def get_types(self, list):
        """ Return a dictionary mapping data types to analyzed values. """
        name = self.value.get_type()
        if not name in list:
            list[name] = self.value


class obj_function:
    """ Class representing a function. """

    def __init__(self, parent, filename, attrib, retval, argval, comment):
        self.parent = parent
        self.filename = filename
        self.attribs = str_to_dict(attrib)
        self.retval = obj_argument(self, retval)
        self.name = self.retval.remove_name()
        self.comment = comment

        # build the argument objects
        self.arguments = []
        arglist = string.split(argval, ',')
        argindex = 0
        while argindex < len(arglist):
            arg = arglist[argindex]
            if arg.find('<') >= 0 and arg.find('>') == -1:
                # We've split inside of a template type declaration. Join the
                # next argument with this argument.
                argindex += 1
                arg += ',' + arglist[argindex]

            arg = string.strip(arg)
            if len(arg) > 0:
                argument = obj_argument(self, arg)
                if argument.needs_attrib_count_func() and \
                    argument.get_attrib_count_func() is None:
                    raise Exception("A 'count_func' attribute is required "+ \
                                    "for the '"+argument.get_name()+ \
                                    "' parameter to "+self.get_qualified_name())
                self.arguments.append(argument)

            argindex += 1

        if self.retval.needs_attrib_default_retval() and \
            self.retval.get_attrib_default_retval() is None:
            raise Exception("A 'default_retval' attribute is required for "+ \
                            self.get_qualified_name())

    def __repr__(self):
        return '/* '+dict_to_str(self.attribs)+' */ '+self.get_cpp_proto()

    def get_file_name(self):
        """ Return the C++ header file name. """
        return self.filename

    def get_capi_file_name(self):
        """ Return the CAPI header file name. """
        return get_capi_file_name(self.filename)

    def get_name(self):
        """ Return the function name. """
        return self.name

    def get_qualified_name(self):
        """ Return the fully qualified function name. """
        if isinstance(self.parent, obj_header):
            # global function
            return self.name
        else:
            # member function
            return self.parent.get_name()+'::'+self.name

    def get_capi_name(self, prefix = None):
        """ Return the CAPI function name. """
        if 'capi_name' in self.attribs:
            return self.attribs['capi_name']
        return get_capi_name(self.name, False, prefix)

    def get_comment(self):
        """ Return the function comment as an array of lines. """
        return self.comment

    def get_attribs(self):
        """ Return all attributes as a dictionary. """
        return self.attribs

    def has_attrib(self, name):
        """ Return true if the specified attribute exists. """
        return name in self.attribs

    def get_attrib(self, name):
        """ Return the first or only value for specified attribute. """
        if name in self.attribs:
            if isinstance(self.attribs[name], list):
                # the value is a list
                return self.attribs[name][0]
            else:
                # the value is a string
                return self.attribs[name]
        return None

    def get_attrib_list(self, name):
        """ Return all values for specified attribute as a list. """
        if name in self.attribs:
            if isinstance(self.attribs[name], list):
                # the value is already a list
                return self.attribs[name]
            else:
                # convert the value to a list
                return [self.attribs[name]]
        return None

    def get_retval(self):
        """ Return the return value object. """
        return self.retval

    def get_arguments(self):
        """ Return the argument array. """
        return self.arguments

    def get_types(self, list):
        """ Return a dictionary mapping data types to analyzed values. """
        for cls in self.arguments:
            cls.get_types(list)

    def get_capi_parts(self, defined_structs = [], prefix = None):
        """ Return the parts of the C API function definition. """
        retval = ''
        dict = self.retval.get_type().get_capi(defined_structs)
        if dict['format'] == 'single':
            retval = dict['value']

        name = self.get_capi_name(prefix)
        args = []

        if isinstance(self, obj_function_virtual):
            # virtual functions get themselves as the first argument
            str = 'struct _'+self.parent.get_capi_name()+'* self'
            if isinstance(self, obj_function_virtual) and self.is_const():
                # const virtual functions get const self pointers
                str = 'const '+str
            args.append(str)

        if len(self.arguments) > 0:
            for cls in self.arguments:
                type = cls.get_type()
                dict = type.get_capi(defined_structs)
                if dict['format'] == 'single':
                    args.append(dict['value'])
                elif dict['format'] == 'multi-arg':
                    # add an additional argument for the size of the array
                    type_name = type.get_name()
                    if type.is_const():
                        # for const arrays pass the size argument by value
                        args.append('size_t '+type_name+'Count')
                    else:
                        # for non-const arrays pass the size argument by address
                        args.append('size_t* '+type_name+'Count')
                    args.append(dict['value'])

        return { 'retval' : retval, 'name' : name, 'args' : args }

    def get_capi_proto(self, defined_structs = [], prefix = None):
        """ Return the prototype of the C API function. """
        parts = self.get_capi_parts(defined_structs, prefix)
        result = parts['retval']+' '+parts['name']+ \
                 '('+string.join(parts['args'], ', ')+')'
        return result

    def get_cpp_parts(self, isimpl = False):
        """ Return the parts of the C++ function definition. """
        retval = str(self.retval)
        name = self.name

        args = []
        if len(self.arguments) > 0:
            for cls in self.arguments:
                args.append(str(cls))

        if isimpl and isinstance(self, obj_function_virtual):
            # enumeration return values must be qualified with the class name
            # if the type is defined in the class declaration scope.
            type = self.get_retval().get_type()
            if type.is_result_struct() and type.is_result_struct_enum() and \
                self.parent.has_typedef_alias(retval):
                retval = self.parent.get_name()+'::'+retval

        return { 'retval' : retval, 'name' : name, 'args' : args }

    def get_cpp_proto(self, classname = None):
        """ Return the prototype of the C++ function. """
        parts = self.get_cpp_parts()
        result = parts['retval']+' '
        if not classname is None:
            result += classname+'::'
        result += parts['name']+'('+string.join(parts['args'], ', ')+')'
        if isinstance(self, obj_function_virtual) and self.is_const():
            result += ' const'
        return result

    def is_same_side(self, other_class_name):
        """ Returns true if this function is on the same side (library or
            client) and the specified class. """
        if isinstance(self.parent, obj_class):
            # this function is part of a class
            this_is_library_side = self.parent.is_library_side()
            header = self.parent.parent
        else:
            # this function is global
            this_is_library_side = True
            header = self.parent

        if other_class_name == 'CefBase':
            other_is_library_side = False
        else:
            other_class = header.get_class(other_class_name)
            if other_class is None:
                raise Exception('Unknown class: '+other_class_name)
            other_is_library_side = other_class.is_library_side()

        return other_is_library_side == this_is_library_side


class obj_function_static(obj_function):
    """ Class representing a static function. """

    def __init__(self, parent, attrib, retval, argval, comment):
        if not isinstance(parent, obj_class):
            raise Exception('Invalid parent object type')
        obj_function.__init__(self, parent, parent.filename, attrib, retval,
                              argval, comment)

    def __repr__(self):
        return 'static '+obj_function.__repr__(self)+';'

    def get_capi_name(self, prefix = None):
        """ Return the CAPI function name. """
        if prefix is None:
            # by default static functions are prefixed with the class name
            prefix = get_capi_name(self.parent.get_name(), False)
        return obj_function.get_capi_name(self, prefix)

class obj_function_virtual(obj_function):
    """ Class representing a virtual function. """

    def __init__(self, parent, attrib, retval, argval, comment, vfmod):
        if not isinstance(parent, obj_class):
            raise Exception('Invalid parent object type')
        obj_function.__init__(self, parent, parent.filename, attrib, retval,
                              argval, comment)
        if vfmod == 'const':
            self.isconst = True
        else:
            self.isconst = False

    def __repr__(self):
        return 'virtual '+obj_function.__repr__(self)+';'

    def is_const(self):
        """ Returns true if the method declaration is const. """
        return self.isconst


class obj_argument:
    """ Class representing a function argument. """

    def __init__(self, parent, argval):
        if not isinstance(parent, obj_function):
            raise Exception('Invalid parent object type')

        self.parent = parent
        self.type = self.parent.parent.get_analysis(argval)

    def __repr__(self):
        result = ''
        if self.type.is_const():
            result += 'const '
        result += self.type.get_type()
        if self.type.is_byref():
            result += '&'
        elif self.type.is_byaddr():
            result += '*'
        if self.type.has_name():
            result += ' '+self.type.get_name()
        return result

    def get_name(self):
        """ Return the name for this argument. """
        return self.type.get_name()

    def remove_name(self):
        """ Remove and return the name value. """
        name = self.type.get_name()
        self.type.name = None
        return name

    def get_type(self):
        """ Return an analysis of the argument type based on the class
        definition context.
        """
        return self.type

    def get_types(self, list):
        """ Return a dictionary mapping data types to analyzed values. """
        name = self.type.get_type()
        if not name in list:
            list[name] = self.type

    def needs_attrib_count_func(self):
        """ Returns true if this argument requires a 'count_func' attribute. """
        # A 'count_func' attribute is required for non-const non-string vector
        # attribute types
        return self.type.has_name() and \
            self.type.is_result_vector() and \
            not self.type.is_result_vector_string() and \
            not self.type.is_const()

    def get_attrib_count_func(self):
        """ Returns the count function for this argument. """
        # The 'count_func' attribute value format is name:function
        if not self.parent.has_attrib('count_func'):
            return None
        name = self.type.get_name()
        vals = self.parent.get_attrib_list('count_func')
        for val in vals:
            parts = string.split(val, ':')
            if len(parts) != 2:
                raise Exception("Invalid 'count_func' attribute value for "+ \
                                self.parent.get_qualified_name()+': '+val)
            if string.strip(parts[0]) == name:
                return string.strip(parts[1])
        return None

    def needs_attrib_default_retval(self):
        """ Returns true if this argument requires a 'default_retval' attribute.
        """
        # A 'default_retval' attribute is required for enumeration return value
        # types.
        return not self.type.has_name() and \
            self.type.is_result_struct() and \
            self.type.is_result_struct_enum()

    def get_attrib_default_retval(self):
        """ Returns the defualt return value for this argument. """
        return self.parent.get_attrib('default_retval')

    def get_arg_type(self):
        """ Returns the argument type as defined in translator.README.txt. """
        if not self.type.has_name():
            raise Exception('Cannot be called for retval types')

        # simple or enumeration type
        if (self.type.is_result_simple() and \
                self.type.get_type() != 'bool') or \
           (self.type.is_result_struct() and \
                self.type.is_result_struct_enum()):
            if self.type.is_byref():
                if self.type.is_const():
                    return 'simple_byref_const'
                return 'simple_byref'
            elif self.type.is_byaddr():
                return 'simple_byaddr'
            return 'simple_byval'

        # boolean type
        if self.type.get_type() == 'bool':
            if self.type.is_byref():
                return 'bool_byref'
            elif self.type.is_byaddr():
                return 'bool_byaddr'
            return 'bool_byval'

        # structure type
        if self.type.is_result_struct() and self.type.is_byref():
            if self.type.is_const():
                return 'struct_byref_const'
            return 'struct_byref'

        # string type
        if self.type.is_result_string() and self.type.is_byref():
            if self.type.is_const():
                return 'string_byref_const'
            return 'string_byref'

        # refptr type
        if self.type.is_result_refptr():
            same_side = self.parent.is_same_side(self.type.get_refptr_type())
            if self.type.is_byref():
                if same_side:
                    return 'refptr_same_byref'
                return 'refptr_diff_byref'
            if same_side:
                return 'refptr_same'
            return 'refptr_diff'


        if self.type.is_result_vector():
            # all vector types must be passed by reference
            if not self.type.is_byref():
                return 'invalid'

            if self.type.is_result_vector_string():
                # string vector type
                if self.type.is_const():
                    return 'string_vec_byref_const'
                return 'string_vec_byref'

            if self.type.is_result_vector_simple():
                if self.type.get_vector_type() != 'bool':
                    # simple/enumeration vector types
                    if self.type.is_const():
                        return 'simple_vec_byref_const'
                    return 'simple_vec_byref'

                # boolean vector types
                if self.type.is_const():
                    return 'bool_vec_byref_const'
                return 'bool_vec_byref'

            if self.type.is_result_vector_refptr():
                # refptr vector types
                same_side = self.parent.is_same_side(self.type.get_refptr_type())
                if self.type.is_const():
                    if same_side:
                        return 'refptr_vec_same_byref_const'
                    return 'refptr_vec_diff_byref_const'
                if same_side:
                    return 'refptr_vec_same_byref'
                return 'refptr_vec_diff_byref'


        # string single map type
        if self.type.is_result_map_single():
            if not self.type.is_byref():
                return 'invalid'
            if self.type.is_const():
                return 'string_map_single_byref_const'
            return 'string_map_single_byref'

        # string multi map type
        if self.type.is_result_map_multi():
            if not self.type.is_byref():
                return 'invalid'
            if self.type.is_const():
                return 'string_map_multi_byref_const'
            return 'string_map_multi_byref'

        return 'invalid'

    def get_retval_type(self):
        """ Returns the retval type as defined in translator.README.txt. """
        if self.type.has_name():
            raise Exception('Cannot be called for argument types')

        # unsupported modifiers
        if self.type.is_const() or self.type.is_byref() or \
            self.type.is_byaddr():
            return 'invalid'

        # void types don't have a return value
        if self.type.get_type() == 'void':
            return 'none'

        if (self.type.is_result_simple() and \
                self.type.get_type() != 'bool') or \
           (self.type.is_result_struct() and self.type.is_result_struct_enum()):
            return 'simple'

        if self.type.get_type() == 'bool':
            return 'bool'

        if self.type.is_result_string():
            return 'string'

        if self.type.is_result_refptr():
            if self.parent.is_same_side(self.type.get_refptr_type()):
                return 'refptr_same'
            else:
                return 'refptr_diff'

        return 'invalid'

    def get_retval_default(self, for_capi):
        """ Returns the default return value based on the retval type. """
        # start with the default retval attribute, if any.
        retval = self.get_attrib_default_retval()
        if not retval is None:
            if for_capi:
                # apply any appropriate C API translations.
                if retval == 'true':
                    return '1'
                if retval == 'false':
                    return '0'
            return retval

        # next look at the retval type value.
        type = self.get_retval_type()
        if type == 'simple':
            return self.get_type().get_result_simple_default()
        elif type == 'bool':
            if for_capi:
                return '0'
            return 'false'
        elif type == 'string':
            if for_capi:
                return 'NULL'
            return 'CefString()'
        elif type == 'refptr_same' or type == 'refptr_diff':
            return 'NULL'

        return ''

class obj_analysis:
    """ Class representing an analysis of a data type value. """

    def __init__(self, scopelist, value, named):
        self.value = value
        self.result_type = 'unknown'
        self.result_value = None
        self.result_default = None
        self.refptr_type = None

        # parse the argument string
        partlist = string.split(string.strip(value))

        if named == True:
            # extract the name value
            self.name = partlist[-1]
            del partlist[-1]
        else:
            self.name = None

        if len(partlist) == 0:
            raise Exception('Invalid argument value: '+value)

        # check const status
        if partlist[0] == 'const':
            self.isconst = True
            del partlist[0]
        else:
            self.isconst = False

        if len(partlist) == 0:
            raise Exception('Invalid argument value: '+value)

        # combine the data type
        self.type = string.join(partlist, ' ')

        # extract the last character of the data type
        endchar = self.type[-1]

        # check if the value is passed by reference
        if endchar == '&':
            self.isbyref = True
            self.type = self.type[:-1]
        else:
            self.isbyref = False

        # check if the value is passed by address
        if endchar == '*':
            self.isbyaddr = True
            self.type = self.type[:-1]
        else:
            self.isbyaddr = False

        # see if the value is directly identifiable
        if self._check_advanced(self.type) == True:
            return

        # not identifiable, so look it up
        translation = None
        for scope in scopelist:
            if not isinstance(scope, obj_header) \
                and not isinstance(scope, obj_class):
                raise Exception('Invalid scope object type')
            translation = scope.get_alias_translation(self.type)
            if not translation is None:
                break

        if translation is None:
            raise Exception('Failed to translate type: '+self.type)

        # the translation succeeded so keep the result
        self.result_type = translation.result_type
        self.result_value = translation.result_value

    def _check_advanced(self, value):
        # check for vectors
        if value.find('std::vector') == 0:
            self.result_type = 'vector'
            val = string.strip(value[12:-1])
            self.result_value = [
                self._get_basic(val)
            ]
            self.result_value[0]['vector_type'] = val
            return True

        # check for maps
        if value.find('std::map') == 0:
            self.result_type = 'map'
            vals = string.split(value[9:-1], ',')
            if len(vals) == 2:
                self.result_value = [
                    self._get_basic(string.strip(vals[0])),
                    self._get_basic(string.strip(vals[1]))
                ]
                return True

        # check for multimaps
        if value.find('std::multimap') == 0:
            self.result_type = 'multimap'
            vals = string.split(value[14:-1], ',')
            if len(vals) == 2:
                self.result_value = [
                    self._get_basic(string.strip(vals[0])),
                    self._get_basic(string.strip(vals[1]))
                ]
                return True

        # check for basic types
        basic = self._get_basic(value)
        if not basic is None:
            self.result_type = basic['result_type']
            self.result_value = basic['result_value']
            if 'refptr_type' in basic:
                self.refptr_type = basic['refptr_type']
            if 'result_default' in basic:
                self.result_default = basic['result_default']
            return True

        return False

    def _get_basic(self, value):
        # check for string values
        if value == "CefString":
            return {
                'result_type' : 'string',
                'result_value' : None
            }

        # check for simple direct translations
        if value in _simpletypes.keys():
            return {
                'result_type' : 'simple',
                'result_value' : _simpletypes[value][0],
                'result_default' : _simpletypes[value][1],
            }

        # check if already a C API structure
        if value[-2:] == '_t':
            return {
                'result_type' : 'structure',
                'result_value' : value
            }

        # check for CEF reference pointers
        p = re.compile('^CefRefPtr<(.*?)>$', re.DOTALL)
        list = p.findall(value)
        if len(list) == 1:
            return {
                'result_type' : 'refptr',
                'result_value' : get_capi_name(list[0], True)+'*',
                'refptr_type' : list[0]
            }

        # check for CEF structure types
        if value[0:3] == 'Cef' and value[-4:] != 'List':
            return {
                'result_type' : 'structure',
                'result_value' : get_capi_name(value, True)
            }

        return None

    def __repr__(self):
        return '('+self.result_type+') '+str(self.result_value)

    def has_name(self):
        """ Returns true if a name value exists. """
        return (not self.name is None)

    def get_name(self):
        """ Return the name. """
        return self.name

    def get_value(self):
        """ Return the C++ value (type + name). """
        return self.value

    def get_type(self):
        """ Return the C++ type. """
        return self.type

    def get_refptr_type(self):
        """ Return the C++ class type referenced by a CefRefPtr. """
        if self.is_result_vector() and self.is_result_vector_refptr():
            # return the vector RefPtr type
            return self.result_value[0]['refptr_type']
        # return the basic RefPtr type
        return self.refptr_type

    def get_vector_type(self):
        """ Return the C++ class type referenced by a std::vector. """
        if self.is_result_vector():
            return self.result_value[0]['vector_type']
        return None

    def is_const(self):
        """ Returns true if the argument value is constant. """
        return self.isconst

    def is_byref(self):
        """ Returns true if the argument is passed by reference. """
        return self.isbyref

    def is_byaddr(self):
        """ Returns true if the argument is passed by address. """
        return self.isbyaddr

    def is_result_simple(self):
        """ Returns true if this is a simple argument type. """
        return (self.result_type == 'simple')

    def get_result_simple_type_root(self):
        """ Return the simple structure or basic type name. """
        return self.result_value

    def get_result_simple_type(self):
        """ Return the simple type. """
        result = ''
        if self.is_const():
            result += 'const '
        result += self.result_value
        if self.is_byaddr() or self.is_byref():
            result += '*'
        return result

    def get_result_simple_default(self):
        """ Return the default value fo the basic type. """
        return self.result_default

    def is_result_refptr(self):
        """ Returns true if this is a reference pointer type. """
        return (self.result_type == 'refptr')

    def get_result_refptr_type_root(self):
        """ Return the refptr type structure name. """
        return self.result_value[:-1]

    def get_result_refptr_type(self, defined_structs = []):
        """ Return the refptr type. """
        result = ''
        if not self.result_value[:-1] in defined_structs:
            result += 'struct _'
        result += self.result_value
        if self.is_byref() or self.is_byaddr():
            result += '*'
        return result

    def is_result_struct(self):
        """ Returns true if this is a structure type. """
        return (self.result_type == 'structure')

    def is_result_struct_enum(self):
        """ Returns true if this struct type is likely an enumeration. """
        # structure values that are passed by reference or address must be
        # structures and not enumerations
        if not self.is_byref() and not self.is_byaddr():
            return True
        return False

    def get_result_struct_type(self, defined_structs = []):
        """ Return the structure or enumeration type. """
        result = ''
        is_enum = self.is_result_struct_enum()
        if not is_enum:
            if self.is_const():
                result += 'const '
            if not self.result_value in defined_structs:
                result += 'struct _'
        result += self.result_value
        if not is_enum:
            result += '*'
        return result

    def is_result_string(self):
        """ Returns true if this is a string type. """
        return (self.result_type == 'string')

    def get_result_string_type(self):
        """ Return the string type. """
        if not self.has_name():
            # Return values are string structs that the user must free. Use
            # the name of the structure as a hint.
            return 'cef_string_userfree_t'
        elif not self.is_const() and (self.is_byref() or self.is_byaddr()):
            # Parameters passed by reference or address. Use the normal
            # non-const string struct.
            return 'cef_string_t*'
        # Const parameters use the const string struct.
        return 'const cef_string_t*'

    def is_result_vector(self):
        """ Returns true if this is a vector type. """
        return (self.result_type == 'vector')

    def is_result_vector_string(self):
        """ Returns true if this is a string vector. """
        return self.result_value[0]['result_type'] == 'string'

    def is_result_vector_simple(self):
        """ Returns true if this is a string vector. """
        return self.result_value[0]['result_type'] == 'simple'

    def is_result_vector_refptr(self):
        """ Returns true if this is a string vector. """
        return self.result_value[0]['result_type'] == 'refptr'

    def get_result_vector_type_root(self):
        """ Return the vector structure or basic type name. """
        return self.result_value[0]['result_value']

    def get_result_vector_type(self, defined_structs = []):
        """ Return the vector type. """
        if not self.has_name():
            raise Exception('Cannot use vector as a return type')

        type = self.result_value[0]['result_type']
        value = self.result_value[0]['result_value']

        result = {}
        if type == 'string':
            result['value'] = 'cef_string_list_t'
            result['format'] = 'single'
            return result

        if type == 'simple':
            str = value
            if self.is_const():
                str += ' const'
            str += '*'
            result['value'] = str
        elif type == 'refptr':
            str = ''
            if not value[:-1] in defined_structs:
                str += 'struct _'
            str += value
            if self.is_const():
                str += ' const'
            str += '*'
            result['value'] = str
        else:
            raise Exception('Unsupported vector type: '+type)

        # vector values must be passed as a value array parameter
        # and a size parameter
        result['format'] = 'multi-arg'
        return result

    def is_result_map(self):
        """ Returns true if this is a map type. """
        return (self.result_type == 'map' or self.result_type == 'multimap')

    def is_result_map_single(self):
        """ Returns true if this is a single map type. """
        return (self.result_type == 'map')

    def is_result_map_multi(self):
        """ Returns true if this is a multi map type. """
        return (self.result_type == 'multimap')

    def get_result_map_type(self, defined_structs = []):
        """ Return the map type. """
        if not self.has_name():
            raise Exception('Cannot use map as a return type')
        if self.result_value[0]['result_type'] == 'string' \
            and self.result_value[1]['result_type'] == 'string':
            if self.result_type == 'map':
                return {
                    'value' : 'cef_string_map_t',
                    'format' : 'single'
                }
            elif self.result_type == 'multimap':
                return {
                    'value' : 'cef_string_multimap_t',
                    'format' : 'multi'
                }
        raise Exception('Only mappings of strings to strings are supported')

    def get_capi(self, defined_structs = []):
        """ Format the value for the C API. """
        result = ''
        format = 'single'
        if self.is_result_simple():
            result += self.get_result_simple_type()
        elif self.is_result_refptr():
            result += self.get_result_refptr_type(defined_structs)
        elif self.is_result_struct():
            result += self.get_result_struct_type(defined_structs)
        elif self.is_result_string():
            result += self.get_result_string_type()
        elif self.is_result_map():
            resdict = self.get_result_map_type(defined_structs)
            if resdict['format'] == 'single' or resdict['format'] == 'multi':
                result += resdict['value']
            else:
                raise Exception('Unsupported map type')
        elif self.is_result_vector():
            resdict = self.get_result_vector_type(defined_structs)
            if resdict['format'] != 'single':
                format = resdict['format']
            result += resdict['value']

        if self.has_name():
            result += ' '+self.get_name()

        return {'format' : format, 'value' : result}


# test the module
if __name__ == "__main__":
    import pprint
    import sys

    # verify that the correct number of command-line arguments are provided
    if len(sys.argv) != 2:
        sys.stderr.write('Usage: '+sys.argv[0]+' <directory>')
        sys.exit()

    pp = pprint.PrettyPrinter(indent=4)

    # create the header object
    header = obj_header()
    header.add_directory(sys.argv[1])

    # output the type mapping
    types = {}
    header.get_types(types)
    pp.pprint(types)
    sys.stdout.write('\n')

    # output the parsed C++ data
    sys.stdout.write(wrap_code(str(header), '\t'))

    # output the C API formatted data
    defined_names = header.get_defined_structs()
    result = ''

    # global functions
    funcs = header.get_funcs()
    if len(funcs) > 0:
        for func in funcs:
            result += func.get_capi_proto(defined_names)+';\n'
        result += '\n'

    classes = header.get_classes()
    for cls in classes:
        # virtual functions are inside a structure
        result += 'struct '+cls.get_capi_name()+'\n{\n'
        funcs = cls.get_virtual_funcs()
        if len(funcs) > 0:
            for func in funcs:
                result += '\t'+func.get_capi_proto(defined_names)+';\n'
        result += '}\n\n'

        defined_names.append(cls.get_capi_name())

        # static functions become global
        funcs = cls.get_static_funcs()
        if len(funcs) > 0:
            for func in funcs:
                result += func.get_capi_proto(defined_names)+';\n'
            result += '\n'
    sys.stdout.write(wrap_code(result, '\t'))