# Copyright:      2010-2012 Paul Obermeier (obermeier@tcl3d.org)
#
#                 See the file "Tcl3D_License.txt" for information on 
#                 usage and redistribution of this file, and for a
#                 DISCLAIMER OF ALL WARRANTIES.
#
# Module:         Tcl3D
# Filename:       OglInfo.tcl
#
# Author:         Paul Obermeier
#
# Description:    Tcl script to display OpenGL related information.
#                 When called without arguments, a window is opened with
#                 buttons to display OpenGL information for the following
#                 categories: 
#                 - General information                           (-info)
#                 - Available OpenGL commands in Tcl              (-cmd)
#                 - Available OpenGL enumerations in Tcl          (-enum)
#
#                 The information texts can also be printed to stdout
#                 whithout opening a GUI, if calling this Tcl script
#                 with any of the above listed command line options.
#                 To display all three categories, the option "-all"
#                 can be used.
#
#                 Note: To retrieve all necessary information, an OpenGL
#                       context has to be established. So the batch mode
#                       needs a DISPLAY, too.

package require tablelist
package require tcl3dogl

set retVal [catch {package require tcomexcel} version]
set gPo(tcomexcel,avail) [expr !$retVal]

tcl3dAddEvents

proc bgerror { msg } {
    tk_messageBox -icon error -type ok -message "Error: $msg\n\n$::errorInfo"
    exit
}

proc Focus { w } {
    # puts "focus  $w"
    focus $w
}

proc AddMenuCmd { menu label acc cmd args } {
    eval {$menu add command -label $label -accelerator $acc -command $cmd} $args
}
 
proc AddMenuRadio { menu label acc var val cmd args } {
    eval {$menu add radiobutton -label $label -accelerator $acc \
                                -variable $var -value $val -command $cmd} $args
}

proc AddMenuCheck { menu label acc var cmd args } {
    eval {$menu add checkbutton -label $label -accelerator $acc \
                                -variable $var -command $cmd} $args
}

proc ChangeTab { tabInd } {
    global gPo

    $gPo(notebookId) select $tabInd
    set gPo(curTab) $tabInd
}

proc ChangedTab { notebookId } {
    global gPo

    set gPo(curTab) [$notebookId index [$notebookId select]]
}

proc UpdateCombo { comboId typeList { showInd -1 } } {
    $comboId configure -values $typeList
    if { $showInd >= 0 } {
        $comboId current $showInd
    }
}

proc ExecFromHistory { comboId } {
    global gPo

    set ind [$comboId current]
    set item [lindex $gPo(historyView) $ind]
    set cmd [lindex $gPo(historyCmds) $ind]
    # puts "ExecFromHistory: $cmd"
    eval $cmd
    SelectEntry $item $gPo(selTableId,$gPo(curTab)) 0
}

proc AskOpenFile { textId } {
    global gPo

    set fileTypes {
        {{All files}   *}
        {{Tcl files}   {.tcl}}
        {{C/C++ files} {.c .cpp}}
        {{Text files}  {.txt}}
    }

    if { ! [file isdirectory $gPo(lastDir)] } {
        set gPo(lastDir) [pwd]
    }
    set fileName [tk_getOpenFile -filetypes $fileTypes \
                                 -initialdir $gPo(lastDir)]
    if { $fileName ne "" } {
        set gPo(lastDir) [file dirname $fileName]
    }
}

proc LoadFileIntoTextWidget { fileName textId } {
    global gPo

    set retVal [catch {open $fileName r} fp]
    if { $retVal != 0 } {
        error "Could not open file $fileName for reading."
    }

    set curState [$textId cget -state]
    $textId configure -state normal
    $textId delete 1.0 end
    while { ![eof $fp] } {
        $textId insert end [read $fp 2048]
    }
    $textId configure -state $curState
    close $fp
    set gPo(curCheckFile) [file tail $fileName]
}

proc CheckFile { textId cmdTableId enumTableId } {
    global gPo gOpts gCmdInd gEnumInd

    catch { unset gCmdInd }
    catch { unset gEnumInd }

    scan [$textId index end] %d noLines
    for { set i 1 } { $i < $noLines } { incr i } {
        set line [$textId get $i.0 $i.end]
        set ind 0
        set len [string length $line]
        set retVal 1
        while { $retVal && $ind < $len } {
            set retVal [regexp -start $ind -indices {gl([A-z,0-9]+)} $line total indList]
            if { $retVal } {
                set ws [expr {[lindex $indList 0] -2}]
                set we [lindex $indList 1]
                set func [string range $line $ws $we]
                set found [tcl3dOglIsFuncWrapped $func]
                if { $found } {
                    lappend gCmdInd($func) "$i.$ws"
                    $textId tag add "GLCmd" $i.$ws "$i.$ws + [string length $func] chars"
                }
                set ind [expr {$we + 1}]
            }
            set retVal [regexp -start $ind -indices {GL_([A-z,0-9,_]+)} $line total indList]
            if { $retVal } {
                set ws [expr {[lindex $indList 0] -3}]
                set we [lindex $indList 1]
                set enum [string range $line $ws $we]
                set found [tcl3dOglGetEnumVersion $enum]
                if { $found ne "" } {
                    lappend gEnumInd($enum) "$i.$ws"
                    $textId tag add "GLEnum" $i.$ws "$i.$ws + [string length $enum] chars"
                }
                set ind [expr {$we + 1}]
            }
        }
    }
    $textId tag configure "GLCmd"  -background $gOpts(cmdColor)
    $textId tag configure "GLEnum" -background $gOpts(enumColor)
    set funcList [lsort [array names gCmdInd]]
    _CmdInfo $gPo(chkTabInd) $gPo(cmdTableId,$gPo(chkTabInd)) $funcList \
             "Commands of $gPo(curCheckFile)" "ShowGLCommands"
    set enumList [lsort [array names gEnumInd]]
    _EnumInfo $gPo(chkTabInd) $gPo(enumTableId,$gPo(chkTabInd)) $enumList \
              "Enums of $gPo(curCheckFile)" "ShowGLEnums" true
}

proc GetComboValue { comboId textId } {
    global gPo

    set fileName [tcl3dDirSelect::GetValue $comboId]
    LoadFileIntoTextWidget $fileName $textId
    CheckFile $textId $gPo(cmdTableId,$gPo(chkTabInd)) $gPo(enumTableId,$gPo(chkTabInd))
}

proc ShowMainWin {title} {
    global gPo

    # Create the windows title.
    wm title . $title

    ttk::frame .fr -relief sunken -borderwidth 1
    pack .fr -side top -fill both -expand 1

    ttk::frame .fr.toolfr
    ttk::frame .fr.workfr
    ttk::frame .fr.infofr
    ttk::frame .fr.toglfr

    grid .fr.toolfr -row 0 -column 0 -sticky nwse
    grid .fr.workfr -row 1 -column 0 -sticky nwse
    grid .fr.infofr -row 2 -column 0 -sticky nwse
    grid .fr.toglfr -row 3 -column 0 -sticky nwse
    grid rowconfigure .fr 1 -weight 1
    grid columnconfigure .fr 0 -weight 1

    # Create menus File and Help
    set hMenu .fr.menufr
    menu $hMenu -borderwidth 2 -relief sunken
    if { $::tcl_platform(os) eq "Darwin" } {
        $hMenu add cascade -menu $hMenu.apple -label $gPo(appName)
        set appleMenu  $hMenu.apple
        menu $appleMenu -tearoff 0
        AddMenuCmd $appleMenu "About $gPo(appName) ..." ""  HelpAbout
    }
    $hMenu add cascade -menu $hMenu.file -label "File" -underline 0
    $hMenu add cascade -menu $hMenu.help -label "Help" -underline 0

    set fileMenu $hMenu.file
    menu $fileMenu -tearoff 0

    AddMenuCmd $fileMenu "Save current table as CSV file ..." "Ctrl+S" "AskSaveCsvFile false"
    AddMenuCmd $fileMenu "Save all tables as CSV files ..."   "Ctrl+D" "AskSaveCsvFile true"
    bind . <Control-s>  "AskSaveCsvFile false"
    bind . <Control-d>  "AskSaveCsvFile true"

    if { $gPo(tcomexcel,avail) && $::tcl_platform(platform) eq "windows" } {
        AddMenuCmd $fileMenu "Save current table as Excel file ..." "Ctrl+R" "AskSaveExcelFile false"
        AddMenuCmd $fileMenu "Save all tables as Excel file ..."    "Ctrl+T" "AskSaveExcelFile true"
        AddMenuCmd $fileMenu "Load table to Excel ..."              "Ctrl+E" LoadToExcel
        bind . <Control-r>  "AskSaveExcelFile false"
        bind . <Control-t>  "AskSaveExcelFile true"
        bind . <Control-e>  LoadToExcel
    }
    if { $::tcl_platform(os) ne "Darwin" } {
        AddMenuCmd $fileMenu "Quit" "Ctrl+Q" ExitProg
    }
    bind . <Control-q>  ExitProg
    wm protocol . WM_DELETE_WINDOW "ExitProg"

    set helpMenu $hMenu.help
    menu $helpMenu -tearoff 0
    AddMenuCmd $helpMenu "Help ..."                "F1" HelpCont
    AddMenuCmd $helpMenu "Open reference link"     "F2" HelpLinks
    AddMenuCmd $helpMenu "About $gPo(appName) ..." ""   HelpAbout
    bind . <Key-F1> HelpCont
    bind . <Key-F2> HelpLinks

    . configure -menu $hMenu

    # Create toolbar.
    set toolfr .fr.toolfr
    ttk::label $toolfr.lSearch -text "Search:"
    set gPo(searchLabel) $toolfr.searchLabel
    ttk::label $gPo(searchLabel) -width 8 -textvariable gPo(searchKey)
    pack $toolfr.lSearch $gPo(searchLabel) -side left

    ttk::label $toolfr.lHistory -text "History:"
    set gPo(historyCombo) $toolfr.comboHistory
    ttk::combobox $gPo(historyCombo) -width 30 -state readonly
    UpdateCombo $gPo(historyCombo) $gPo(historyCmds)
    bind $gPo(historyCombo) <<ComboboxSelected>> "ExecFromHistory %W"
    pack $toolfr.lHistory $gPo(historyCombo) -side left

    set gPo(progress,widget) $toolfr.lProgress
    set gPo(progress,value)  0
    ttk::progressbar $gPo(progress,widget) -mode determinate -variable gPo(progress,value)
    pack $gPo(progress,widget) -side right
    
    # Create the notebook and tabs for general infos, version infos and file checking.
    set nb .fr.workfr.nb
    set gPo(notebookId) $nb
    ttk::notebook $nb
    pack $nb -fill both -expand 1 -padx 2 -pady 3
    ttk::notebook::enableTraversal $nb
    bind $nb <<NotebookTabChanged>> "ChangedTab %W"

    set genTab $nb.generalFr
    set genTabInd 0
    set gPo(genTabInd) $genTabInd
    ttk::frame $genTab
    $nb add $genTab -text "General infos" -underline 0 -padding 2
    ttk::panedwindow $genTab.pane -orient horizontal
    pack $genTab.pane -side top -expand 1 -fill both
    ttk::frame $genTab.pane.catfr
    ttk::frame $genTab.pane.resfr
    pack $genTab.pane.catfr -expand 1 -fill both -side left
    pack $genTab.pane.resfr -expand 1 -fill both -side left
    $genTab.pane add $genTab.pane.catfr
    $genTab.pane add $genTab.pane.resfr

    set verTab $nb.versionFr
    set verTabInd 1
    set gPo(verTabInd) $verTabInd
    ttk::frame $verTab
    $nb add $verTab -text "Version infos" -underline 0 -padding 2
    ttk::panedwindow $verTab.pane -orient horizontal
    pack $verTab.pane -side top -expand 1 -fill both
    ttk::frame $verTab.pane.catfr
    ttk::frame $verTab.pane.resfr
    pack $verTab.pane.catfr -expand 1 -fill both -side left
    pack $verTab.pane.resfr -expand 1 -fill both -side left
    $verTab.pane add $verTab.pane.catfr
    $verTab.pane add $verTab.pane.resfr

    set chkTab $nb.checkFr
    set chkTabInd 2
    set gPo(chkTabInd) $chkTabInd
    ttk::frame $chkTab
    $nb add $chkTab -text "Check file" -underline 0 -padding 2
    ttk::panedwindow $chkTab.pane -orient horizontal
    pack $chkTab.pane -side top -expand 1 -fill both
    ttk::frame $chkTab.pane.catfr
    ttk::frame $chkTab.pane.resfr
    pack $chkTab.pane.catfr -expand 1 -fill both -side left
    pack $chkTab.pane.resfr -expand 1 -fill both -side left
    $chkTab.pane add $chkTab.pane.catfr
    $chkTab.pane add $chkTab.pane.resfr

    # Fill the General info notebook tab.
    set gPo(selTableId,$genTabInd) [tcl3dCreateScrolledTablelist $genTab.pane.catfr "Categories" \
                  -stripebackground #e0e8f0 \
                  -exportselection false \
                  -labelcommand tablelist::sortByColumn \
                  -showseparators yes -showlabels false -stretch all]
    bind $gPo(selTableId,$genTabInd) <<TablelistSelect>> "ExecCmd $gPo(selTableId,$genTabInd)"
    bind $gPo(selTableId,$genTabInd) <<TablelistColumnSorted>> "AddSortToHistory $gPo(selTableId,$genTabInd)"
    $gPo(selTableId,$genTabInd) configure -columns {0 "Keywords" "left" 
                                                    0 "Command"  "left"}
    $gPo(selTableId,$genTabInd) columnconfigure 0 -sortmode ascii
    $gPo(selTableId,$genTabInd) columnconfigure 1 -hide true

    set gPo(cmdTableId,$genTabInd) [tcl3dCreateScrolledTablelist $genTab.pane.resfr "Result window" \
                  -stripebackground #e0e8f0 \
                  -labelcommand tablelist::sortByColumn \
                  -showseparators yes -width 100 -height 20]
    bind $gPo(cmdTableId,$genTabInd) <<TablelistSelect>> "ShowDetail $gPo(cmdTableId,$genTabInd)"
    bind $gPo(cmdTableId,$genTabInd) <<TablelistColumnSorted>> "AddSortToHistory $gPo(cmdTableId,$genTabInd)"

    set tableId $gPo(cmdTableId,$genTabInd)
    set gPo(catList) [list \
        [list "GL information" "ShowGLInfo      \"GL information\" $gPo(genTabInd) $tableId" ] \
        [list "GL extensions"  "ShowExtensions  \"GL extensions\"  $gPo(genTabInd) $tableId" ] \
        [list "GL commands"    "ShowGLCommands  \"GL commands\"    $gPo(genTabInd) $tableId" ] \
        [list "GL enums"       "ShowGLEnums     \"GL enums\"       $gPo(genTabInd) $tableId" ] \
        [list "GL state vars"  "ShowGLStateVars \"GL state vars\"  $gPo(genTabInd) $tableId" ] \
        [list "GLU commands"   "ShowGLUCommands \"GLU commands\"   $gPo(genTabInd) $tableId" ] \
        [list "GLU enums"      "ShowGLUEnums    \"GLU enums\"      $gPo(genTabInd) $tableId" ] \
    ]

    $gPo(selTableId,$genTabInd) delete 0 end
    foreach catInfo $gPo(catList) {
        $gPo(selTableId,$genTabInd) insert end $catInfo
    }

    set gPo(curTab)     $genTabInd
    set gPo(curTableId) $gPo(cmdTableId,$gPo(curTab))

    # Fill the Version info notebook tab.
    set gPo(selTableId,$verTabInd) [tcl3dCreateScrolledTablelist $verTab.pane.catfr "Versions" \
                  -stripebackground #e0e8f0 \
                  -exportselection false \
                  -labelcommand tablelist::sortByColumn \
                  -selectmode single \
                  -showseparators yes -showlabels false -stretch all]
    bind $gPo(selTableId,$verTabInd) <<TablelistSelect>> "ExecCmd $gPo(selTableId,$verTabInd)"
    bind $gPo(selTableId,$verTabInd) <<TablelistColumnSorted>> "AddSortToHistory $gPo(selTableId,$verTabInd)"
    $gPo(selTableId,$verTabInd) configure -columns {0 "Keywords" "left"
                                                    0 "Command"  "left"}
    $gPo(selTableId,$verTabInd) columnconfigure 0 -sortmode ascii
    $gPo(selTableId,$verTabInd) columnconfigure 1 -hide true

    set pane $verTab.pane.resfr.pane
    ttk::panedwindow $pane -orient vertical
    pack $pane -side top -expand 1 -fill both
    ttk::frame $pane.cmdfr
    ttk::frame $pane.enumfr
    pack $pane.cmdfr  -expand 1 -fill both -side left
    pack $pane.enumfr -expand 1 -fill both -side left
    $pane add $pane.cmdfr
    $pane add $pane.enumfr

    set gPo(cmdTableId,$verTabInd) [tcl3dCreateScrolledTablelist $pane.cmdfr "Command list" \
                  -stripebackground #e0e8f0 \
                  -labelcommand tablelist::sortByColumn \
                  -showseparators yes -width 100 -height 10]
    bind $gPo(cmdTableId,$verTabInd) <<TablelistSelect>> "ShowDetail $gPo(cmdTableId,$verTabInd)"
    bind $gPo(cmdTableId,$verTabInd) <<TablelistColumnSorted>> "AddSortToHistory $gPo(cmdTableId,$verTabInd)"

    set gPo(enumTableId,$verTabInd) [tcl3dCreateScrolledTablelist $pane.enumfr "Enum list" \
                  -stripebackground #e0e8f0 \
                  -labelcommand tablelist::sortByColumn \
                  -showseparators yes -width 100 -height 10]
    bind $gPo(enumTableId,$verTabInd) <<TablelistSelect>> "ShowDetail $gPo(enumTableId,$verTabInd)"
    bind $gPo(enumTableId,$verTabInd) <<TablelistColumnSorted>> "AddSortToHistory $gPo(enumTableId,$verTabInd)"

    set cmdTableId  $gPo(cmdTableId,$verTabInd)
    set enumTableId $gPo(enumTableId,$verTabInd)
    $gPo(selTableId,$verTabInd) delete 0 end
    foreach extVers [tcl3dOglGetVersionList] {
        $gPo(selTableId,$verTabInd) insert end \
            [list $extVers "ShowVersionInfo $extVers $gPo(verTabInd) $cmdTableId $enumTableId"]
    }

    bind [$gPo(cmdTableId,$genTabInd) bodypath] <<RightMousePress>> \
         "OpenContextMenu $gPo(selTableId,$verTabInd) $gPo(cmdTableId,$genTabInd) %X %Y"

    # Fill the Check file notebook tab.
    ttk::frame $chkTab.pane.catfr.btnfr
    ttk::frame $chkTab.pane.catfr.txtfr
    pack $chkTab.pane.catfr.btnfr -expand 1 -fill x -side top
    pack $chkTab.pane.catfr.txtfr -expand 1 -fill both -side top

    set gPo(selTextId,$chkTabInd) [tcl3dCreateScrolledText $chkTab.pane.catfr.txtfr "" \
                                   -wrap none]

    set pane $chkTab.pane.resfr.pane
    ttk::panedwindow $pane -orient vertical
    pack $pane -side top -expand 1 -fill both
    ttk::frame $pane.cmdfr
    ttk::frame $pane.enumfr
    pack $pane.cmdfr  -expand 1 -fill both -side left
    pack $pane.enumfr -expand 1 -fill both -side left
    $pane add $pane.cmdfr
    $pane add $pane.enumfr

    set gPo(cmdTableId,$chkTabInd) [tcl3dCreateScrolledTablelist $pane.cmdfr "Command list" \
                  -stripebackground #e0e8f0 \
                  -labelcommand tablelist::sortByColumn \
                  -showseparators yes -width 50 -height 10]
    bind $gPo(cmdTableId,$chkTabInd) <<TablelistSelect>> "ShowDetail $gPo(cmdTableId,$chkTabInd)"
    bind $gPo(cmdTableId,$chkTabInd) <<TablelistSelect>> "+ShowCmdInText $gPo(cmdTableId,$chkTabInd) $gPo(selTextId,$chkTabInd)"
    bind $gPo(cmdTableId,$chkTabInd) <<TablelistColumnSorted>> "AddSortToHistory $gPo(cmdTableId,$chkTabInd)"

    set gPo(enumTableId,$chkTabInd) [tcl3dCreateScrolledTablelist $pane.enumfr "Enum list" \
                  -stripebackground #e0e8f0 \
                  -labelcommand tablelist::sortByColumn \
                  -showseparators yes -width 50 -height 10]
    bind $gPo(enumTableId,$chkTabInd) <<TablelistSelect>> "ShowDetail $gPo(enumTableId,$chkTabInd)"
    bind $gPo(enumTableId,$chkTabInd) <<TablelistSelect>> "+ShowEnumInText $gPo(enumTableId,$chkTabInd) $gPo(selTextId,$chkTabInd)"
    bind $gPo(enumTableId,$chkTabInd) <<TablelistColumnSorted>> "AddSortToHistory $gPo(enumTableId,$chkTabInd)"


    set gPo(fileCombo) [tcl3dDirSelect::CreateFileSelect $chkTab.pane.catfr.btnfr \
                        $gPo(lastDir) "open" "Open ..."]
    set fileTypes {
        {{All files}   *}
        {{Tcl files}   {.tcl}}
        {{C/C++ files} {.c .cpp}}
        {{Text files}  {.txt}}
    }
    tcl3dDirSelect::SetFileTypes $gPo(fileCombo) $fileTypes
    bind $gPo(fileCombo) <Key-Return>     "GetComboValue $gPo(fileCombo) $gPo(selTextId,$chkTabInd)"
    bind $gPo(fileCombo) <<FileSelected>> "GetComboValue $gPo(fileCombo) $gPo(selTextId,$chkTabInd)"

    ttk::button $chkTab.pane.catfr.btnfr.chk  -text "Check" -command \
        "CheckFile $gPo(selTextId,$chkTabInd) $gPo(cmdTableId,$chkTabInd) $gPo(enumTableId,$chkTabInd)"
    pack $chkTab.pane.catfr.btnfr.chk  -side left

    # Status frame.
    ttk::frame .fr.infofr.fr
    pack .fr.infofr.fr -expand true -fill both

    tcl3dOglSetDebugMode

    # Dummy Togl window to get graphics context.
    togl .fr.toglfr.togl -width 1 -height 1 -accum true -depth true -double true
    pack .fr.toglfr.togl

    set versionDict [tcl3dOglGetVersionNumber]
    set gPo(defSavePrefix) [format "%s_%s_GL-%d.%d" \
                            $::tcl_platform(os) \
                            [glGetString GL_RENDERER] \
                            [dict get $versionDict major] \
                            [dict get $versionDict minor]]

    set gPo(initDir) [pwd]
    wm title . $title
}

# proc ::tk::mac::ShowPreferences {} {
#    ShowSpecificNotebookWin
# }

proc AskSaveCsvFile { saveAll } {
    global gPo

    set fileTypes {
        {{All files}   *}
        {{CSV files} {.csv}}
    }
    set defSaveName [format "%s.csv" $gPo(defSavePrefix)]
    set fileName [tk_getSaveFile -filetypes $fileTypes \
                  -initialfile $defSaveName \
                  -initialdir $gPo(lastDir) \
                  -title "Save table(s) as CSV file"]
    if { $fileName ne "" } {
        if { $saveAll } {
            SaveCsvFiles $fileName
        } else {
           SaveCsvFile $fileName $gPo(curTableId)
       }
    }
}

proc AskSaveExcelFile { saveAll } {
    global gPo

    set fileTypes {
        {{All files}   *}
        {{Excel files} {.xls .xlsx}}
    }
    set defSaveName [format "%s.xls" $gPo(defSavePrefix)]
    set fileName [tk_getSaveFile -filetypes $fileTypes \
                  -initialfile $defSaveName \
                  -initialdir $gPo(lastDir) \
                  -title "Save table(s) as Excel file"]
    if { $fileName ne "" } {
        if { $saveAll } {
            SaveExcelFiles $fileName
        } else {
            SaveExcelFile $fileName $gPo(curTableId)
        }
    }
}

proc SaveCsvFile { fileName tableId } {
    set numCols [$tableId columncount]
    for { set col 0 } { $col < $numCols } { incr col } {
        lappend colList [$tableId columncget $col -title]
    }
    set tableList [list $colList]
    foreach line [$tableId get 0 end] {
        lappend tableList $line
    }
    ::Csv::Write $tableList $fileName
}

proc SaveCsvFiles { fileName } {
    global gPo

    foreach catInfo [lreverse $gPo(catList)] {
        SelectEntry [lindex $catInfo 0] $gPo(selTableId,$gPo(genTabInd)) 0
        ExecCmd $gPo(selTableId,$gPo(genTabInd))
        update
        set tableFileName [format "%s-%s.csv" \
            [file rootname $fileName] [lindex [split $gPo(curTableName) "("] 0]]
        SaveCsvFile $tableFileName $gPo(curTableId)
    }
}

proc SaveExcelFile { fileName tableId } {
    global gPo

    set appId [::Excel::OpenNew]
    set workbookId [::Excel::AddWorkbook $appId]

    LoadTableToExcel $workbookId $gPo(curTableId) $gPo(curTableName)

    ::Excel::SaveAs $workbookId $fileName
    ::Excel::Quit $appId
}

proc SaveExcelFiles { fileName } {
    global gPo

    file delete -force $fileName
    set appId [::Excel::OpenNew]
    set workbookId [::Excel::AddWorkbook $appId]

    foreach catInfo [lreverse $gPo(catList)] {
        SelectEntry [lindex $catInfo 0] $gPo(selTableId,$gPo(genTabInd)) 0
        ExecCmd $gPo(selTableId,$gPo(genTabInd))
        update
        LoadTableToExcel $workbookId $gPo(curTableId) $gPo(curTableName)
    }
    ::Excel::SaveAs $workbookId $fileName
    ::Excel::Quit $appId
}

proc LoadTableToExcel { workbookId tableId tableName } {
    set numRows [$tableId size]
    set numCols [llength [lindex  [$tableId get 0 0] 0]]

    ::Excel::SetMapFloatSeparator false

    set worksheetName [lindex [split $tableName "("] 0]
    set worksheetId [::Excel::AddWorksheet $workbookId $worksheetName]

    # Set column 1 to type "Number", all other columns to type "Text".
    set rangeId [::Excel::SelectRangeByIndex $worksheetId 2 1 [expr $numRows+1] 1]
    ::Excel::SetRangeFormat $rangeId "int"
    set rangeId [::Excel::SelectRangeByIndex $worksheetId 2 2 [expr $numRows+1] $numCols]
    ::Excel::SetRangeFormat $rangeId "text"

    set cellsId [::Excel::GetCellsId $worksheetId]
    ::Excel::TablelistToWorksheet $tableId $worksheetId true
 
    # Adjust the column width and switch on the AutoFilter feature
    # for the whole table.
    ::Excel::SetColumnsWidth $worksheetId 1 $numCols 0
    set rangeId [::Excel::SelectRangeByIndex $worksheetId 1 1 [expr $numRows+1] $numCols]
    ::Excel::ToggleAutoFilter $rangeId
}

proc LoadToExcel {} {
    global gPo

    set appId [::Excel::OpenNew]
    set workbookId [::Excel::AddWorkbook $appId]

    LoadTableToExcel $workbookId $gPo(curTableId) $gPo(curTableName)
}

proc WriteEntryValue { index msg } {
    .fr.infofr.fr.entry_$index delete 0 end
    .fr.infofr.fr.entry_$index insert 0 $msg
}

proc Cleanup {} {
    uplevel #0 unset gPo
}

proc ExitProg {} {
    exit
}

proc AddCmdToHistory { view cmd  { addToHistory true } } {
    global gPo

    if { $addToHistory } {
        # If cmd is already in the history list, remove it. Append cmd at the front of the list.
        set ind [lsearch -exact $gPo(historyCmds) $cmd]
        if { $ind >= 0 } {
            set gPo(historyCmds) [lreplace $gPo(historyCmds) $ind $ind]
            set gPo(historyView) [lreplace $gPo(historyView) $ind $ind]
        }
        set gPo(historyCmds) [linsert $gPo(historyCmds) 0 $cmd]
        set gPo(historyView) [linsert $gPo(historyView) 0 $view]
        UpdateCombo $gPo(historyCombo) $gPo(historyView) 0
    }
}

proc AddSortToHistory { tableId } {
    global gPo

    set sortColumn [$tableId sortcolumn]
    set sortOrder  [$tableId sortorder]
    lappend gPo(historyCmds) "$tableId sortbycolumn $sortColumn -$sortOrder"
}

proc OpenURL { url } {
    global env tcl_platform

    if { $tcl_platform(platform) eq "windows" } {
        if {[file exists $env(COMSPEC)]} {
            eval exec [list $env(COMSPEC)] /c start [list $url] &
        } else {
            eval exec command /c start [list $url] &
        }
    } elseif { $tcl_platform(os) eq "Darwin" } {
        eval exec open [list $url] &
    } elseif { $tcl_platform(os) eq "Linux" } {
        eval exec xdg-open [list $url] &
    } else {
        eval exec [list $url] &
    }
}

proc GetURL { item } {
    return [tcl3dOglGetUrl $item]
}

proc GetSelectedTableItem { tableId column } {
    if { [$tableId size] == 0 } {
        return ""
    }
    set rowIndList [$tableId curselection]
    set rowCont [$tableId get [lindex $rowIndList 0]]
    return [lindex $rowCont $column]
}

proc ShowCmdInText { tableId textId } {
    global gCmdInd

    set cmd [GetSelectedTableItem $tableId 1]
    if { $cmd ne "" && [info exists gCmdInd($cmd)] } {
        $textId see [lindex $gCmdInd($cmd) 0]
    }
}

proc ShowEnumInText { tableId textId } {
    global gEnumInd

    set enum [GetSelectedTableItem $tableId 1]
    if { $enum ne "" && [info exists gEnumInd($enum)] } {
        $textId see [lindex $gEnumInd($enum) 0]
    }
}

proc ShowDetail { tableId } {
    global gPo

    set gPo(curTableId) $tableId

    if { [$tableId size] == 0 } {
        return
    }
    DrawDetail $tableId
    set rowIndList [$tableId curselection]
    set rowCont [$tableId get [lindex $rowIndList 0]]
    set item [lindex $rowCont 1]
    set url [GetURL $item]
    set numCols [llength $rowCont]
    for { set i 1 } { $i < $numCols } { incr i } {
        set value [lindex $rowCont $i]
        WriteEntryValue $i "$value"
    }
    WriteEntryValue $i "$url"
}

proc HelpLinks {} {
    global gPo

    set rowIndList [$gPo(curTableId) curselection]
    if { $rowIndList eq "" } {
        tk_messageBox -title "Reference links" \
                      -message "No item of the table selected" \
                      -type ok -icon error
        return
    }

    set rowCont [$gPo(curTableId) get [lindex $rowIndList 0]]
    set item [lindex $rowCont 1]
    set url [GetURL $item]
    OpenURL $url
}

proc HelpCont {} {
    set helpStr \
"Sorry, no serious documentation available yet."

    tk_messageBox -title "Online help" \
                  -message $helpStr \
                  -type ok -icon info
}

proc HelpAbout {} {
    tcl3dLogoShowPoSoft "OglInfo Version 0.6.0" \
        "Copyright 2010-2012 Paul Obermeier" ""
}

proc DrawTitle { tableId titleStr } {
    global gPo

    tcl3dSetScrolledTitle $tableId $titleStr
    set gPo(curTableName) $titleStr
}

proc SelectGLCmd { item } {
    global gPo

    set genTabInd $gPo(genTabInd)
    set tableId   $gPo(cmdTableId,$genTabInd)
    ShowGLCommands "GL commands" $genTabInd $tableId true $item
    SelectEntry "GL commands" $gPo(selTableId,$gPo(genTabInd)) 0
}

proc SelectGLEnum { item } {
    global gPo

    set genTabInd $gPo(genTabInd)
    set tableId   $gPo(cmdTableId,$genTabInd)
    SelectEntry "GL enums" $gPo(selTableId,$gPo(genTabInd)) 0
    ShowGLEnums "GL enums" $genTabInd $tableId true $item
}

proc OpenContextMenu { selTableId resTableId x y } {
    global gPo

    set w .tcl3dInfo_ContextMenu
    catch { destroy $w }
    menu $w -tearoff false -disabledforeground white

    set rowIndList [$resTableId curselection]
    set noSel [llength $rowIndList]
    if { $noSel != 0 } {
        set rowCont [$resTableId get [lindex $rowIndList 0]]
        if { $gPo($resTableId,infoType) eq "ShowExtensions" || \
             $gPo($resTableId,infoType) eq "ShowGLCommands" || \
             $gPo($resTableId,infoType) eq "ShowGLEnums" } {
            if { $gPo($resTableId,infoType) eq "ShowExtensions" } {
                set item [lindex $rowCont 1]
            }
            if { $gPo($resTableId,infoType) eq "ShowGLCommands" } {
                set item [lindex $rowCont 3]
            }
            if { $gPo($resTableId,infoType) eq "ShowGLEnums" } {
                set item [lindex $rowCont 5]
            }
            $w add command -label "Show all enums and commands of $item" \
                           -command "SelectVersionInfo $selTableId $item"
        } elseif { $gPo($resTableId,infoType) eq "ShowGLStateVars" } {
            set enumItem [lindex $rowCont 2]
            set cmdItem  [lindex $rowCont 4]
            $w add command -label "Show details of $enumItem" \
                           -command "SelectGLEnum $enumItem"
            $w add command -label "Show details of $cmdItem" \
                           -command "SelectGLCmd $cmdItem"
        } else {
            set menuTitle "No additional information"
            $w add command -label "$menuTitle" -state disabled \
                           -background "#303030"
        }
    } else {
        set menuTitle "Nothing selected"
        $w add command -label "$menuTitle" -state disabled -background "#303030"
    }
    tk_popup $w $x $y
}

proc Search { searchKey resTableId column } {
    global gPo

    # puts "Search $resTableId <$searchKey> $column"
    set gPo(searchKey) $searchKey
    if { $searchKey eq "" } {
        SetSelection $resTableId 0
        return
    }
    set keyLen [expr {[string length $searchKey] -1}]
    set ind 0
    set searchKey [string tolower $searchKey]
    foreach line [$resTableId get 0 end] {
        set key [string range [lindex $line $column] 0 $keyLen]
        if { $searchKey eq [string tolower $key] } {
            SetSelection $resTableId $ind
            return
        }
        incr ind
    }
}

proc SetSearchOff {} {
    global gPo

    $gPo(searchLabel) configure -text "" -state disabled
}

proc SetSearchKey { infoType resTableId column { key "" } } {
    global gPo

    if { ! [info exists gPo(lastKeys,$infoType)] } {
        set gPo(lastKeys,$infoType) ""
    }
    if { $key ne "" } {
        set gPo(lastKeys,$infoType) $key
    }
    $gPo(searchLabel) configure -text $gPo(lastKeys,$infoType) -state normal
    Search $gPo(lastKeys,$infoType) $resTableId $column
}

proc SearchKey { infoType resTableId column key sym } {
    global gPo

    # puts "SearchKey $infoType"
    if { ! [info exists gPo(lastKeys,$infoType)] } {
        set gPo(lastKeys,$infoType) ""
    }

    if { $sym eq "BackSpace" } {
        set gPo(lastKeys,$infoType) [string range $gPo(lastKeys,$infoType) 0 end-1]
    } else {
        if { [string length $key] == 0 } {
            return
        }
        if { $sym ne "Return" } {
            append gPo(lastKeys,$infoType) [string tolower $key]
        }
    }
    Search $gPo(lastKeys,$infoType) $resTableId $column
}

proc GetCmdInfo { cmd } {
    if { [tcl3dOglIsFuncWrapped $cmd] } {
        set avail  [tcl3dOglHaveFunc $cmd]
        set vers   [tcl3dOglGetFuncVersion $cmd]
        set depr   [tcl3dOglGetFuncDeprecated $cmd]
        set cSig   [tcl3dOglGetFuncSignature $cmd]
        set tclSig [tcl3dOglGetFuncSignature $cmd "tcl"]
        return [list "" $cmd $avail $vers $depr $cSig $tclSig]
    } else {
        set avail [tcl3dOglHaveFunc $cmd]
        set retVal [catch {eval $cmd} errMsg]
        set versionStr ""
        set helpStr  ""
        if { $retVal == 0 } {
            set helpStr "$cmd"
        } else {
            set startInd [string first $cmd $errMsg]
            set endInd [expr [string last "argument" $errMsg] -1]
            if { $endInd < 0 } {
                set endInd end
            }
            set helpStr [string trim [string range $errMsg $startInd $endInd] " \""]
        }
        return [list "" $cmd $avail $versionStr "" $helpStr $helpStr]
    }
}

proc ResetTablelist { tableId } {
    $tableId delete 0 end
}

proc SetSelection { tableId rowNum } {
    $tableId selection clear 0 end
    $tableId selection set $rowNum $rowNum
    $tableId see $rowNum
    $tableId activate $rowNum
    ShowDetail $tableId
}

proc ExecCmd { tableId } {
    set rowIndList [$tableId curselection]
    set rowCont [$tableId get [lindex $rowIndList 0]]
    set cmd [lindex $rowCont 1]
    eval $cmd
}

# Draw label and entry widgets below the table for detailled
# viewing of a table row.
proc DrawDetail { tableId } {
    global gPo

    if { $gPo(detailInfoType) eq $gPo($tableId,infoType) } {
        return
    }

    catch { eval destroy [winfo children .fr.infofr.fr] }

    set numCols [$tableId columncount]
    # Start at column 1, as the first column is just the row numbering.
    for { set i 1 } { $i < $numCols } { incr i } {
        set key [$tableId columncget $i -title]
        ttk::label .fr.infofr.fr.label_$i -anchor nw -text "$key"
        ttk::entry .fr.infofr.fr.entry_$i -exportselection false
        grid .fr.infofr.fr.label_$i -row [expr $i-1] -column 0 -sticky w
        grid .fr.infofr.fr.entry_$i -row [expr $i-1] -column 1 -sticky news
    }
    ttk::label .fr.infofr.fr.label_$i -anchor nw -text "Reference link"
    ttk::entry .fr.infofr.fr.entry_$i -exportselection false
    grid .fr.infofr.fr.label_$i -row [expr $i-1] -column 0 -sticky w
    grid .fr.infofr.fr.entry_$i -row [expr $i-1] -column 1 -sticky news
    grid columnconfigure .fr.infofr.fr 1 -weight 1
    set gPo(detailInfoType) $gPo($tableId,infoType)
}

proc ShowGLInfo { displayName tabInd tableId { addToHistory true } } {
    global gPo

    ChangeTab $tabInd
    ResetTablelist $tableId
    $tableId configure -columns {0 "#"          "right"
                                 0 "Identifier" "left"
                                 0 "Value"      "left"}
    $tableId columnconfigure 0 -showlinenumbers true
    $tableId columnconfigure 1 -sortmode ascii
    $tableId columnconfigure 2 -sortmode ascii

    foreach glInfo [tcl3dOglGetVersions] {
        $tableId insert end [linsert $glInfo 0 ""]
    }
    $tableId insert end [list "" TCL3D_VERSION [package present tcl3dogl]]
    set gPo($tableId,infoType) "ShowGLInfo"
    DrawDetail $tableId
    DrawTitle $tableId "OpenGL information"
    AddCmdToHistory $displayName "ShowGLInfo \"$displayName\" $tabInd $tableId" $addToHistory
    bind [$tableId bodypath] <KeyPress> {}
    SetSearchOff

    SetSelection $tableId 0
    Focus $tableId
}

proc ShowExtensions { displayName tabInd tableId { addToHistory true } { selectItem "" } } {
    global gPo gOpts

    ChangeTab $tabInd
    ResetTablelist $tableId

    $tableId configure -columns {0 "#"         "right"
                                 0 "Extension" "left"
                                 0 "Avail"     "left"
                                 0 "GLEW"      "left"}
    $tableId columnconfigure 0 -showlinenumbers true
    $tableId columnconfigure 1 -sortmode ascii
    $tableId columnconfigure 2 -sortmode integer
    $tableId columnconfigure 3 -sortmode integer

    foreach ext [tcl3dOglGetExtensionList] {
        set extArr($ext,avail)   0
        set extArr($ext,wrapped) 0
    }

    foreach glInfo [tcl3dOglGetExtensions] {
        foreach ext [lindex $glInfo 1] {
            set extArr($ext,avail)   1
            set extArr($ext,wrapped) 0
        }
    }

    foreach ext [tcl3dOglGetVersionList] {
        if { ! [string match "GL_VERSION_*" $ext] } {
            if { ! [info exists extArr($ext,avail)] } {
                puts "Extension $ext is listed in GLEW, but not in official list."
                set extArr($ext,avail) 0
            }
            set extArr($ext,wrapped) 1
        }
    }

    set extList [lsort -dictionary [array names extArr "*,avail"]]

    DrawTitle $tableId "Getting [llength $extList] values ..."
    InitProgress [llength $extList]

    set numExt    0
    set numAvail  0
    set tblList   [list]
    set availList [list]
    foreach keys $extList {
        set ext [lindex [split $keys ","] 0]
        set wrapped $extArr($ext,wrapped)
        set avail   $extArr($ext,avail)
        lappend tblList [list "" $ext $avail $wrapped]
        if { $avail } {
            lappend availList $numExt
            incr numAvail
        }
        incr numExt
        if { $numExt % 50 == 0 } {
            UpdateProgress $numExt
        }
    }
    $tableId insertlist end $tblList

    foreach avail $availList {
        $tableId rowconfigure $avail -background $gOpts(availColor)
    }

    set titleStr "OpenGL Extensions ($numAvail of $numExt available)"
    set gPo($tableId,infoType) "ShowExtensions"
    DrawDetail $tableId
    DrawTitle $tableId $titleStr
    AddCmdToHistory $displayName "ShowExtensions \"$displayName\" $tabInd $tableId" $addToHistory

    bind [$tableId bodypath] <KeyPress> \
        "SearchKey $gPo($tableId,infoType) $tableId 1 %A %K"

    UpdateProgress 0

    SetSearchKey $gPo($tableId,infoType) $tableId 1 $selectItem
    Focus $tableId
}

proc _CmdInfo { tabInd tableId cmdList title cmdType { selectItem "" } } {
    global gPo gOpts

    ChangeTab $tabInd
    ResetTablelist $tableId
    $tableId configure -columns {0 "#"             "right"
                                 0 "Command"       "left"
                                 0 "Avail"         "left"
                                 0 "OGL/Ext"       "left"
                                 0 "Deprecated"    "left"
                                 0 "C signature"   "left"
                                 0 "Tcl signature" "left"}
    $tableId columnconfigure 0 -showlinenumbers true
    $tableId columnconfigure 1 -sortmode ascii
    $tableId columnconfigure 2 -sortmode integer
    $tableId columnconfigure 3 -sortmode ascii
    $tableId columnconfigure 4 -sortmode ascii
    $tableId columnconfigure 5 -sortmode ascii
    $tableId columnconfigure 6 -sortmode ascii

    set numCmds [llength $cmdList]

    DrawTitle $tableId "Getting $numCmds values ..."
    InitProgress $numCmds

    set numAvail  0
    set curRow    0
    set tblList   [list]
    set availList [list]
    foreach cmd $cmdList {
        set cmdInfoList [GetCmdInfo $cmd]
        lappend tblList $cmdInfoList
        if { [lindex $cmdInfoList 2] } {
            lappend availList $curRow
            incr numAvail
        }
        incr curRow
        if { $curRow % 100 == 0 } {
            UpdateProgress $curRow
        }
    }
    $tableId insertlist end $tblList

    foreach avail $availList {
        $tableId rowconfigure $avail -background $gOpts(availColor)
    }
    set titleStr "$title ($numAvail of $numCmds available)"
    set gPo($tableId,infoType) $cmdType
    DrawTitle $tableId $titleStr
    DrawDetail $tableId

    bind [$tableId bodypath] <KeyPress> "SearchKey $gPo($tableId,infoType) $tableId 1 %A %K"

    UpdateProgress 0

    SetSearchKey $gPo($tableId,infoType) $tableId 1 $selectItem
    Focus $tableId
}

proc SelectEntry { item tableId column } {
    set ind -1
    # puts "SelectEntry $item $tableId $column"
    for { set row 0 } { $row < [$tableId size] } { incr row } {
        set rowCont [$tableId get $row]
        set key [lindex $rowCont $column]
        if { $key eq $item } {
            set ind $row
            break
        }
    }
    if { $ind < 0 } {
        tk_messageBox -title "Wrapping info" \
                      -message "No detail information available for $item" \
                      -type ok -icon info
        return false
    }
    $tableId selection clear 0 end
    $tableId selection set $ind $ind
    $tableId see $ind
    return true
}

proc SelectVersionInfo { selTableId item } {
    global gPo

    if { [SelectEntry $item $selTableId 0] } {
        ChangeTab $gPo(verTabInd)
        ExecCmd $selTableId
    }
}

proc ShowVersionInfo { item tabInd cmdTableId enumTableId { addToHistory true } } {
    global gPo

    ChangeTab $tabInd
    ResetTablelist $cmdTableId
    ResetTablelist $enumTableId

    set funcList [lsort [tcl3dOglGetVersionFuncs $item]]
    _CmdInfo $tabInd $cmdTableId $funcList "Commands of $item" "ShowGLCommands"

    set enumList [lsort [tcl3dOglGetVersionEnums $item]]
    _EnumInfo $tabInd $enumTableId $enumList "Enums of $item" "ShowGLEnums" true

    AddCmdToHistory "$item" "ShowVersionInfo $item $tabInd $cmdTableId $enumTableId" $addToHistory
}

proc _EnumInfo { tabInd tableId enumList title enumType showVersionColumn { selectItem "" } } {
    global gPo

    ChangeTab $tabInd
    ResetTablelist $tableId
    set columns {0 "#"            "right"
                 0 "Enumeration"  "left"
                 0 "State Var."   "left"
                 0 "Value (Dez.)" "right"
                 0 "Value (Hex.)" "right"}
    if { $showVersionColumn } {
        lappend columns 0 "OGL/Ext" "left"
    }
    $tableId configure -columns $columns
    $tableId columnconfigure 0 -showlinenumbers true
    $tableId columnconfigure 1 -sortmode integer
    $tableId columnconfigure 2 -sortmode ascii
    $tableId columnconfigure 3 -sortmode integer
    if { $showVersionColumn } {
        $tableId columnconfigure 4 -sortmode ascii
    }

    set numEnums [llength $enumList]

    DrawTitle $tableId "Getting $numEnums values ..."
    InitProgress $numEnums

    set curRow    0
    set tblList [list]
    foreach cmd $enumList {
        set cmdStr [format "::%s" $cmd]
        set decVal [set $cmdStr]
        if { [string is integer $decVal] } {
            set hexVal [format "%0X" $decVal]
        } else {
            set hexVal $decVal
        }
        set version ""
        if { $showVersionColumn } {
            set version [tcl3dOglGetEnumVersion $cmd]
        }
        set isStateVar [tcl3dOglIsStateVar $cmd]
        lappend tblList [list "" $cmd $isStateVar $decVal $hexVal $version]
        if { $curRow % 100 == 0 } {
            UpdateProgress $curRow
        }
        incr curRow
    }
    $tableId insertlist end $tblList
    set titleStr "$title ($numEnums enumerations)"
    set gPo($tableId,infoType) $enumType
    DrawDetail $tableId
    DrawTitle $tableId $titleStr

    bind [$tableId bodypath] <KeyPress> "SearchKey $gPo($tableId,infoType) $tableId 1 %A %K"

    UpdateProgress 0

    SetSearchKey $gPo($tableId,infoType) $tableId 1 $selectItem
    Focus $tableId
}

proc ShowGLCommands { displayName tabInd tableId { addToHistory true } { selectItem "" } } {
    global gPo

    _CmdInfo $tabInd $tableId $gPo(glCmds) "OpenGL commands" "ShowGLCommands" $selectItem
    AddCmdToHistory $displayName "ShowGLCommands \"$displayName\" $tabInd $tableId" $addToHistory
}

proc ShowGLUCommands { displayName tabInd tableId { addToHistory true } { selectItem "" } } {
    global gPo

    _CmdInfo $tabInd $tableId $gPo(gluCmds) "OpenGL utility commands" "ShowGLUCommands" $selectItem
    AddCmdToHistory $displayName "ShowGLUCommands \"$displayName\" $tabInd $tableId" $addToHistory
}

proc ShowGLEnums { displayName tabInd tableId { addToHistory true } { selectItem "" } } {
    _EnumInfo $tabInd $tableId [lsort [info globals GL_*]] "OpenGL enums" "ShowGLEnums" true $selectItem
    AddCmdToHistory $displayName "ShowGLEnums \"$displayName\" $tabInd $tableId" $addToHistory
}

proc ShowGLUEnums { displayName tabInd tableId { addToHistory true } { selectItem "" } } {
    _EnumInfo $tabInd $tableId [lsort [info globals GLU_*]] "OpenGL utility enums" "ShowGLUEnums" false $selectItem
    AddCmdToHistory $displayName "ShowGLUEnums \"$displayName\" $tabInd $tableId" $addToHistory
}

proc InitProgress { maxValue } {
    global gPo

    $gPo(progress,widget) configure -maximum $maxValue
    update
}

proc UpdateProgress { curValue } {
    global gPo

    set gPo(progress,value) $curValue
    update
}

proc CheckInitVal { curVal initVal isMin } {
    puts "  CheckInitVal $curVal $initVal $isMin"
    if { [llength $curVal] != [llength $initVal] } {
        return false
    } 
    if { $isMin }  {
        foreach cur $curVal init $initVal {
            if { $curVal < $init } {
                return false
            }
        }
    } else {
        foreach cur $curVal init $initVal {
            if { $cur != $init } {
                return false
            }
        }
    }
    return true
}

proc ShowGLStateVars { displayName tabInd tableId { addToHistory true } { selectItem "" } } {
    global gPo gOpts

    ChangeTab $tabInd
    ResetTablelist $tableId
    set columns { 0 "#"             "right"
                  0 "Table"         "right"
                 20 "Get Value"     "left"
                  0 "Type"          "left"
                 20 "Get Command"   "left"
                  0 "IsMin"         "left"
                  0 "Default value" "left"
                  0 "Current value" "left"
                  0 "Attribute"     "left"
                  0 "Profile"       "left"
                  0 "Description"   "left"}
    $tableId configure -columns $columns
    $tableId columnconfigure  0 -showlinenumbers true
    $tableId columnconfigure  1 -sortmode ascii
    $tableId columnconfigure  2 -sortmode ascii
    $tableId columnconfigure  3 -sortmode ascii
    $tableId columnconfigure  4 -sortmode ascii
    $tableId columnconfigure  5 -sortmode integer
    $tableId columnconfigure  6 -sortmode ascii
    $tableId columnconfigure  7 -sortmode ascii
    $tableId columnconfigure  8 -sortmode ascii
    $tableId columnconfigure  9 -sortmode ascii
    $tableId columnconfigure 10 -sortmode ascii

    if { ! [info exists gPo(tblList,stateVar)] } {
        set enumList [lsort -decreasing [info globals GL_*]]
        foreach enum $enumList {
            set cmdStr [format "::%s" $enum]
            set decVal [set $cmdStr]
            lappend enumArray($decVal) $enum
        }

        set stateVarList [__tcl3dGetStateList]
        set numValues [llength $stateVarList]
    } else {
        set numValues [llength $gPo(tblList,stateVar)]
    }

    DrawTitle $tableId "Getting $numValues values ..."
    InitProgress $numValues

    if { ! [info exists gPo(tblList,stateVar)] } {
        set numStateVars 0
        set gPo(tblList,stateVar)   [list]
        set gPo(availList,stateVar) [list]
        foreach stateVar $stateVarList {
            set tbl   [lindex $stateVar  0]
            set enum  [lindex $stateVar  1]
            set type  [lindex $stateVar  2]
            set cmd   [lindex $stateVar  3]
            set isMin [lindex $stateVar  4]
            set num   [lindex $stateVar  5]
            set init  [lindex $stateVar  6]
            set attr  [lindex $stateVar  7]
            set prof  [lindex $stateVar  9]
            set desc  [lindex $stateVar 11]
            puts "\n-- Enum: $enum Cmd: $cmd Num: $num Init: $init --" ; flush stdout
            set needCheck true
            set notAvail  false
            if { $enum ne "" && $cmd ne "" } {
                # puts "$tbl $enum $num $init"
                if { ! [tcl3dOglHaveFunc $cmd] } {
                    set curVal "Command not available"
                    set needCheck false
                    set notAvail  true
                } elseif { $cmd eq "glGetFloatv" } {
                    set curVal [tcl3dOglGetFloatState $enum $num]
                } elseif { $cmd eq "glGetIntegerv" } {
                    set curVal [tcl3dOglGetIntegerState $enum $num]
                } elseif { $cmd eq "glGetBooleanv" } {
                    set curVal [tcl3dOglGetBooleanState $enum $num]
                } elseif { $cmd eq "glIsEnabled" } {
                    set curVal [glIsEnabled $enum]
                } elseif { $cmd eq "glGetMaterialfv" } {
                    if { 0 } {
                    foreach face { GL_FRONT GL_BACK } {
                        $cmd $face $enumVal $floatVec
                        set result [list 1 $glCmd $enumVal]
                        for { set i 0 } { $i < $numVals } { incr i } {
                            lappend result [$floatVec get $i]
                        }
                    }
                    }
                } else {
                    set curVal "Command not yet implemented"
                    set needCheck false
                }
                set errMsg [tcl3dOglGetError]
                if { $errMsg ne "" } {
                    set curVal "GL_INVALID_ENUM"
                    set notAvail  true
                } else {
                    if { [lsearch -sorted -decreasing -exact $enumList $init] >= 0 } {
                        # The init value of the specification is an OpenGL enumeration name.
                        # Reverse lookup the value returned from the glGet commands with the
                        # help of the enumArray to get the corresponding enumeration name.
                        if { [info exists enumArray($curVal)] } {
                            puts ">> Searching for $init (GetValue $enum)" ; flush stdout
                            foreach enumMatch $enumArray($curVal) {
                                puts "   Checking $enumMatch" ; flush stdout
                                if { $enumMatch eq $init } {
                                    puts "<< Found $enumMatch ($curVal)" ; flush stdout
                                    set curVal $enumMatch
                                    set needCheck false
                                    break
                                }
                            }
                        }
                    } elseif { $init eq "none" && $curVal == $::GL_DONT_CARE} {
                        set curVal "GL_DONT_CARE"
                        set needCheck false
                    } elseif { $init eq "none" } {
                        set needCheck false
                    }
                    set found true
                    if { $needCheck } {
                        set found [CheckInitVal $curVal $init $isMin]
                    }
                }
                if { $found } {
                    lappend gPo(availList,stateVar) $numStateVars
                }
                if { $notAvail } {
                    lappend gPo(notAvailList,stateVar) $numStateVars
                }
                set varList [list "" $tbl $enum $type $cmd $isMin $init $curVal $attr $prof $desc]
                lappend gPo(tblList,stateVar) $varList
                incr numStateVars
                if { $numStateVars % 50 == 0 } {
                    UpdateProgress $numStateVars
                }
            }
        }
    } else {
        UpdateProgress [expr $numValues / 2]
    }
    $tableId insertlist end $gPo(tblList,stateVar)

    foreach avail $gPo(availList,stateVar) {
        $tableId rowconfigure $avail -background $gOpts(availColor)
    }
    foreach notAvail $gPo(notAvailList,stateVar) {
        $tableId rowconfigure $notAvail -background $gOpts(notAvailColor)
    }

    set titleStr "OpenGL StateVars ($numValues enumerations)"
    set gPo($tableId,infoType) "ShowGLStateVars"
    DrawDetail $tableId
    DrawTitle $tableId $titleStr

    bind [$tableId bodypath] <KeyPress> "SearchKey $gPo($tableId,infoType) $tableId 1 %A %K"
    AddCmdToHistory $displayName "ShowGLStateVars \"$displayName\" $tabInd $tableId" $addToHistory

    UpdateProgress 0

    SetSearchKey $gPo($tableId,infoType) $tableId 1 $selectItem
    Focus $tableId
}

set gPo(appName) "OglInfo"

set gPo(detailInfoType) ""
set gPo(searchKey)      ""
set gPo(historyCmds)    ""
set gPo(historyView)    ""
set gPo(lastDir)        [pwd]
set gPo(curCheckFile)   "Clipboard" 

set gOpts(availColor)    "lightgreen"
set gOpts(notAvailColor) "red"
set gOpts(cmdColor)      "green"
set gOpts(enumColor)     "yellow"

# Pre-compute some command lists.
set gPo(glCmds)  [lsort [tcl3dOglGetFuncList "gl"]]
set gPo(gluCmds) [lsort [tcl3dOglGetFuncList "glu"]]

ShowMainWin "$gPo(appName): OpenGL Information Center"

set inFile ""
set inDir  ""
if { $argc > 0 } {
    set inFileOrDir [lindex $argv 0]
    if { [file isdirectory $inFileOrDir] } {
        set inDir [file normalize $inFileOrDir]
    } elseif { [file isfile $inFileOrDir] } {
        set inFile [file normalize $inFileOrDir]
    }
}

if { $inFile ne "" } {
    set tabInd $gPo(chkTabInd)
    ChangeTab $tabInd
    LoadFileIntoTextWidget $inFile $gPo(selTextId,$tabInd)
    tcl3dDirSelect::SetValue $gPo(fileCombo) $inFile
    CheckFile $gPo(selTextId,$tabInd) $gPo(cmdTableId,$tabInd) $gPo(enumTableId,$tabInd)
} elseif { $inDir ne "" } {
    puts "Not yet implemented"
} else {
    SelectEntry "GL information" $gPo(selTableId,$gPo(genTabInd)) 0
    ExecCmd $gPo(selTableId,$gPo(genTabInd))
}
