# This file generates GLEW include files suitable for use with Swig.
# It scans the original GLEW file and replaces the defines of extension
# functions with "real" function declarations as needed by Swig for wrapping.
# It also creates a __glFuncAvail function for each OpenGL function, which can
# be used to check the availability of an extension function in the used
# OpenGL driver.
#
# For example the script uses the following two lines to extract the 
# return value, the parameter list and the function name:
#
#    typedef void (GLAPIENTRY * PFNGLACTIVETEXTUREPROC) (GLenum texture);
#    #define glActiveTexture GLEW_GET_FUN(__glewActiveTexture)
#
# These lines are tranformed into the following statements: 
#
#    %{
#    static int __glActiveTextureAvail (void) { return (__glewActiveTexture != NULL); }
#    %}
#    void glActiveTexture (GLenum texture);
#    int  __glActiveTextureAvail (void);
#
# See also the ReadmeGlew.txt file in directory GLSpec.

set inGlFile   "GL/glew.h"
set inGluFile  "GL/Linux/GL/glu.h"
set glSpecFile "GLSpec/gl.spec"
set glRefFile  "GLSpec/glFuncUrlList.txt"
set glExtFile  "GLSpec/glExtUrlList.txt"
set glTmplFile "GLSpec/tcl3dOglHelp.txt"

set swigFuncFile "swigfiles/glewfuncs.i"
set swigDefFile  "swigfiles/glewdefs.i"
set helpFile     "tclfiles/tcl3dOglHelp.tcl"

set genHelpFile true
set genSwigFile true
if { $argc >= 1 } {
    if { [lindex $argv 0] eq "-swig" } {
        set genHelpFile false
    }
    if { [lindex $argv 0] eq "-help" } {
        set genSwigFile false
    }
}

puts "Processing GLEW file $inGlFile ..."
set inFp [open $inGlFile "r"]

set foundTypedef false
set glEnumNameList [list]
set glEnumValList  [list]
set glEnumVersList [list]

while { [gets $inFp line] >= 0 } {
    if { [string match "/\**-----*" $line] } {
        set marker [string trim $line "/* -"]
        if { [string match "GL_*" $marker] } {
            set curVersOrExt $marker
        }
    } elseif { [string match "typedef*PFNGL*" $line] } {
        # Found a typedef line.
        set foundTypedef true

        # Extract the return value.
        # Skip the word "typedef" and use all characters till the first "(".
        set startInd [string wordend $line 0]
        set endInd   [expr [string first "(" $line] -1]
        set retVal   [string trim [string range $line $startInd $endInd]]

        # Extract the function name with all letters uppercase.
        set startInd [string first "PFNGL" $line]
        set pfStr [string range $line $startInd end]
        scan $pfStr "PFN%s" funcName
        set startInd [string first "PROC" $funcName]
        set funcName [string range $funcName 0 [expr $startInd -1]]

        # Extract the parameter list.
        set startInd [string first "(" $pfStr]
        set params [string range $pfStr $startInd end]

        # Store the parameter list and the return value in arrays.
        # Key is the uppercase function name.
        set paramList($funcName) $params
        set retList($funcName)   $retVal

    } elseif { [string match "#define*" $line] } {
        # Found a define line.
        # We need to check, if it is a define of a function specified earlier
        # with a typedef line.
        set foundDefine false
        if { $foundTypedef } {
            # Extract the real function name.
            scan $line "#define %s" realFuncName
            set funcName [string toupper $realFuncName]
            if { [info exists paramList($funcName)] } {
                # The name specified in the define line is a function name.
                # Save the SWIG-ready function declaration.
                lappend glExtFuncList $realFuncName
                lappend glExtDefList  "$retList($funcName) $realFuncName $paramList($funcName)"
                lappend glExtVersList "$curVersOrExt"

                # Extract the GLEW function pointer name.
                set startInd [string first "(" $line]
                set glewFuncPtr [string range $line $startInd end]
                set glewFuncPtr [string trim $glewFuncPtr " )("]
                lappend glExtFuncPtr $glewFuncPtr

                set foundDefine true
            }
        }
        if { ! $foundDefine } {
            # If the define was not a function, search for the GLenum definitions.
            if { [string match "#define GL_*" $line] } {
                scan $line "#define %s %s" enumName enumVal
                # In the GLEW header file, some extensions specify defines, which are
                # already specified by an OpenGL version, ex. GL_FLOAT or GL_UNSIGNED_INT.
                # Do not insert these duplicate values into the enumeration lists.
                if { [lsearch -exact $glEnumNameList $enumName] < 0 } {  
                    lappend glEnumNameList $enumName
                    lappend glEnumValList  $enumVal
                    lappend glEnumVersList $curVersOrExt
                }
            }
        }
    } elseif { [string match "GLAPI *" $line] } {
        # These are the GL 1.1 version available without GetProcAdress.
        set startInd [string first "GLAPIENTRY" $line]
        set startInd [expr [string wordend $line $startInd] + 1]
        set endInd   [string wordend $line $startInd]

        set defStr [string trim [string map {"GLAPIENTRY" "" "GLAPI" ""} $line]]

        lappend glCoreFuncList [string trim [string range $line $startInd $endInd]]
        lappend glCoreDefList  "$defStr"
        lappend glCoreVersList "$curVersOrExt"
    }
}
close $inFp

puts "Processing GLU file $inGluFile ..."
set inFp [open $inGluFile "r"]
while { [gets $inFp line] >= 0 } {
    if { [string match "extern *" $line] } {
        if { [string match "extern \"C\" *" $line] } {
            continue
        }
        set startInd [string first "glu" $line]
        scan [string range $line $startInd end] "%s" funcName
        set defStr [string trim [string map {"extern" ""} $line]]

        lappend gluFuncList $funcName
        lappend gluDefList  "$defStr"
    }
}
close $inFp

puts "Processing GL specification file $glSpecFile ..."
set glSpecFp [open $glSpecFile "r"]

while { [gets $glSpecFp line] >= 0 } {
    set retVal1 [regexp {^([A-z,0-9]*)\(.*\).*} $line total funcName]
    if { $retVal1 } {
        # puts "$funcName"
    }
    set retVal [regexp {^[\s]+deprecated[\s]+([0-9]+\.[0-9]+)} \
                       $line total version]
    if { $retVal } {
        # puts "gl$funcName $version"
        set deprecated(gl$funcName) $version
    }
}
close $glSpecFp

puts "Processing GL URL reference file $glRefFile ..."
set glRefFp [open $glRefFile "r"]
while { [gets $glRefFp line] >= 0 } {
    scan $line "%s %s" funcName urlName
    set url($funcName) $urlName
}
close $glRefFp

puts "Processing GL extension reference file $glExtFile ..."
set glExtFp [open $glExtFile "r"]
while { [gets $glExtFp line] >= 0 } {
    scan $line "%s %s" extName urlName
    set extUrl($extName) $urlName
}
close $glExtFp

if { $genHelpFile } {
    puts "Processing help template file $glTmplFile ..."
    set glTmplFp [open $glTmplFile "r"]
    set glTmplCont [read $glTmplFp]
    close $glTmplFp

    puts "  Writing Tcl helper file $helpFile ..."
    # Open the tcl3dOglHelp.txt file and put the contents of the template 
    # file first.
    set helpFp [open $helpFile "w"]
    puts $helpFp $glTmplCont

    lappend glAllFuncList $glCoreFuncList $glExtFuncList
    puts $helpFp "# List of the names of all wrapped OpenGL functions."
    puts $helpFp "set ::__tcl3dOglFuncList \[list \\"
    foreach glFunc [join $glAllFuncList] {
        puts $helpFp "  $glFunc \\"
    }
    puts $helpFp "\]\n"
    
    lappend glAllDefList $glCoreDefList $glExtDefList
    puts $helpFp "# List of the C-signatures of all wrapped OpenGL functions."
    puts $helpFp "set ::__tcl3dOglFuncSignatureList \[list \\"
    foreach glDef [join $glAllDefList] {
        set sig [string trim $glDef ";"]
        set sig [string map {"[" "\\[" "]" "\\]"} $sig]
        puts $helpFp "  \"$sig\" \\"
    }
    puts $helpFp "\]\n"
    
    lappend glAllVersList $glCoreVersList $glExtVersList
    puts $helpFp "# List of the OpenGL versions or extensions of all wrapped OpenGL functions."
    puts $helpFp "set ::__tcl3dOglFuncVersionList \[list \\"
    foreach glVers [join $glAllVersList] {
        puts $helpFp "  $glVers \\"
    }
    puts $helpFp "\]\n"

    puts $helpFp "# List of the deprecation version of all wrapped OpenGL functions."
    puts $helpFp "set ::__tcl3dOglFuncDeprecatedList \[list \\"
    foreach glFunc [join $glAllFuncList] {
        set deprVersion 0.0
        if { [info exists deprecated($glFunc)] } {
            set deprVersion $deprecated($glFunc)
        }
        puts $helpFp "  $deprVersion \\"
    }
    puts $helpFp "\]\n"

    puts $helpFp "# List of the reference URLs of all wrapped OpenGL functions."
    puts $helpFp "set ::__tcl3dOglFuncUrlList \[list \\"
    foreach glFunc [join $glAllFuncList] {
        set urlName "http://ERROR"
        if { [info exists url($glFunc)] } {
            set urlName $url($glFunc)
        }
        puts $helpFp "  \"$urlName\" \\"
    }
    puts $helpFp "\]\n"

    puts $helpFp "# Array of extension names and corresponding URLs"
    puts $helpFp "array set ::__tcl3dOglExtensionList {"
    foreach {extName urlName} [array get extUrl] {
        puts $helpFp "    \"$extName\" \"$urlName\""
    }
    puts $helpFp "}\n"

    puts $helpFp "# Array of enums and corresponding OpenGL version or extension."
    puts $helpFp "array set ::__tcl3dOglEnumVersion {"
    foreach enumName $glEnumNameList enumVers $glEnumVersList {
        puts $helpFp "    \"$enumName\" \"$enumVers\""
    }
    puts $helpFp "}\n"

    puts $helpFp "# List of the names of all wrapped GLU functions."
    puts $helpFp "set ::__tcl3dGluFuncList \[list \\"
    foreach gluFunc $gluFuncList {
        puts $helpFp "  $gluFunc \\"
    }
    puts $helpFp "\]\n"
    
    puts $helpFp "# List of the C-signatures of all wrapped GLU functions."
    puts $helpFp "set ::__tcl3dGluFuncSignatureList \[list \\"
    foreach gluDef $gluDefList {
        set sig [string trim $gluDef ";"]
        set sig [string map {"[" "\\[" "]" "\\]"} $sig]
        puts $helpFp "  \"$sig\" \\"
    }
    puts $helpFp "\]\n"

    puts $helpFp "# List of the reference URLs of all wrapped GLU functions."
    puts $helpFp "set ::__tcl3dGluFuncUrlList \[list \\"
    foreach gluFunc $gluFuncList {
        set urlName "http://ERROR"
        if { [info exists url($gluFunc)] } {
            set urlName $url($gluFunc)
        }
        puts $helpFp "  \"$urlName\" \\"
    }
    puts $helpFp "\]\n"

    close $helpFp
}

if { $genSwigFile } {
    puts "  Writing SWIG wrapper file $swigDefFile ..."
    set swigFp [open $swigDefFile "w"]
    puts $swigFp "/* This is an automatically generated file. Do not edit. */\n"

    # Write the declarations of all constants and functions being wrapped by SWIG.
    puts $swigFp "/* OpenGL enum definitions */"

    # Scan all enumerations, if there are definitions of the form
    # #define GL_NEW GL_OLD. As GL_OLD may be defined later in the
    # glew.h file, SWIG can not replace GL_NEW with the real integer value.
    # First find all not yet defined enumerations.
    foreach enumName $glEnumNameList enumVal $glEnumValList {
        if { [string is integer $enumVal] } {
            set enumDefs($enumName) $enumVal
        } else {
            set enumUndefs($enumName) $enumVal 
        }
    }
    # Then scan all undefined enumerations and find their integer values.
    foreach key [array names enumUndefs] {
        set val $enumUndefs($key)
        if { [info exists enumDefs($val)] } {
            # puts "Replacing $key with $enumDefs($val)"
            set enumDefs($key) $enumDefs($val)
        } else {
            puts "ERROR: Cannot find definition for $key"
        }
    }

    # Now all enumerations have their integer values associated.
    # Write them out sorted alphabetically.
    foreach enumKey [lsort -dictionary [array names enumDefs]] {
        puts $swigFp "#define $enumKey $enumDefs($enumKey)"
    }

    close $swigFp

    puts "  Writing SWIG wrapper file $swigFuncFile ..."
    # Open the SWIG glewautogen.i file.
    set swigFp [open $swigFuncFile "w"]
    puts $swigFp "/* This is an automatically generated file. Do not edit. */\n"
    
    # Write the definitions of the functions checking for the existence of an 
    # OpenGL extension function.
    puts $swigFp "%\{"

    puts $swigFp "#include \"GL/glew.h\""

    # All core functions (OpenGL 1.1) are available in any case.
    foreach coreFunc $glCoreFuncList {
        set defStr [format "static int __%sAvail (void) \{ return 1; \}" $coreFunc]
        puts $swigFp $defStr
    }

    # For functions introduced after OpenGL 1.1 check the GLEW generated function pointer.
    foreach extFunc $glExtFuncList funcPtr $glExtFuncPtr {
        set bodyStr [format "return (%s != NULL)" $funcPtr]
        set defStr [format "static int __%sAvail (void) \{ %s; \}" $extFunc $bodyStr]
        puts $swigFp $defStr
    }

    puts $swigFp "%\}"

    puts $swigFp "/* Wrapper for OpenGL core functions in version 1.1 */"
    foreach coreFunc $glCoreFuncList coreDef $glCoreDefList coreVers $glCoreVersList {
        puts $swigFp $coreDef
    }

    puts $swigFp ""
    puts $swigFp "/* Wrapper for OpenGL extension functions above version 1.1 */"
    foreach extFunc $glExtFuncList extDef $glExtDefList extVers $glExtVersList {
        puts $swigFp $extDef
    }

    foreach coreFunc $glCoreFuncList {
        set defStr [format "static int __%sAvail (void);" $coreFunc]
        puts $swigFp $defStr
    }

    foreach extFunc $glExtFuncList {
        set defStr [format "static int __%sAvail (void);" $extFunc]
        puts $swigFp $defStr
    }

    # Add the GLEW initialization function.
    puts $swigFp "GLenum glewInit ();"
    puts $swigFp "const char* glewGetString (GLenum name);"

    close $swigFp
}

puts "Done."
exit 0
