#!/usr/bin/sh
#\
exec wish "$0" ${1+"$@"}
##
# @file
# @ingroup ngcdcsExpDrvWish
# @copyright
#   SPDX-FileCopyrightText: 2026 European Southern Observatory (ESO)
#   SPDX-License-Identifier: LGPL-3.0-only
#

##
# Create exposure driver toplevel widget
#
proc ngcdcsExpDrv {} {
    global gvar env argc argv argv0

    #
    # Hide while creating the widgets
    #
    wm withdraw .
     
    catch {font create ngcdcsFontDefault -family Helvetica -size 11}
    catch {option add *font ngcdcsFontDefault}

    #
    # Initialize global variable array
    #
    set gvar(config) 0
    set gvar(paramList) ""
    set gvar(pname) ""
    set gvar(pval) ""
    set gvar(drv) 0
    set gvar(drvList) ""
    set gvar(drvSel) "Select"
    set gvar(mod) 0
    set gvar(drvLibs) ""
    set gvar(drvLd) ""

    #
    # Title
    #
    wm title . "NGC Exposure Driver"
    
    #
    # Not resizable
    # 
    wm resizable . 0 0

    #
    # Line-feed on stdout
    #
    fconfigure stdout -translation lf

    #
    # Refresh
    #
    image create bitmap refresh -data {
	#define refresh_width 16
	#define refresh_height 16
	static char refresh_bits[] = {
	    0xe1, 0x07, 0xfb, 0x1f, 0x3f, 0x3c, 0x0f, 0x70, 0x1f, 0x60, 0x3f,
	    0xe0, 0x7f, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x03, 0xfe, 0x07, 0xfc, 
	    0x06, 0xf8, 0x0e, 0xf0, 0x3c, 0xfc, 0xf8, 0xdf, 0xe0, 0x87};
    }

    #
    # Add hull-frames
    #
    set w .ngcdcsExpDrv
    #frame $w 
    frame $w -relief groove -borderwidth 2
    frame $w.mbar -relief raised -bd 1
    menubutton $w.mbar.file -text File -menu $w.mbar.file.menu
    menu $w.mbar.file.menu 
    set gvar(drvMenu1) $w.mbar.file.menu.drv
    if {1} {
    $w.mbar.file.menu add command -label "Load Modules..." -underline 0 \
	-command ngcdcsExpDrvFind
    }
    set gvar(menuHelp_1) "Find and load exposure driver modules"
    $w.mbar.file.menu add cascade -label "Select Driver" -underline 0 \
	-menu $gvar(drvMenu1)
    set gvar(menuHelp_2) "Select exposure driver"
    $w.mbar.file.menu add command -label "Reload Driver" -underline 0 \
	-command ngcdcsExpDrvReset 
    set gvar(menuHelp_3) "Reload current exposure driver"
    $w.mbar.file.menu add separator
    $w.mbar.file.menu add command -label "Save Configuration..." -underline 5 \
	-command ngcdcsExpDrvSaveCfg
    set gvar(menuHelp_5) "Save current configuration"
    $w.mbar.file.menu add separator
    $w.mbar.file.menu add command -label "Quit" -underline 0 \
	-command ngcdcsExpDrvExit 
    set gvar(menuHelp_7) "Quit Application"
    menu $gvar(drvMenu1) -tearoff 0 \
	-background #bfbfbf -foreground black \
	-activebackground #2040a0 -activeforeground white \
	-font -Adobe-helvetica-bold-r-normal--12-*-*-*-*-*-iso8859-1
    label $w.head -relief ridge -borderwidth 4 -background white \
	-text "NGC EXPOSURE DRIVER"
    frame $w.top 
    frame $w.mid
    frame $w.bot 
    frame $w.help -relief raised -borderwidth 4

    #
    # Add control/status widgets
    #
    frame $w.top.param -relief ridge -borderwidth 4
    label $w.top.param.head -text "Configuration:"
    listbox $w.top.param.out -relief sunken -borderwidth 2 \
	-width 50 -height 10 -background white -foreground black \
	-selectmode single \
	-font -Adobe-courier-medium-r-normal--12-*-*-*-*-*-iso8859-1 \
	-xscrollcommand "$w.top.param.sx set" \
	-yscrollcommand "$w.top.param.sy set" 
    $w.top.param.out configure -listvariable gvar(paramList)
    scrollbar $w.top.param.sx -command "$w.top.param.out xview" \
	-orient horizontal
    scrollbar $w.top.param.sy -command "$w.top.param.out yview" \
	-orient vertical

    frame $w.top.hist -relief ridge -borderwidth 4
    label $w.top.hist.head -text "History:"
    text $w.top.hist.out -relief sunken -borderwidth 2 -width 50 -height 10 \
	-wrap none -state disabled -background white -foreground black \
	-xscrollcommand "$w.top.hist.sx set" \
	-yscrollcommand "$w.top.hist.sy set" 
    scrollbar $w.top.hist.sx -command "$w.top.hist.out xview" \
	-orient horizontal
    scrollbar $w.top.hist.sy -command "$w.top.hist.out yview" \
	-orient vertical

    entry $w.mid.pname -relief ridge -width 27 -textvariable gvar(pname) \
	-state disabled \
	-font -Adobe-courier-medium-r-normal--12-*-*-*-*-*-iso8859-1
    $w.mid.pname configure -disabledforeground blue 
    entry $w.mid.pval -relief sunken -width 63 -textvariable gvar(pval) \
	-font -Adobe-courier-medium-r-normal--12-*-*-*-*-*-iso8859-1 \
	-highlightthickness 0 
    button $w.mid.clr -text "Clear" -command "ngcdcsExpDrvClearLog"
    button $w.mid.save -text "Save" -command "ngcdcsExpDrvSaveLog"

    button $w.bot.cfg -text "Configure" -width 7 -command "ngcdcsExpDrvCfg" \
	-state disabled
    button $w.bot.start -text "Start" -width 7 -command "ngcdcsExpDrvStart"
    button $w.bot.abort -text "Abort" -width 7 -command "ngcdcsExpDrvAbort" \
	-state disabled
    button $w.bot.dismiss -text "Dismiss" -width 7 \
	-command "ngcdcsExpDrvDelete"
    label $w.bot.stat -relief ridge -foreground blue -width 10 \
	-borderwidth 4 -text "" 

    button $w.bot.reset -image refresh -command "ngcdcsExpDrvReset" 
    set gvar(drvMenu2) $w.bot.sel.drv
    menubutton $w.bot.sel -relief raised -indicatoron 1 -width 10  \
	-textvariable gvar(drvSel) -menu $gvar(drvMenu2)
    menu $gvar(drvMenu2) -tearoff 0 \
	       -background #bfbfbf -foreground black \
	       -activebackground #2040a0 -activeforeground white \
	       -font -Adobe-helvetica-bold-r-normal--12-*-*-*-*-*-iso8859-1
    label $w.bot.drv  -foreground black -text "DRIVER:"


    label $w.help.msg -foreground blue -background white -relief sunken \
	-anchor w

    #
    # Pack the control/status widgets
    #
    pack $w -side top
    pack $w.mbar -side top -fill x -padx 0 -pady 0
    pack $w.mbar.file -side left 
    pack $w.head -side top -ipadx 5 -ipady 5 -pady 10
    pack $w.top -side top -anchor w -expand 1 -fill both -padx 2 -pady 5
    pack $w.mid -side top -anchor w -expand 1 -fill both -padx 2 -pady 5
    pack $w.bot -side top -anchor center -expand 1 -fill x -padx 2 -pady 5
    pack $w.help -side bottom -anchor w -expand 1 -fill x -padx 2 -pady 2

    pack $w.top.param -side left -padx 5 -fill both
    pack $w.top.param.head -side top -fill both
    pack $w.top.param.sx -side bottom -fill x 
    pack $w.top.param.sy -side right -fill y 
    pack $w.top.param.out -side left -fill both

    pack $w.top.hist -side right -padx 5 -fill both
    pack $w.top.hist.head -side top -fill both
    pack $w.top.hist.sx -side bottom -fill x 
    pack $w.top.hist.sy -side right -fill y 
    pack $w.top.hist.out -side left -fill both

    pack $w.mid.pname -side left -padx 5
    pack $w.mid.pval -side left -padx 0
    pack $w.mid.save -side right -padx 5
    pack $w.mid.clr -side right -padx 0

    pack $w.bot.cfg $w.bot.start $w.bot.abort  \
	$w.bot.dismiss \
	-side left -padx 5 -pady 5
    pack $w.bot.stat -side right -padx 5 -ipady 2
    pack $w.bot.reset -side right -padx 2
    pack $w.bot.sel -side right -padx 0
    pack $w.bot.drv -side right -padx 2
    pack $w.help.msg -side left -expand 1 -fill x

    update idletasks

    #
    # Widget help texts
    #
    set $w.top.param.hlp "List of configuration parameters - click on parameter name to select"
    set $w.top.hist.hlp "Action history"
    set $w.mid.pname.hlp "Selected parameter name"
    set $w.mid.pval.hlp "Input parameter value +\[enter\] - when finished press \[Configure\] button to upload the new setting(s)"
    set $w.bot.cfg.hlp "Upload configuration to exposure driver"
    set $w.bot.start.hlp "Start exposure sequence"
    set $w.bot.abort.hlp "Abort exposure sequence"
    set $w.bot.sel.hlp "Select exposure driver"
    set $w.bot.reset.hlp "Reload exposure driver"
    set $w.bot.dismiss.hlp "Unload exposure driver"
    set $w.bot.stat.hlp "Exposure driver status"
    set $w.mid.clr.hlp "Clear action history"
    set $w.mid.save.hlp "Save action history to file"
    
    #
    # Bindings
    #
    set wdgList [list $w.top.param $w.top.hist $w.mid.pname $w.mid.pval \
		     $w.mid.clr $w.mid.save $w.bot.cfg $w.bot.start \
                     $w.bot.abort $w.bot.sel $w.bot.reset $w.bot.dismiss \
		     $w.bot.stat]
    foreach wdg $wdgList {
	set txt ""
	if {![catch {eval set txt "$\{$wdg.hlp\}"}]} {
	    bind $wdg <Enter> \
		".ngcdcsExpDrv.help.msg configure -text {$txt}"
	    bind $wdg <Leave> \
		".ngcdcsExpDrv.help.msg configure -text {}"
	}
    }
    #bind $w.top.param.out <1> "focus $w.top.param.out"
    bind $w.top.param.out <B1-ButtonRelease> ngcdcsExpDrvGetSel
    bind $w.mid.pval <Return> ngcdcsExpDrvSet
    bind $w <Destroy> ngcdcsExpDrvExit
    bind $w.mbar.file.menu <<MenuSelect>> {ngcdcsExpDrvMenuHelp %W}

    #
    # Install event handler for stdin
    #
    fileevent stdin readable ngcdcsExpDrvEvt

    #
    # We are alive
    #
    puts "!"
    flush stdout
    wm deiconify .
}

##
# Handle event on stdin
#
proc ngcdcsExpDrvEvt {} {
    global gvar param paramUnit
    
    update idletasks

    set w .ngcdcsExpDrv

    if {![eof stdin]} {
	if {[catch {set str [gets stdin]}]} {
	    #
	    # Communication failed - the other side may have died
	    #
	    exit
	} elseif {$str != ""} {
	    #
	    # Get token and message
	    #
	    set tok [string range $str 0 2]
	    if {[catch {set msg [string range $str 4 end]}]} {
		set msg ""
	    }

	    if {$tok == "C\[:"} {
		#
		# Initiate configuration cycle
		#
		set gvar(config) 1
		if {[info exists param]} {
		    foreach ele [array names param] {
			unset param($ele)
		    }
		}
	    } elseif {$tok == "C\]:"} {
		#
		# Configuration cycle closed
		#
		set gvar(config) 0
		ngcdcsExpDrvUpdateParam
	    } elseif {$tok == "D\[:"} {
		#
		# Initiate driver list cycle
		#
		set gvar(drv) 1
		set gvar(drvList) ""
		set gvar(drvSel) "Select"
		$gvar(drvMenu1) delete 0 end
		$gvar(drvMenu2) delete 0 end
	    } elseif {$tok == "D\]:"} {
		#
		# Driver list cycle closed
		#
		set gvar(drv) 0
		ngcdcsExpDrvUpdateList
	    } elseif {$tok == "M\[:"} {
		#
		# Initiate module list cycle
		#
		set gvar(mod) 1
	    } elseif {$tok == "M\]:"} {
		#
		# Module list cycle closed
		#
		set gvar(mod) 0
	    } elseif {$gvar(config)} {
		#
		# Get configuration paramater
		#
		set x [regexp -inline -all {[^ \"']+|\"[^\"]*\"|'[^']*'} $str]
		if {$x != {}} {
		    set name [lindex $x 0]
		    set v [lindex $x 1]
		    set c [string range $v 0 0]
		    if {$c == "\'" || $c == "\""} {
			set v "\"[string trim $v "\'\""]\""
		    }
		    set param($name) $v
		    set paramUnit($name) [lindex $x 2]
		} else {
		    exit
		}
	    } elseif {$gvar(drv)} {
		#
		# Get driver list entry
		#
		if {[scan $str "%s" name] > 0} {
		    lappend gvar(drvList) $name
		} else {
		    exit
		}
	    } elseif {$gvar(mod)} {
		#
		# Get module list entry
		#
		if {[scan $str "%s" name] > 0} {
		    lappend gvar(drvLd) $name
		} else {
		    exit
		}
	    } elseif {$tok == "ST:"} {
		#
		# Status update
		#
		set w .ngcdcsExpDrv
		if {$gvar(drvSel) != "Select"} {
		    $w.bot.stat configure -text "$msg"
		}
		if {$msg == "ACTIVE"} {
		    .ngcdcsExpDrv.bot.abort configure -state normal
		    .ngcdcsExpDrv.bot.start configure -state disabled
		    .ngcdcsExpDrv.bot.reset configure -state disabled
		    .ngcdcsExpDrv.bot.sel configure -state disabled
		    .ngcdcsExpDrv.mbar.file.menu entryconfigure 1 \
			-state disabled
		    .ngcdcsExpDrv.mbar.file.menu entryconfigure 2 \
			-state disabled
		    .ngcdcsExpDrv.mbar.file.menu entryconfigure 3 \
			-state disabled
		} else {
		    .ngcdcsExpDrv.bot.abort configure -state disabled
		    .ngcdcsExpDrv.bot.start configure -state normal
		    .ngcdcsExpDrv.bot.reset configure -state normal
		    .ngcdcsExpDrv.bot.sel configure -state normal
		    .ngcdcsExpDrv.mbar.file.menu entryconfigure 1 \
			-state normal
		    .ngcdcsExpDrv.mbar.file.menu entryconfigure 2 \
			-state normal
		    .ngcdcsExpDrv.mbar.file.menu entryconfigure 3 \
			-state normal
		}
	    } elseif {$tok == "DN:"} {
		#
		# Driver name
		#
		set gvar(drvSel) $msg
	    } elseif {$tok == "XX:"} {
		#
		# About to terminate
		#
		set w .ngcdcsExpDrv
		$w.bot.cfg configure -state disabled
		$w.bot.abort configure -state disabled
		$w.bot.start configure -state disabled
		$w.bot.reset configure -state disabled
		$w.bot.sel configure -state disabled
		$w.mbar.file.menu entryconfigure 1 -state disabled
		$w.mbar.file.menu entryconfigure 2 -state disabled
		$w.mbar.file.menu entryconfigure 3 -state disabled
	    } else {
		#
		# We assume this is an action log
		#
		set t $w.top.hist.out
		$t configure -state normal
		$t insert end "$str\n"
		$t see end 
		$t configure -state disabled
	    }
	}
    } else {
	#
	# The other side has died
	#
	exit
    }
}

##
# Send command
#
proc ngcdcsExpDrvCmd {cmd {rep 0}} {
    global gvar

    set w .ngcdcsExpDrv

    #
    # Write...
    #
    if {[catch {puts "$cmd"} res]} {
	#
	# The other side has died
	#
	exit
    }

    #
    # ... and flush
    #
    if {[catch {flush stdout} res]} {
	#
	# The other side has died
	#
	exit
    }
    
    if {$rep} {
	if {![eof stdin]} {
	    if {[catch {set str [gets stdin]}]} {
		#
		# Communication failed - the other side may have died
		#
		exit
	    } elseif {$str != ""} {
		return $str
	    } else {
		return 0
	    }
	} else {
	    #
	    # The other side has died
	    #
	    exit
	}
    } else {
	return 1
    }
}

##
# Get parameter list selection
#
proc ngcdcsExpDrvGetSel {} {
    global gvar param

    set pw .ngcdcsExpDrv.top.param.out
    set index [$pw curselection]

    if {$index != ""} {
	set sel [$pw get $index]
	set x {}
	eval set x [list $sel]
	if {$x != {}} {
	    set gvar(pname) [lindex $x 0]
	    set gvar(pval) [lindex $x 1]
	}
    }
}

##
# Set configuration parameter
#
proc ngcdcsExpDrvSet {} {
    global gvar param

    if {$gvar(pname) != ""} {
	if {$gvar(pval) == ""} {
	    set param($gvar(pname)) "\"\""
	} else {
	    set tmp [string trim $gvar(pval)]
	    set tmp [string trim $tmp \"]
	    if {[string first " " $tmp] < 0} {
		set param($gvar(pname)) $tmp
	    } else {
		set param($gvar(pname)) "\"$tmp\""
	    }
	}
	ngcdcsExpDrvUpdateParam
	#set gvar(pval) ""
	.ngcdcsExpDrv.bot.cfg configure -state normal
    }
}

##
# Update configuration parameter list
#
proc ngcdcsExpDrvUpdateParam {} {
    global gvar param paramUnit

    if {![info exists param]} {
	return
    }

    set pw .ngcdcsExpDrv.top.param.out
    
    set pl ""
    set pn [lsort -dictionary -increasing [array names param]]
    foreach ele $pn {
	set s [format "%-24s %s %s" $ele $param($ele) $paramUnit($ele)]
	lappend pl $s
	#$pw insert end $s
    }
    set gvar(paramList) $pl
}

##
# Select driver
#
proc ngcdcsExpDrvSelect {name} {
    global gvar
    
    #
    # Send driver command
    #
    .ngcdcsExpDrv.bot.stat configure -text ""
    ngcdcsExpDrvCmd "! $name"
}

##
# Update configuration parameter list
#
proc ngcdcsExpDrvUpdateList {} {
    global gvar

    foreach ele $gvar(drvList) {
	$gvar(drvMenu1) add command -label "$ele" \
	    -command "ngcdcsExpDrvSelect $ele"
	$gvar(drvMenu2) add command -label "$ele" \
	    -command "ngcdcsExpDrvSelect $ele"
    }
}

##
# Configure exposure driver
#
proc ngcdcsExpDrvCfg {} {
    global gvar param

    if {![info exists param]} {
	return
    }

    set pl [lsort -dictionary -increasing [array names param]]

    ngcdcsExpDrvCmd "\["
    foreach ele $pl {
	if {($param($ele) != "\"\"") && 
	    ([string first " " $param($ele)] < 0)} {
	    set val [string trim $param($ele) \"]
	} else {
	    set val $param($ele)
	}

	ngcdcsExpDrvCmd "$ele $val"
    }
    ngcdcsExpDrvCmd "\]"

    .ngcdcsExpDrv.bot.cfg configure -state disabled
    .ngcdcsExpDrv.bot.stat configure -text "IDLE"
    set gvar(pname) ""
    set gvar(pval) ""
}

##
# Delete exposure driver
#
proc ngcdcsExpDrvDelete {} {
    global gvar

    if {[.ngcdcsExpDrv.bot.abort cget -state] == "normal"} {
	set b [tk_messageBox -parent . -title "Caution" -icon "question" \
		   -type okcancel -default "cancel" -message \
		   "The exposure driver is still active! Abort?"]
	
	if {$b == "cancel"} {
	    return
	}
    }

    #
    # Send delete command
    #
    .ngcdcsExpDrv.bot.stat configure -text ""
    ngcdcsExpDrvCmd "d"
    set gvar(drvSel) "Select"
    set gvar(pname) ""
    set gvar(pval) ""

    set gvar(paramList) ""
}

##
# Reset exposure driver
#
proc ngcdcsExpDrvReset {} {
    global gvar

    #
    # Send reset command
    #
    .ngcdcsExpDrv.bot.stat configure -text ""
    ngcdcsExpDrvCmd "!"
    set gvar(pname) ""
    set gvar(pval) ""
}

##
# Start exposure sequence
#
proc ngcdcsExpDrvStart {} {
    global gvar

    if {[.ngcdcsExpDrv.bot.cfg cget -state] == "normal"} {
	set b [tk_messageBox -parent . -title "Warning" -icon "question" \
		   -type yesnocancel -default "yes" -message \
		   "Configuration has been modified! Upload before start?"]
	
	if {$b == "yes"} {
	    ngcdcsExpDrvCfg
	} elseif {$b == "cancel"} {
	    return
	}
    }

    #
    # Send start command
    #
    ngcdcsExpDrvCmd "s"
    .ngcdcsExpDrv.bot.start configure -state disabled
    .ngcdcsExpDrv.bot.reset configure -state disabled
    .ngcdcsExpDrv.bot.sel configure -state disabled
    .ngcdcsExpDrv.mbar.file.menu entryconfigure 1 -state disabled
    .ngcdcsExpDrv.mbar.file.menu entryconfigure 2 -state disabled
    .ngcdcsExpDrv.mbar.file.menu entryconfigure 3 -state disabled
    .ngcdcsExpDrv.bot.abort configure -state normal
}

##
# Abort exposure sequence
#
proc ngcdcsExpDrvAbort {} {
    global gvar

    #
    # Send abort command
    #
    ngcdcsExpDrvCmd "a"
}

##
# Clear action history
#
proc ngcdcsExpDrvClearLog {} {
    global gvar
    set w .ngcdcsExpDrv.top.hist.out
    $w configure -state normal
    $w delete 1.0 end
    $w configure -state disabled
}

##
# Save action history to file
#
proc ngcdcsExpDrvSaveLog {} {
    global gvar

    set text [.ngcdcsExpDrv.top.hist.out get 1.0 end]
    if {[string trim $text] == ""} {
	tk_messageBox -parent . -title "Info" -icon "info" \
		   -type ok  -message "Action log contains no data!"
	return
    }

    set types {
	{{Text Files} {.txt} }
	{{Log Files} {.log} }
	{{All Files} {*} }
    }
	
    set fn [tk_getSaveFile -parent . -filetypes $types \
		-title "Save Action Log"]
    if {$fn == ""} {
	return
    }
    if {[catch {open $fn w} fd]} {
	tk_messageBox -parent . -title "Error" -icon "error" \
		   -type ok  -message $fd
	return
    }

    puts $fd $text

    close $fd
}

##
# Save configuration to file
#
proc ngcdcsExpDrvSaveCfg {} {
    global gvar param paramUnit

    if {![info exists param]} {
	return
    }

    set text ""
    set pn [lsort -dictionary -increasing [array names param]]
    foreach ele $pn {
	if {$paramUnit($ele) != ""} {
	    set s [format "%-24s %s; \# %s\n" \
		       $ele $param($ele) $paramUnit($ele)]
	} else {
	    set s [format "%-24s %s;\n" $ele $param($ele)]
	}
	set text "$text$s"
   
    }

    if {$text == ""} {
	return
    }

    set types {
	{{Cfg Files} {.cfg} }
	{{All Files} {*} }
    }
	
    set fn [tk_getSaveFile -parent . -filetypes $types \
		-title "Save Configuration"]
    if {$fn == ""} {
	return
    }
    if {[catch {open $fn w} fd]} {
	tk_messageBox -parent . -title "Error" -icon "error" \
		   -type ok  -message $fd
	return
    }

    puts $fd $text

    close $fd
}

##
# Exit application
#
proc ngcdcsExpDrvExit {} {
    global gvar

    set w .ngcdcsExpDrv

    if {[.ngcdcsExpDrv.bot.abort cget -state] == "normal"} {
	set b [tk_messageBox -parent . -title "Caution" -icon "question" \
		   -type okcancel -default "cancel" -message \
		   "The exposure driver is still active! Abort exposure sequence and quit?"]
	
	if {$b == "cancel"} {
	    return
	}
    }

    #
    # Send exit command
    #
    ngcdcsExpDrvCmd "x"

    bind $w <Destroy> ""
    exit
}

##
# Top-level widget to find and load exposure driver modules
#
proc ngcdcsExpDrvFind {} {
    global env gvar

    #
    # Search INTROOT and VLTROOT
    #
    set c [.ngcdcsExpDrv cget -cursor] 
    .ngcdcsExpDrv configure -cursor watch
    update idletasks
    set gvar(drvLibs) ""
    if {[catch {set path [split $env(LD_LIBRARY_PATH) :]}]} {
	set path ""
	catch {lappend path "$env(INTROOT)/lib/"} 
	catch {lappend path "$env(VLTROOT)/lib/"}
    }
    foreach p $path {
	if {![catch {glob -directory $p "*.so"} libs]} {
	    foreach lib $libs {
		#
		# Extract module name (.../.../lib<mod>.so)
		#
		set mod [file rootname [file tail $lib]]
		if {[string range $mod 0 2] == "lib"} {
		    set mod [string range $mod 3 end]
		}

		#
		# Check _ngcdcs_expdrv_<mod> signature
		#
		set signature _ngcdcs_expdrv_$mod
		if {![catch {exec nm $lib | grep -w $signature} res]} {
		    if {$res != ""} {
			#
			# Add module to the list if it is not yet in. This
			# implies the search order INTROOT, VLTROOT
			# 
			if {[lsearch $gvar(drvLibs) $mod] == -1} {
			    lappend gvar(drvLibs) $mod
			}
		    }
		}
	    }
	}
    }
    .ngcdcsExpDrv configure -cursor $c
    
    #
    # Top-level widget
    #
    set w .ngcdcsExpDrvFind

    if {[winfo exists $w]} {
	#
	# Widget is already there - just raise it
	#
	raise $w
	wm deiconify $w
	return
    }

    toplevel $w
    wm title .ngcdcsExpDrvFind "Exposure Driver Modules"

    frame $w.top

    frame $w.top.libs -relief ridge -borderwidth 4
    label $w.top.libs.head -text "Available Modules:"
    listbox $w.top.libs.list -relief sunken -borderwidth 2 \
	-width 24 -height 8 -background white \
	-selectmode single -foreground blue \
	-font -Adobe-courier-medium-r-normal--12-*-*-*-*-*-iso8859-1 \
	-xscrollcommand "$w.top.libs.sx set" \
	-yscrollcommand "$w.top.libs.sy set" 
    $w.top.libs.list configure -listvariable gvar(drvLibs)
    scrollbar $w.top.libs.sx -command "$w.top.libs.list xview" \
	-orient horizontal
    scrollbar $w.top.libs.sy -command "$w.top.libs.list yview" \
	-orient vertical

    frame $w.top.sel
    button $w.top.sel.add -text Add -width 4 \
	-command ngcdcsExpDrvLoad
    button $w.top.sel.del -text Delete -width 4 \
	-command ngcdcsExpDrvUnload
    button $w.top.sel.dismiss -text Dismiss -width 4 \
	-command "wm withdraw $w"

    bind $w.top.sel.add <Enter> {
	.ngcdcsExpDrv.help.msg configure -text "Add exposure driver module"
    }
    bind $w.top.sel.add <Leave> ".ngcdcsExpDrv.help.msg configure -text {}"

    bind $w.top.sel.del <Enter> {
	.ngcdcsExpDrv.help.msg configure -text "Remove exposure driver module"
    }
    bind $w.top.sel.del <Leave> \
	".ngcdcsExpDrv.help.msg configure -text {}"
    
    bind $w.top.sel.dismiss <Enter> {
	.ngcdcsExpDrv.help.msg configure -text "Quit driver module loader"
    }
    bind $w.top.sel.dismiss <Leave> \
	".ngcdcsExpDrv.help.msg configure -text {}"
    
    frame $w.top.drv -relief ridge -borderwidth 4
    label $w.top.drv.head -text "Loaded Modules:"
    listbox $w.top.drv.list -relief sunken -borderwidth 2 \
	-width 24 -height 8 -background white \
	-selectmode single -foreground blue \
	-font -Adobe-courier-medium-r-normal--12-*-*-*-*-*-iso8859-1 \
	-xscrollcommand "$w.top.drv.sx set" \
	-yscrollcommand "$w.top.drv.sy set" 
    $w.top.drv.list configure -listvariable gvar(drvLd)
    scrollbar $w.top.drv.sx -command "$w.top.drv.list xview" \
	-orient horizontal
    scrollbar $w.top.drv.sy -command "$w.top.drv.list yview" \
	-orient vertical

    pack $w.top -side top

    pack $w.top.libs -side left -padx 0 -fill both
    pack $w.top.libs.head -side top -fill both
    pack $w.top.libs.sx -side bottom -fill x 
    pack $w.top.libs.sy -side right -fill y 
    pack $w.top.libs.list -side left -fill both

    pack $w.top.sel -side left -padx 5 -expand 1 -fill y -pady 25
    pack $w.top.sel.add $w.top.sel.del -side top
    pack $w.top.sel.dismiss -side bottom

    pack $w.top.drv -side left -padx 0 -fill both
    pack $w.top.drv.head -side top -fill both
    pack $w.top.drv.sx -side bottom -fill x 
    pack $w.top.drv.sy -side right -fill y 
    pack $w.top.drv.list -side left -fill both

}

##
# Load driver library
#
proc ngcdcsExpDrvLoad {} {
    global gvar

    set w .ngcdcsExpDrvFind.top.libs.list
    set index [$w curselection]

    if {$index != ""} {
	set module [$w get $index]
	if {[lsearch $gvar(drvLd) $module] == -1} {
	    set res [ngcdcsExpDrvCmd "L $module" 1]
	    if {$res} {
		lappend gvar(drvLd) $module
	    }
	}
    }
}

##
# Unload driver library
#
proc ngcdcsExpDrvUnload {} {
    global gvar

    set w .ngcdcsExpDrvFind.top.drv.list
    set index [$w curselection]
    if {$index == ""} {
	set w .ngcdcsExpDrvFind.top.libs.list
	set index [$w curselection]
    }

    if {$index != ""} {
	set module [$w get $index]
	set libIdx [lsearch $gvar(drvLd) $module]
	if {$libIdx >= 0} {
	    set res [ngcdcsExpDrvCmd "U $module" 1]
	    if {$res} {
		set gvar(drvLd) [lreplace $gvar(drvLd) $libIdx $libIdx]
	    }
	}
    }
}

proc ngcdcsExpDrvMenuHelp {w} {
    global gvar
    set index [$w index active]

    if {$index == "none"} {
	set txt {}
    } elseif {$index > 0} {
	set txt $gvar(menuHelp_$index)
    } else {
	set txt {}
    }

    .ngcdcsExpDrv.help.msg configure -text $txt
}

#
# Call top-level widget
#
ngcdcsExpDrv
    
#
# ___oOo___
