#!/usr/bin/python
#
# Comedy python script to generate cdecl to stdcall wrappers for GL functions
#
# This is designed to operate on OpenGL spec files from
# http://www.opengl.org/registry/api/
#
#
# Copyright (c) Jon TURNEY 2009
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is 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 Software.
#
# THE SOFTWARE IS 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 ABOVE LISTED COPYRIGHT HOLDER(S) 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 SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
#
# Except as contained in this notice, the name(s) of the above copyright
# holders shall not be used in advertising or otherwise to promote the sale,
# use or other dealings in this Software without prior written authorization.
#

import sys
import re
import getopt

dispatchheader = ''
prefix = 'gl'
preresolve = False
staticwrappers = False

opts, args = getopt.getopt(sys.argv[1:], "", ['spec=', 'typemap=', 'dispatch-header=', 'prefix=', 'preresolve', 'staticwrappers' ])

for o,a in opts:
        if o == '--typemap' :
                typemapfile = a
        elif o == '--dispatch-header' :
                dispatchheader = a
        elif o == '--spec' :
                specfile = a
        elif o == '--prefix' :
                prefix = a
        elif o == '--preresolve' :
                preresolve = True
        elif o == '--staticwrappers' :
                staticwrappers = True

#
# look for all the SET_ macros in dispatch.h, this is the set of functions
# we need to generate
#

dispatch = {}

if dispatchheader :
        fh = open(dispatchheader)
        dispatchh = fh.readlines()

        dispatch_regex = re.compile(r'#define\sSET_(\S*)\(')

        for line in dispatchh :
                line = line.strip()
                m1 = dispatch_regex.search(line)

                if m1 :
                        dispatch[m1.group(1)] = 1

        del dispatch['by_offset']

#
# read the typemap .tm file
#

typemap = {}

fh = open(typemapfile)
tm = fh.readlines()

typemap_regex = re.compile(r'#define\sSET_(\S*)\(')

for line in tm :
        # ignore everything after a '#' as a comment
        hash = line.find('#')
        if hash != -1 :
                line = line[:hash-1]

        # ignore blank lines
        if line.startswith('#') or len(line) == 0 :
                continue

        l = line.split(',')
        typemap[l[0]] = l[3].strip()

# interestingly, * is not a C type
if typemap['void'] == '*' :
        typemap['void'] = 'void'

#
# crudely parse the .spec file
#

r1 = re.compile(r'\t(\S*)\s+(\S*.*)')
r2 = re.compile(r'(.*)\((.*)\)')
r3 = re.compile(r'glWindowPos.*MESA')
r4 = re.compile(r'gl.*Program(s|)NV')
r5 = re.compile(r'glGetVertexAttribfvNV')

wrappers = {}

fh = open(specfile)
glspec = fh.readlines()
param_count = 0

for line in glspec :
        line = line.rstrip()

        # ignore everything after a '#' as a comment
        hash = line.find('#')
        if hash != -1 :
                line = line[:hash-1]

        # ignore blank lines
        if line.startswith('#') or len(line) == 0 :
                continue

        # lines containing ':' aren't intersting to us
        if line.count(':') != 0 :
                continue

        # attributes of each function follow the name, indented by a tab
        if not line.startswith('\t') :
                m1 = r2.search(line)
                if m1 :
                        function = m1.group(1)
                        arglist_use = m1.group(2)
                        wrappers[function] = {}

                        # near and far might be reserved words or macros so can't be used as formal parameter names
                        arglist_use = arglist_use.replace('near','zNear')
                        arglist_use = arglist_use.replace('far','zFar')

                        wrappers[function]['arglist_use'] = arglist_use
                        param_count = 0
        else :
                m1 = r1.search(line)
                if m1 :
                        attribute = m1.group(1)
                        value = m1.group(2)

                        # make param attributes unique and ordered
                        if attribute == 'param' :
                                attribute = 'param' + '%02d' % param_count
                                param_count += 1

                        wrappers[function][attribute] = value

#
# now emit code
#

print '/* Automatically generated by ' + sys.argv[0] + ' DO NOT EDIT */'
print '/* from ' + specfile + ' and typemap ' + typemapfile + ' */'
print ''

#
# if required, emit code for non-lazy function resolving
#

if preresolve :
        for w in sorted(wrappers.keys()) :
                funcname = prefix + w
                print 'RESOLVE_DECL(PFN' + funcname.upper() + 'PROC);'

        print ''
        print 'void ' + prefix + 'ResolveExtensionProcs(void)'
        print '{'

        for w in sorted(wrappers.keys()) :
                funcname = prefix + w
                print '  PRERESOLVE(PFN' + funcname.upper() + 'PROC, "' + funcname + '");'

        print '}\n'

#
# now emit the wrappers
# for GL 1.0 and 1.1 functions, generate stdcall wrappers which call the function directly
# for GL 1.2+ functions, generate wrappers which use wglGetProcAddress()
#

for w in sorted(wrappers.keys()) :

        funcname = prefix + w
        returntype = wrappers[w]['return']
        if returntype != 'void' :
                returntype = typemap[returntype]

        # Avoid generating wrappers which aren't referenced by the dispatch table
        if dispatchheader and not dispatch.has_key(w) :
                print '/* No wrapper for ' + funcname + ', not in dispatch table */'
                continue

        # manufacture arglist
        # if no param attributes were found, it should be 'void'
        al = []
        for k in sorted(wrappers[w].keys()) :
                if k.startswith('param') :
                        l = wrappers[w][k].split()

                        # near and far might be reserved words or macros so can't be used as formal parameter names
                        l[0] = l[0].replace('near','zNear')
                        l[0] = l[0].replace('far','zFar')

                        if l[2] == 'in' :
                                if l[3] == 'array' :
                                        arg = 'const ' + typemap[l[1]] + ' *' + l[0]
                                else :
                                        arg = typemap[l[1]] + ' ' + l[0]
                        elif l[2] == 'out' :
                                arg = typemap[l[1]] + ' *' + l[0]

                        al.append(arg)

        if len(al) == 0 :
                arglist = 'void'
        else:
                arglist  = ', '.join(al)

        if wrappers[w]['category'].startswith('VERSION_1_0') or wrappers[w]['category'].startswith('VERSION_1_1') :
                if staticwrappers :
                        print 'static',
                print returntype + ' ' + funcname + 'Wrapper(' + arglist + ')'
                print '{'
                print '  if (glxWinDebugSettings.enable' + prefix.upper() + 'callTrace) ErrorF("'+ funcname + '\\n");'
                print '  glWinDirectProcCalls++;'
                if returntype.lower() == 'void' :
                        print '  ' +  funcname + '(',
                else :
                        print ' /* returntype was ' + returntype.lower() + '*/'
                        print '  return ' +  funcname + '(',

                if arglist != 'void' :
                        print wrappers[w]['arglist_use'],

                print ');'
                print "}\n"
        else:
                if staticwrappers :
                        print 'static',
                print returntype + ' ' + funcname + 'Wrapper(' + arglist + ')'
                print '{'

                stringname = funcname

#
# special case: Windows OpenGL implementations are far more likely to have GL_ARB_window_pos than GL_MESA_window_pos,
# so arrange for the wrapper to use the ARB strings to find functions...
#

                m2 = r3.search(funcname)
                if m2 :
                        stringname = stringname.replace('MESA','ARB')

#
# special case: likewise, implementations are more likely to have GL_ARB_vertex_program than GL_NV_vertex_program,
# especially if they are not NV implementations, so arrange for the wrapper to use ARB strings to find functions
#

                m3 = r4.search(funcname)
                if m3 :
                        stringname = stringname.replace('NV','ARB')
                m4 = r5.search(funcname)
                if m4 :
                        stringname = stringname.replace('NV','ARB')

                pfntypename = 'PFN' + funcname.upper() + 'PROC'

                if returntype.lower() == 'void' :
                        print '  RESOLVE(' + pfntypename + ', "' + stringname + '");'
                        print '  if (glxWinDebugSettings.enable' + prefix.upper() + 'callTrace) ErrorF("'+ funcname + '\\n");'
                        print '  RESOLVED_PROC(' + pfntypename + ')(',
                else :
                        print '  RESOLVE_RET(' + pfntypename + ', "' + stringname + '", FALSE);'
                        print '  if (glxWinDebugSettings.enable' + prefix.upper() + 'callTrace) ErrorF("'+ funcname + '\\n");'
                        print '  return RESOLVED_PROC(' + pfntypename + ')(',

                if arglist != 'void' :
                        print wrappers[w]['arglist_use'],

                print ');'
                print "}\n"


# generate function to setup the dispatch table, which sets each
# dispatch table entry to point to it's wrapper function
# (assuming we were able to make one)

if dispatchheader :
        print 'void glWinSetupDispatchTable(void)'
        print '{'
        print '  struct _glapi_table *disp = _glapi_get_dispatch();'

        for d in sorted(dispatch.keys()) :
                if wrappers.has_key(d) :
                        print '  SET_'+ d + '(disp, (void *)' + prefix + d + 'Wrapper);'
                else :
                        print '#warning  No wrapper for ' + prefix + d + ' !'

        print '}'
