##################################################
# Commands:
#
# droexe prg_name prg_file inputs outputs
# ecexe prg_name prg_file inputs outputs
#
# panel panel_name panel_desc
#
# main panel_desc
#
# A panel is either a node or a leaf,
# depending on its class, a panel_desc is a list:
#   kind p1 p2 -opt1 v1 -opt2 v2 { panel_desc list }
# or
#   kind p1 p2 -opt1 v1 -opt2 v2
#
# Panel descs are conceptually DATA TREES and
# must be built using well nested braces {}.
# In particular: variable sibstitution ($id)
# are NOT HANDLED at tcl level.
#
# GLOBAL ARRAY 'Prg' contains all the infos that can be used
# in tcl scripts:
#
# Prg(command)
# - The tcl command to retrieve infos from the external rp
# - Normally: only for the 'luc' core, use Prg(xxx) in luciole/luciolerc !
# - infos are: nb_ins, nb_outs, in_name, out_name, in_type, out_type, step, reset
# Prg(creator) + Prg(creator_arg) are:
# - "droexe" + "file.dro", standard way to execute 'compiled' code
# - "ecexe" +  "file.ec", for historical luc embeds the ec interpretor
# - "rpexe" + "file.ext", just sugar, determines the actual creator according to ')ext'
# - "pipexe" + "<shell commands>", standard way to execute core via text pipes
# Prg(module_name)
# - the 'functionnal' name of the external program
# - in general the 'node' name for programs comming from lustre or lutin:
# - set to '_unnamed_' if it can be determined by luciole
# Prg(input_names)
# Prg(input_types)
# Prg(output_names)
# Prg(output_types)
# Prg(user_reset)
# Prg(user_step)
##################################################


##################################################
##### LOAD DYNAMIC LIB ###########################
##################################################

# Load c-stubs, import commands (machine dep.):
# - for loading/running the 'dro' appli
# - misc utilities (e.g. timer)
# 2026/02 -> check version compat beteen tclsh and lustubs.so
# - only major version is takenm into account, maybe not a good idea ...
# - more complex, put in separate file
# load lustubs (provides droexe/ecexe)
#load $::env(LUSTRE_INSTALL)/tcl/lustubs.so
source $::env(LUSTRE_INSTALL)/tcl/luciole/luc-load.tcl

# Load ptk lib (machine indep.)
lappend auto_path $::env(LUSTRE_INSTALL)/tcl
package require Ptk
package provide Luciole 2.0
#source ptk.tcl
set ptk::dbg(on) 0

##################################################
##### USER INTERFACE #############################
##################################################

# hygiene: a few number of commands are exported
# use:  namespace import luc::*

# RPEXE INTERFACE
# - exactly one 'droexe' or 'ecexe' or 'rpexe' command
#   is expected, before building the panel
# - rpexe determines file kind from extension
# - droexe/ececexe ignore extension
namespace eval luc {
	namespace export droexe
	namespace export ecexe
	namespace export rpexe
	namespace export pipexe
	namespace export rifexe
}
#
# Alternatively, one can call 'MACHIN nme file.ext':
# determine the 'type' according to .ext
# load it  and init exec env with infos retrived from the rpexe
namespace eval luc {
	namespace export MACHIN
}


# GUI BUILDING, ``braced-style''
namespace eval luc {
	namespace export panel
	namespace export stdgui
	namespace export set_title
}

# GUI BUILDING, imperative ``ptk'' style
#
# The gui can be build using ptk begin/end + add mechanism.
# In addition to ptk items, luc provides:
# - One specific container:
#   * 'begin_stdgui'/'end_stdgui'
#      that start and finalize a classical lurette (v1)
#      top-level with menus, auto-step/compose mode,
#      step button, luciolerc support etc.
# - A few leaves, implementing the classical luciole v1 widgets:
#   * 'inbool':    (radio)button for the auto-step/compose mode
#   * 'outbool':   grey/red label
#   * 'intscale':  default widget for integer inputs
#   * 'realscale': default widget for floating-point inputs
#   * 'outvar':    default non-Boolean output
#
# WARNINGS:
# - exactly one TOP container is expected,
#   either 'luc::begin_stdgui/luc::end_stdgui'
#   or 'ptk::begin main/ptk::end main'
# - specific luc leaves are not supposed to work properly
#   outside the luc 'stdgui'. In particular
namespace eval luc {
	namespace export begin_stdgui end_stdgui
	namespace export inbool
	namespace export outbool
	namespace export intscale
	namespace export realscale
	namespace export outvar
}

# EXEC PRG INTERFACE
# These variables are available once dro/ec exe has been executed
namespace eval luc {
	namespace export Prg
   namespace export InputVars
   namespace export OutputVars
   #namespace export VarAttrs
	namespace export VarType
# either in or out
	namespace export VarPin
}

# return $dflt if key does not exist

# MISC INTERFACE
namespace eval luc {
	namespace export set_dbg
	namespace export set_verb
	namespace export parse_args
	namespace export parse_these_args
	namespace export print_options
}

##################################################
##### VERBOSE & DEBUG
##################################################

# hygiene: everything that starts with capital is 'private'
namespace eval luc {
	variable Global
	variable VerbChannels
	set Global(std_verb_on) 0
	set Global(std_verb_channel) stderr

	set Global(dbg) 0
	set Global(dbg_channel) stderr
}

#### Internal procs
proc luc::verbose_on { } {
   set luc::VerbChannels($luc::Global(std_verb_channel)) 1
	luc::DBG "set luc::VerbChannels($luc::Global(std_verb_channel)) 1"
	luc::_puts_profile $luc::Global(std_verb_channel)
}
proc luc::verbose_off { } {
   set luc::VerbChannels($luc::Global(std_verb_channel)) 0
}
proc luc::_trace_verb_is_on { args } {
#puts "trace luc::Global(std_verb_on)=$luc::Global(std_verb_on)"
   if { $luc::Global(std_verb_on) } {
      luc::verbose_on
   } else {
      luc::verbose_off
   }
}
trace add variable luc::Global(std_verb_on) write luc::_trace_verb_is_on

proc luc::verbose_to { channel } {
	luc::DBG "luc::verbose_to $channel"
   #unset -nocomplain luc::VerbChannels($channel)
   set luc::VerbChannels($channel) 1
	luc::_puts_profile $channel
}

proc luc::verbose_close { channel } {
   #unset -nocomplain luc::VerbChannels($channel)
   set luc::VerbChannels($channel) 0
}

proc luc::Verbose {msg} {
	#luc::DBG "luc::Verbose"
	#parray luc::VerbChannels
	foreach { ch x } [array get luc::VerbChannels] {
		#luc::DBG "luc::VerbChannels $ch $x"
		if { $luc::VerbChannels($ch) } {
			luc::DBG "puts $ch $msg"
			puts $ch "$msg"
			flush $ch
		}
	}
}
proc luc::VerboseNNL {msg} {
	#luc::DBG "luc::VerbChannels"
	foreach { ch x } [array get luc::VerbChannels] {
		#luc::DBG "luc::VerbChannels $ch $x"
		if { $luc::VerbChannels($ch) } {
			luc::DBG "puts -nonewline $ch $msg"
	puts -nonewline $ch "$msg"
	flush $ch
		}
   }
}
proc luc::_puts_profile { ch } {
	luc::DBG "_puts_profile $ch"
	# only if profile is defined !
	if { $luc::Prg(module_name) != "" } {
		#luc::DBG "#program \"$luc::Prg(module_name)\""
	#puts $ch "#program $luc::Prg(module_name)"
	puts $ch "#program \"$luc::Prg(module_name)\""
	}
	if { $luc::Prg(input_names) != "" } {
	puts -nonewline $ch "#inputs"
	foreach n $luc::Prg(input_names) t $luc::Prg(input_types) {
	puts -nonewline $ch " $n:$t"
	}
	puts $ch ""
	}
	if { $luc::Prg(output_names) != "" } {
	puts -nonewline $ch "#outputs"
	foreach n $luc::Prg(output_names) t $luc::Prg(output_types) {
	puts -nonewline $ch  " $n:$t"
	}
	puts $ch ""
	}
}

proc luc::DBG {args} {
	set msgs $args
   if $luc::Global(dbg) then {
      puts -nonewline $luc::Global(dbg_channel) "#DBG: "
		foreach m $msgs {
	puts -nonewline $luc::Global(dbg_channel) "$m"
		}
		puts $luc::Global(dbg_channel) ""
      flush $luc::Global(dbg_channel)
   }
}
proc luc::Error {msg} {
	puts stderr "luc error: $msg"
	exit 1
}
proc luc::ErrorCode { code args } {
	set msg [lindex $args 0]
	set others [lrange $args 1 end]
	puts stderr "luc error: $msg"
	foreach ms $others {
		foreach m [split $ms "\n"] {
		puts stderr "   $m"
		}
	}
	exit $code
}


proc luc::set_dbg { v } {
	set luc::Global(dbg) $v
}

proc luc::set_verb { v } {
	set luc::Global(std_verb_on) $v
}

# - md is the name of current command (~ argv0)
# - args is a list of command-line options (~argv)
# scan the args list for luc-specific comand line option
# Recognized opts are:
set luc::Options {
	{ "-h" "print help message" "luc::help" }
	{ "-v" "set verbose mode"  "luc::set_verb 1" }
	{ "-dbg" "set debug mode"  "luc::set_dbg 1" }
	{ "-norc" "dont load luciolerc file" "set luc::Global(norc) 1" }
	{ "-auto" "starts in auto-step mode" "set luc::Global(auto_step) 1" }
	{ "-comp" "starts in compose mode" "set luc::Global(auto_step) 0" }
}
proc luc::print_options { ch } {
	foreach opt $luc::Options {
		set k [lindex $opt 0]
		set m [lindex $opt 1]
		puts $ch "   [format "%-6s:" $k] $m"
	}
}

set luc::ScriptName "<luc-script>"
proc luc::help { } {
	puts "usage: $luc::ScriptName \[options\]"
	puts "recognized options:"
	luc::print_options stdout
	exit 0
}
# to pass specific args
proc luc::parse_these_args { argv0 argv } {
	luc::DBG "luc::parse_opts '$argv0' '$argv'"
	set luc::ScriptName $argv0
	proc treat_arg { a } {
		foreach opt $luc::Options {
			set k [lindex $opt 0]
			if { $a == $k } {
				luc::DBG "    $a is a recognized option"
				eval [lindex $opt 2]
				return
			}
		}
		luc::DBG "    $a is NOT a recognized option"
	}
	foreach a $argv {
		treat_arg $a
	}
}
# to pass standard argv
proc luc::parse_args { } {
	luc::parse_these_args $::argv0 $::argv
}

##################################################
##### UTILS (checks, asserts ...) ################
##################################################

# assert utils: fail is assert is not satified
# e.g. luc::ASSERT_length $args ">0" "non empty args expected"
proc luc::ASSERT_length { lst cond msg } {
	set ll [llength $lst]
	set ok [eval "expr $ll $cond"]
	if {! $ok} then {
		luc::Error $msg
	}
}

proc luc::ASSERT { cond msg } {
	set ok [eval "expr $cond"]
	if {! $ok} then {
		luc::Error $msg
	}
}

##################################################
#### Exported proc 'luc::panel'
##################################################

#panel = a 'tcl::set' with
#args checks (TODO)
proc luc::panel { panid args } {
	global $panid
	set $panid $args
}

##################################################
#### Exported proc 'luc::droexe'
##################################################

# Vars management
namespace eval luc {
   # Program infos
   set Prg(module_name)  {}
   set Prg(command)      {}
   set Prg(creator)      {}
   set Prg(creator_arg)  {}
   set Prg(input_names)  {}
   set Prg(input_types)  {}
   set Prg(output_names) {}
   set Prg(output_types) {}
   # User step/reset
   set Prg(user_step) {}
   set Prg(user_reset) {}
# Where are stored the input/output values
# luc::InputVars: input_name -> value
# luc::OutputVars: output_name -> value
   variable InputVars
   variable OutputVars
# Attributes/type
   #variable VarAttrs
   variable VarType
   variable VarPin
}

# OBSOLETE -> use standard tk options instead
# return $dflt if key does not exist
# proc luc::GetVarAttr { id key dflt } {
# 	if [ catch { dict get $luc::VarAttrs($id) "$key" } res ] {
#       return "$dflt"
# 	} else {
# 		return "$res"
# 	}
# }

set luc::DfltIO(in,bool) inbool
set luc::DfltIO(in,int) intscale
set luc::DfltIO(in,real) realscale
set luc::DfltIO(in,double) realscale
set luc::DfltIO(in,float) realscale

set luc::DfltIO(out,bool) outbool
set luc::DfltIO(out,int) outvar
set luc::DfltIO(out,real) outvar
set luc::DfltIO(out,double) outvar
set luc::DfltIO(out,float) outvar


# io=in|out
# N.b. create a default panel with the name of var
proc luc::Add_var { io prg vdecl } {
	set vname [lindex $vdecl 0]
	set vtype [lindex $vdecl 1]
	set vopts [lrange $vdecl 2 end]
	luc::DBG "add '$io' var in prg '$prg': name=$vname type=$vtype opts='$vopts'"
   if { $io == "in" } {
	lappend luc::Prg(input_names) $vname
	lappend luc::Prg(input_types) $vtype
	set luc::InputVars($vname) 0
	#create attrs dict...
	#set luc::VarAttrs($vname) [dict create {*}$vopts]
	set luc::VarType($vname) $vtype
	set luc::VarPin($vname) $io
      $prg add_in luc::InputVars($vname) "$vtype"
   } elseif { $io == "out" } {
	lappend luc::Prg(output_names) $vname
	lappend luc::Prg(output_types) $vtype
	set luc::OutputVars($vname) 0
	#create attrs dict...
	#set luc::VarAttrs($vname) [dict create {*}$vopts]
	set luc::VarType($vname) $vtype
	set luc::VarPin($vname) $io
      $prg add_out luc::OutputVars($vname) "$vtype"
   } else {
      puts stderr "luc::Add_var: expect in or out mode"
      exit 1
   }
	# define default panel for this var
	luc::DBG "   (dflt panel) luc::panel $vname $luc::DfltIO($io,$vtype) $vname"
	luc::panel $vname $luc::DfltIO($io,$vtype) $vname {*}$vopts
}

# use only catch_init_rpexe_vars
proc luc::_do_init_rpexe_vars { prgname args } {
	set argc [llength $args]
	luc::DBG "luc::init_rpexe_vars $prgname $args (argc=$argc)"
	switch -glob $argc {
		"0" {
			# auto set ins/outs
			set nbi [$prgname nb_ins]
			for {set i 0} {$i<$nbi} {incr i} {
				set n [$prgname in_name $i]
				set t [$prgname in_type $i]
				luc::Add_var in $prgname [list $n $t]
			}
			set nbo [$prgname nb_outs]
			for {set i 0} {$i<$nbo} {incr i} {
				set n [$prgname out_name $i]
				set t [$prgname out_type $i]
				luc::Add_var out $prgname [list $n $t]
			}
		}
		"2" {
			# use user-given ins/outs, error if not compat
			set ins [lindex $args 0]
			set actual [llength $ins]
			set expect [$prgname nb_ins]
			luc::DBG "luc::init_rpexe_vars ($prgname nb_ins = $expect)"
			if { $expect != $actual } {
				return -code error "expect $expect inputs, get $actual"
			}
			foreach indecl $ins {
				luc::Add_var in $prgname $indecl
			}
			set outs [lindex $args 1]
			set actual [llength $outs]
			set expect [$prgname nb_outs]
			if { $expect != $actual } {
				return -code error "expect $expect outputs, get $actual"
			}
			foreach outdecl $outs {
				luc::Add_var out $prgname $outdecl
			}
		}
		default {
			luc::Error "rpexe takes 0 or 2 arguments (not $argc)"
		}
	}
	# verbose profile ...
	#if { $luc::Global(std_verb_on) } { luc::_puts_profile $luc::Global(std_verb_channel) }
}
proc luc::init_rpexe_vars { prgname args } {
	if [ catch { luc::_do_init_rpexe_vars $prgname {*}$args } res ores ] {
		#luc::DBG "catch res:`$res`"
		#luc::DBG "catch ores:`$ores`"
		luc::ErrorCode 11 "can't exec '$prgname' ('$luc::Prg(creator_arg)'), interface mismatch:" $res
	}
}

proc luc::droexe { prgname fname args } {
	# create exec
	# TODO catch
	droload $prgname $fname
   set luc::Prg(command)    $prgname
   set luc::Prg(creator)  "droexe"
   set luc::Prg(creator_arg)  $fname
	set luc::Prg(module_name) [$prgname name]
	# init vars
	luc::init_rpexe_vars $prgname {*}$args
   set luc::Prg(user_step) "$prgname step"
   set luc::Prg(user_reset) "$prgname reset"
}

#catch {regsub -all {[^a-zA-Z0-9]+} $title "_" pname}

proc luc::ecexe { prgname fname args } {
	# create exec
	# TODO catch
	luc::DBG "ecexe $prgname $fname $args"
	ecload $prgname $fname
   set luc::Prg(command)    $prgname
   set luc::Prg(creator)  "ecexe"
   set luc::Prg(creator_arg)  $fname
	set luc::Prg(module_name) [$prgname name]
	# init vars
	luc::init_rpexe_vars $prgname {*}$args
   set luc::Prg(user_step) "$prgname step"
   set luc::Prg(user_reset) "$prgname reset"
}

proc luc::pipexe { prgname cmd args } {
	pipecreate $prgname $cmd
	set luc::Prg(command)    $prgname
	set luc::Prg(creator)  "pipexe"
	set luc::Prg(creator_arg)  $cmd
	set luc::Prg(module_name) [$prgname name]
	luc::init_rpexe_vars $prgname {*}$args
	set luc::Prg(user_step) "$prgname step"
   set luc::Prg(user_reset) "$prgname reset"
}

proc luc::rifexe { prgname args } {
	rifcreate $prgname {*}$args
	set luc::Prg(command)    $prgname
	set luc::Prg(creator)  "rifexe"
	set luc::Prg(creator_arg)  "none"
	set luc::Prg(module_name) [$prgname name]
	luc::init_rpexe_vars $prgname {*}$args
	set luc::Prg(user_step) "$prgname step"
   set luc::Prg(user_reset) "$prgname reset"
}


proc luc::rpexe { prgname fname args} {
	set ext [file extension $fname]
	switch -glob $ext {
		".ec" {
			ecexe $prgname $fname {*}$args
		}
		".dro" {
			droexe $prgname $fname {*}$args
		}
		default {
			luc::Error "(rpexe) unexpected file extension '$ext'"
		}
	}
}

# Misc. util, set the window title
proc luc::set_title { title } {
   set luc::Prg(module_name)  "$title"
}

##################################################
#### Exported proc 'luc::stdgui'
##################################################

# - Parse and 'realize' a panel spec.
# - A panel spec is a list of item specs.
# - An item has a 'kind': a string that cannot
#   be overloaded -> kind instances are separators
#   in panel desc.
# - An item is either a node (container) or a leaf
# - A container spec is of the form:
#   kind p1 p2 -opt1 v1 -opt2 v2 { sons }
#   where sons is a panel desc (list of sub-item desc)
# - A leaf spec is of the form:
#   kind p1 p2 -opt1 v1 -opt2 v2
# - p1 p2 etc, are MANDATORY parameters, whose nat
#   and number depends on 'kind'
# - -opt1 v1 -opt2 v2 etc. is a possibly empty list
#   of 'tk-like' options
# - known items are stored in the Kind

variable luc::Items
proc luc::Is_item { s } {
	return [info exists luc::Items($s)]
}

proc luc::decl_node { kind params begin end config } {
	set luc::Items($kind) $kind
	set luc::Items($kind,class) "node"
	set luc::Items($kind,params) $params
	set luc::Items($kind,begin) $begin
	set luc::Items($kind,end) $end
	set luc::Items($kind,config) $config
}
#### IMPLEMENTED NODES (inherited from ptk)
luc::decl_node "line" {} "ptk::begin" "ptk::end" "ptk::configure"
luc::decl_node "col" {} "ptk::begin" "ptk::end" "ptk::configure"
luc::decl_node "hbar" {} "ptk::begin" "ptk::end" "ptk::configure"
luc::decl_node "vbar" {} "ptk::begin" "ptk::end" "ptk::configure"
luc::decl_node "popup" {title} "ptk::begin" "ptk::end" "ptk::configure"

#### AD HOC NODES
luc::decl_node "toggle" {var} "luc::begin_toggle" "luc::end_toggle" "ptk::configure"
luc::decl_node "switch" {var vals} "luc::begin_switch" "luc::end_switch" "ptk::configure"

proc luc::decl_leaf { kind params make config } {
	set luc::Items($kind) $kind
	set luc::Items($kind,class) "leaf"
	set luc::Items($kind,params) $params
	set luc::Items($kind,make) $make
	set luc::Items($kind,config) $config
}

#### IMPLEMENTED LEAVES
luc::decl_leaf "inbool" {var} "luc::inbool" "luc::configure_inbool"
luc::decl_leaf "outbool" {var} "luc::outbool" "luc::configure_outbool"
luc::decl_leaf "outvar" {var} "luc::outvar" "luc::configure_outvar"
luc::decl_leaf "box" {} "ptk::add label" "ptk::configure"
luc::decl_leaf "intscale" {var} "luc::intscale" "luc::configure_intscale"
luc::decl_leaf "realscale" {var} "luc::realscale" "luc::configure_intscale"

#
# To keep proc sizes reasonable:
# mutually rec. procs:
# - make_panels parse `raw' panel desc list and calls either:
# - make_node { kind params opts sons}
# - make_leaf { kind params opts }
#
# OPTS PARSING:
# To ease script writing:
# the opts part of a panel can be any recursive list (tree)
# of pairs '-k v', e.g. :
# set basicopts {-bg blue -fg red}
# set lineopt {-relief raised $basicopts}
# ...
# line $lineopts -bd 10 { ... }
#
# SONS PARSING:
# To ease script writing:
proc flatten list {
    for {set old {}} {$old ne $list} {} {
	set old $list
	set list [join $list]
    }
    return $list
}

proc luc::make_panels { pnl } {
	# arg list iter
	set argv $pnl
	set argc [llength $pnl]
	if { $argc == 0 } return

	proc SHIFT {} {
		upvar 1 argv argv
		upvar 1 argc argc
		if { $argc > 0 }  {
			set argv [lrange $argv 1 end]
			set argc [llength $argv]
		}
	}
	proc HEAD {} {
		upvar 1 argv argv
		return [lindex $argv 0]
	}
	proc ISOPT {} {
		upvar 1 argc argc
		upvar 1 argv argv
		if { $argc > 0 } {
			set hd [lindex $argv 0]
			set flat_hd [flatten $hd]
#puts "---> flat_hd=$flat_hd"
			set fc [string index $flat_hd 0]
			if { $fc eq "-" } {
#puts "---> flat_hd IS opt list"
				## YES: remove hd from argv
				SHIFT
				## concat flat_hd + argv
				set argv [concat $flat_hd $argv]
				set argc [llength $argv]
				return 1
			} else {
#puts "---> flat_hd IS NOT opt list"
				return 0
			}
		} else {
			return 0
		}
	}
	luc::DBG "make_panels $pnl"
#foreach x $pnl {
#puts -nonewline "  \"$x\""
#}
#puts ""
	ASSERT_length $argc ">= 1" "unexpected empty panel"
	set kind [HEAD]
#puts "   --> $kind"
	SHIFT
	ASSERT [Is_item $kind] "unexpected panel kind '$kind'"
	# parse mandatory params
	set xparams $luc::Items($kind,params)
#puts "xparams=$xparams"
	set xln [llength $xparams]
	ASSERT_length $argv ">= $xln" "$xln params expected for '$kind'"
	set params {}
	foreach p $xparams {
#puts "p=$p"
		lappend params [HEAD]
		SHIFT
	}
#puts "params=$params"
	# parse opts
	# while head is a string starting with -
	set opts {}
	while { [ISOPT] } {
#puts "ISOPT=[ISOPT]"
		set opt [HEAD]
		lappend opts $opt
		SHIFT
		ASSERT_length $argc ">=1" "opt '$opt' without value"
		set val [HEAD]
		lappend opts $val
		SHIFT
#puts "opt: $opt $val"
#puts "argv: $argv"
	}
#puts "opts=$opts"
	# rest = nothing (leaf) or panel list (node)
	#
	if { $luc::Items($kind,class) eq "node" } {
		ASSERT_length $argv ">= 1" "missing sons list for node '$kind'"
		set sons [HEAD]
		SHIFT
#puts "sons=$sons"
#foreach s $sons {
#puts "son+=$s"
#}
		luc::make_node $kind $params $opts $sons
		luc::make_panels $argv
	} else {
		#ASSERT_length $argv "== 0" "unexpected args for leaf '$kind'"
		luc::make_leaf $kind $params $opts
		luc::make_panels $argv
	}
}

## OBSOLETE
## IMPORTANT TRICKY
## subsitute user var ref:
## - within top env
## - keeping the 'list' structure
#proc luc::Subst_safe { usr_string } {
#	# Add backslashes in front of metacharacters
##puts "    usr_string='$usr_string'"
#	set safer [string map {\\ \\\\ [ \\[ ] \\]} $usr_string]
#	# Convert variable mentions to our “safe” version
##puts "    safer='$safer'"
#	regsub -all {\$\w+} $safer {[list &]} safe_string
##puts "    safe_string='$safe_string'"
#	set cmd "subst \"$safe_string\""
##puts "    cmd='$cmd'"
#	set res [uplevel #0 $cmd]
##puts "    res='$res'"
#	return $res
#}

proc luc::Subst { usr_string } {
#puts "--> usr_string='$usr_string'"
	#set cmd "subst \"$usr_string\""
	set cmd "subst {$usr_string}"
#puts "--> cmd=$cmd"
	set res [uplevel #0 $cmd]
	return $res
}

proc luc::make_node { kind params opts sons } {
	luc::DBG "make_node kind=\"$kind\"" \
		" params=\"$params\" opts=\"$opts\" sons=\"$sons\""
	set begin $luc::Items($kind,begin)
	set end $luc::Items($kind,end)
	# call begin
	eval "$begin $kind $params $opts"
	# Subsitute possible users vars (at level 0)
	set esons [luc::Subst $sons]
#puts "--> esons = [luc::Subst $sons]"
	make_panels $esons

	# call end
	eval $end $kind
}
proc luc::make_leaf { kind params opts } {
	luc::DBG "make_leaf kind=\"$kind\"" \
		" params=\"$params\" opts=\"$opts\""
	set make $luc::Items($kind,make)
	# call correspondind maker
	eval "$make $params $opts"
}

### GUI variables
namespace eval luc {
	set Global(show_step) 1
	set Global(auto_step) 1
	set Global(step_ctr) 0
	set Global(just_reset) true
	set Global(show_step_ctr) 1
}
# just_reset means programs outputs are irrelevant
# i.e. very begining of after a user_reset command
# n.b. equiv. to "step_ctr = 0"
# GUI id's for later custom (cf. luciolerc)
namespace eval luc {
	set GUI(menu_hbar) ""
	set GUI(clocks_menu) ""
	set GUI(files_menu) "i"
	set GUI(options_menu) ""
}
#---------------------------------------------------------
# load luciolerc.tcl
# load the first existing file in the following list:
#    $PWD/luciolerc.tcl
#    $PWD/.luciolerc.tcl
#    $HOME/luciolerc.tcl
#    $HOME/.luciolerc.tcl
#    $LUSTRE_INSTALL/tcl/luciole/luciolerc.tcl
#
# IMPORTANT: luciolerc.tcl MUST BE evaluated at TOP LEVEL
#---------------------------------------------------------
namespace eval luc {
	set Global(norc) 0
}

proc luc::load_luciolerc {} {
	if { $luc::Global(norc) } { return TCL_OK }
	foreach x [ list \
				"$::env(PWD)" \
				"$::env(HOME)" \
				"$::env(LUSTRE_INSTALL)/tcl/luciole" ] {
		foreach f [ list "luciolerc.tcl" ".luciolerc.tcl" ] {
			if [ file exists "$x/$f"] {
##puts "source: $x/$f"
				if [ catch {uplevel #0 source "$x/$f"} err ] {
					luc::DBG "rc file: FAILS ($x/$f)"
					luc::DBG "      err:'$err'"
					return TCL_KO
				} else {
					luc::DBG "rc file: OK ($x/$f)"
					return TCL_OK
				}
			} else {
				#luc::DBG "$x/$f DOEST NOT EXIST"
			}
		}
	}
	luc::DBG "rc file: no \"luciolerc.tcl\" found"
	return TCL_OK
}

proc luc::LeftMenu { } {
	# MENU LEFT PART = Files | Options | Clocks
	ptk::begin hbar
	# this is where user may insert stuff	(cg. luciolerc)
	ptk::store luc::GUI(menu_hbar)
		# FILES menu
		ptk::begin menubutton -text "File" -underline 0
		ptk::store luc::GUI(files_menu)
			#TODO save something ?
			# n.b. Quit button added at the end
		ptk::end menubutton
		# OPTIONS menu
		ptk::begin menubutton -text "Options" -underline 0
		ptk::store luc::GUI(options_menu)
			ptk::add menu_checkbutton -label "Verbose" -underline {0} \
				-variable {luc::Global(std_verb_on)}
			ptk::add menu_checkbutton -label "Show Step button" -underline {0} \
				-variable {luc::Global(show_step)}
			ptk::add menu_checkbutton -label "Show Step counter" -underline {0} \
				-variable {luc::Global(show_step_ctr)}
		ptk::end menubutton
		# CLOCKS menu
		ptk::begin menubutton -text "Clocks" -underline 0
		ptk::store luc::GUI(clocks_menu)
			ptk::add command -label "Reset" -underline {0} \
				-command {luc::TreatReset}
			ptk::add menu_separator
			ptk::add radiobutton -label "Auto step" -underline {0} \
				-value {1} -variable {luc::Global(auto_step)}
			ptk::add radiobutton -label "Compose" -underline {0} \
				-value {0} -variable {luc::Global(auto_step)}
		ptk::end menubutton
	ptk::end hbar
}
proc luc::RightMenu { } {
	ptk::add label \
		-textvariable luc::Global(step_ctr) \
		-borderwidth 2 \
		-relief {sunken}
	ptk::showif luc::Global(show_step_ctr)

}

# begin/end interface

proc luc::begin_stdgui {  } {
	ptk::begin main $luc::Prg(module_name)
	ptk::confirm_quit "Quit ?"
	# MAIN MENU
	# use a hbar rather than main_menu to add stuff on the right part
	ptk::begin hbar
		luc::LeftMenu
		ptk::add fill
		luc::RightMenu
	ptk::end hbar
}

proc luc::end_stdgui {  } {
	# step button
	ptk::begin hbar
   ptk::showif luc::Global(show_step)
		ptk::add button -text "Step" -command luc::TreatStep
		ptk::packreq -fill x -expand 1
	ptk::end hbar
	### integrate luciolerc.tcl
	luc::load_luciolerc

	## Quit button is added to the end of file menu
	ptk::reopen $luc::GUI(files_menu)
		ptk::add menu_separator
		ptk::add menu_command -label "Quit" -command {exit}	-underline {0}
	ptk::reclose

	ptk::end main
}

proc luc::stdgui { args } {
	set pnl $args

	luc::begin_stdgui

	# insert user panel
	luc::make_panels $pnl

	luc::end_stdgui
}

#### TreatStep

proc luc::TreatStep { } {
   luc::DBG "call of external user_step"
	luc::DBG "luc::TreatStep: luc::Prg(user_step) = \"$luc::Prg(user_step)\""
   set step_ok [ catch $luc::Prg(user_step) ]
   #eval $luc::Prg(user_step)
	#luc::Verbose "#user step returns $step_ok"
   if { $step_ok == 0 } {
      incr luc::Global(step_ctr)
		if { $luc::Global(just_reset) } { set luc::Global(just_reset) false }
      luc::Verbose "#step $luc::Global(step_ctr)"
      foreach inme $luc::Prg(input_names) {
	 luc::VerboseNNL "$luc::InputVars($inme) "
      }
      luc::VerboseNNL "#outs "
      foreach onme $luc::Prg(output_names) {
	 luc::VerboseNNL "$luc::OutputVars($onme) "
      }
      luc::Verbose ""
   }
}
#### TreatReset
proc luc::TreatReset { } {
   luc::DBG "call of \"TreatReset \""
   #reset the input values
   foreach inme $luc::Prg(input_names) {
      set luc::InputVars($inme) 0
   }
#reset the outputs values
   foreach onme $luc::Prg(output_names) {
      set luc::OutputVars($onme) 0
   }
#reset the history
   luc::Verbose "#reset"
   luc::DBG "call of external user_reset"
   set rok [ catch $luc::Prg(user_reset) ]
   set luc::Global(step_ctr) 0
   set luc::Global(just_reset) true
}
