
global Rifenv
set Rifenv(dbg) 1

proc DBG_RIF args {
	luc::DBG "(RIF) $args"
}

proc _rif_add_in { pname tclvar typ } {
	global Rifenv
DBG_RIF "_rif_add_in $pname $tclvar $typ"
	#TODO verif size and type
	#puts "lappend Rifenv($pname,invars) $tclvar $typ"
	lappend Rifenv($pname,invars) $tclvar
}
proc _rif_add_out { pname tclvar typ } {
	global Rifenv
DBG_RIF "_rif_add_out $pname $tclvar $typ"
	#TODO verif size and type
	#puts "lappend Rifenv($pname,outvars) $tclvar"
	lappend Rifenv($pname,outvars) $tclvar
}

proc _rif_getline { pname } {
	global Rifenv
	set inc $Rifenv($pname,in_channel)
   gets $inc line
	return $line
}

# only basic step available !
proc _do_rif_step { pname } {
	global Rifenv
	# write all inputs on stdout
	set out_channel $Rifenv($pname,out_channel)
	luc::DBG "#rif_step, send inputs: $Rifenv($pname,inprof)"
	foreach inme $Rifenv($pname,invars) {
		upvar 1 $inme ivar
		set value $ivar 
DBG_RIF "_do_rif_step: write input $inme (value $value)"
		puts $out_channel $value
		flush $out_channel
	}
	# blocking read of outputs (or other command)
	set inc $Rifenv($pname,in_channel)	

	# Capture comments/pragmas
	set line ""
	while { "$line" == "" } {
		luc::DBG "#rif_step, wait outputs: $Rifenv($pname,outprof)"
   	set line [_rif_getline $pname]
		if {[string index $line 0] eq "#"} {
			# comment or pragma
			if [string match "#reset*" $line] {
DBG_RIF "DO RESET"
			} elseif [string match "#quit*" $line] {
				#TODO close pipes ?
				exit 0
			}
DBG_RIF "IGNORE"
			set line ""
		} else {
DBG_RIF "OK, goon"
		}
	}
	# Outputs must be in $line
	set nb_outs $Rifenv($pname,nb_outs)
	set ocpt 0
	set goon [expr $ocpt < $nb_outs]
	while { $goon } {
		foreach value $line {
			set onme [lindex $Rifenv($pname,outvars) $ocpt]
			upvar 1 $onme ovar
DBG_RIF "read output $ovar (value $value)"
			set ovar $value
			incr ocpt
		}
		set goon [expr $ocpt < $nb_outs]
	}
}

proc _do_rif_reset { pname } {
	global Rifenv
	set outc $Rifenv($pname,out_channel)
	puts $out_channel "#reset"
	flush $out_channel
}

proc _do_rif_cmd { pname cmd  args } {
	global Rifenv
	global Rifenv $pname
DBG_RIF "_do_rif_cmd $pname $cmd $args"
	switch -glob $cmd {
		"name" { return $Rifenv($pname,name) }
		"nb_ins" { return $Rifenv($pname,nb_ins) }
		"nb_outs" { return $Rifenv($pname,nb_outs) }
		"in_name" { return [lindex $Rifenv($pname,in_names) $args] }
		"out_name" { return [lindex $Rifenv($pname,out_names) $args] }
		"in_type" { return [lindex $Rifenv($pname,in_types) $args] }
		"out_type" { return [lindex $Rifenv($pname,out_types) $args] }
		"add_in" { _rif_add_in $pname [lindex {*}$args 0] [lindex {*}$args 1] }
		"add_out" { _rif_add_out $pname [lindex {*}$args 0] [lindex {*}$args 1] }
		"step" { _do_rif_step $pname }
		"reset" { _do_rif_reset $pname }
	}
}

# while here, all code is pure tcl:
# - creates a 'command' whose name is the 'reactive program' (pname)
#   this commands serves to get the informations (in, outs etc.)
# - the global infos are created and stored in a global array
#   with a double key: Rifenv($pname,key) 
#
proc names_and_types {vl} {
	set vl [regexp -all -inline {\S+} $vl]
	set vl [lmap x $vl {split $x ":"}]
	set nl [lmap x $vl {string trim [lindex $x 0] " \""} ]
	set tl [lmap x $vl {lindex $x 1}]
	list $nl $tl
}

# In rif mode:
# - no command to launch, luc is supposed to be connected
#   via stdin/stdout
# step:
# - write inputs on stdout
# - read outputs on stdin

proc rifcreate { pname args } {
	# $pname is the name of the 'appli' to retrieve information
	# Rifenv is the global array where are stored all the infos
	global Rifenv
	set Rifenv(dbg) 0
	# Init info with just the name
	#set Rifenv($pname,tclcmd) $pname 
	set Rifenv($pname,name) "$pname"
	set Rifenv($pname,in_names) {}
	set Rifenv($pname,in_types) {}
	set Rifenv($pname,nb_ins) 0 
	set Rifenv($pname,out_names) {} 
	set Rifenv($pname,out_types) {}
	set Rifenv($pname,nb_outs) 0
	set Rifenv($pname,out_channel) stdout
	set Rifenv($pname,in_channel) stdin
	set Rifenv($pname,inprof) ""	
	set Rifenv($pname,outprof) ""	

	set argc [llength $args]
	switch -glob $argc {
		"2" {
			set ins [lindex $args 0]
			set Rifenv($pname,nb_ins) [llength $ins]
			foreach vdecl $ins {
				set vname [lindex $vdecl 0]
				set vtype [lindex $vdecl 1]
				lappend Rifenv($pname,in_names) $vname
				lappend Rifenv($pname,in_types) $vtype
				lappend Rifenv($pname,inprof) "$vname:$vtype"
			}
			set outs [lindex $args 1]
			set Rifenv($pname,nb_outs) [llength $outs]
			foreach vdecl $ins {
				set vname [lindex $vdecl 0]
				set vtype [lindex $vdecl 1]
				lappend Rifenv($pname,out_names) $vname
				lappend Rifenv($pname,out_types) $vtype
				lappend Rifenv($pname,outprof) "$vname:$vtype"
			}
		}
		default {
			luc::Error "rifcreate takes 2 arguments (not $argc)"
		}
	}
	# args is only used to set nb_ins and nb_outs,
	# lists of tcl vars for reading/writing in/out
	# created at luc level with add_in add_out 
	set Rifenv($pname,invars) ""	
	set Rifenv($pname,outvars) ""	
	# $pname becomes a global command for retriving infos
	global $pname
	proc $pname { cmd  args } {
		global Rifenv
		# get the proc name = pname 
		set pname [lindex [info level 0] 0] 
		return [_do_rif_cmd $pname $cmd $args]
	}
}
