#
# state.tcl: current state load/save routines.
#
# ------------------------------------------------
# Mumit Khan <khan@xraylith.wisc.edu>
# Center for X-ray Lithography
# University of Wisconsin-Madison
# 3731 Schneider Dr., Stoughton, WI, 53589
# ------------------------------------------------
#
# Copyright (c) 1994-1996 Mumit Khan
#
#

#
# State package exported interface:
#
#    State:save FILE     -- saves the entire to state to FILE
#    State:load FILE     -- loads the entire state from FILE
#

######################### Initialize options ##############################

# --------------------------------------------------------------------#
#
# State:load: Loads an entire Workspace saved previously by State:save.
#
# --------------------------------------------------------------------#

proc State:load file {
    global gvars gprefs workspace
    set_msg "Loading Workspace from \"$file\" ..."

    #
    # see if file is really SHADOW file or not by checking magic number
    #
    if {![file readable $file] || [catch "open $file r" fid]} {
	set_msg "Error opening Workspace file \"$file\""
	dialog .info_d "Load Error" \
	    "Error opening Workspace file \"$file\"" \
	    {} 0 Dismiss
    } else {
	#
	# read the first 80 bytes (don't try to read a line, since it may
	# be binary file and we might end up reading garbage.
	#
	set line1 [string toupper [read $fid 80]]
	vputs "Read \"$line1\" ([string length $line1] bytes) from $file ..."
	close $fid
	if {[lsearch $line1 SHADOW] == -1} {
	    set do_try [dialog .load "Load Workspace" \
		"This file may not be a SHADOW Workspace file. Try anyway?" \
		questhead 0 \
		{"Load" "Cancel"} \
	    ]
	    if {$do_try == 1} {
		set_msg "Loading Workspace from \"$file\" ... cancelled"
		return
	    }
	}
    }

    #
    # FIXME: delete_src_cmd is the right thing to do, but needs to be
    # changed before we can use (shut off the error message in there).
    #
    catch "delete_src"
    catch "delete_all_oe_cmd"

    set save_edit_src_at_startup $gprefs(edit_src_at_startup)
    set save_edit_added_obj $gprefs(edit_added_obj)

    set gprefs(edit_src_at_startup) 0
    set gprefs(edit_added_obj) 0

    set gvars(state_peek) 0		;# not just peeking here.

    set workspace(username)	""	;#    user who created it
    set workspace(timestamp)	""	;#    when
    set workspace(machine)	""	;#    on what machine

    # annotation support
    set workspace(title)	""	;# set to be filename initially
    set workspace(subtitle)	""	;# set to be filename initially
    set workspace(annotation)	""	;# the actual annotation


    if [catch "source $file" msg] {
	set_msg "Error loading Workspace from $file"
	dialog .info_d "Load Error" \
	    "Error loading Workspace from $file ($msg)" \
	    {} 0 Dismiss
    } else {
	#global workspace
	#dialog .info_d "Load Workspace" \
	#    [build_msg "Load Info:\n" [build_array_string workspace]] \
	#    {} 0 Dismiss
	set_msg "Loading Workspace from \"$file\" ... done."
    }
    set gprefs(edit_src_at_startup) $save_edit_src_at_startup
    set gprefs(edit_added_obj) $save_edit_added_obj
}

proc State:peek {file info} {
    global gvars gprefs workspace
    set_msg "Peeking Workspace in \"$file\" ..."

    array set save_workspace_info [array get workspace]
    set gvars(state_peek) 1			;# just peeking, not loading

    set workspace(username)	""	;#    user who created it
    set workspace(timestamp)	""	;#    when
    set workspace(machine)	""	;#    on what machine

    # annotation support
    set workspace(title)	""	;# set to be filename initially
    set workspace(subtitle)	""	;# set to be filename initially
    set workspace(annotation)	""	;# the actual annotation

    if [catch "source $file" msg] {
	set_msg "Error peeking Workspace in $file"
	dialog .info_d "Load Error" \
 	    "Error peeking Workspace in $file ($msg)" \
	    {} 0 Dismiss
    } else {
	set_msg "Peeking Workspace in \"$file\" ... done."
    }
    set gvars(state_peek) 0

    upvar 1 $info peek_info
    array set peek_info [array get workspace]
    array set workspace [array get save_workspace_info]
}

# ------------------------------ Stubs -------------------------------#
#
# the following are basically stub routines for loading a Workspace.
# This is to add an additional level of redirection to avoid version
# incompatibility.
#

proc State:add_annotation ann {
    upvar 1 $ann ann_array
    global workspace
}

proc State:add_source {} {
    return [add_src]
}

proc State:add_oe oe {
    return [add_oe $oe]
}

proc State:add_scr {oe scr} {
    return [add_scr $oe $scr]
}

proc State:add_source_inspector {inspector id} {
    #
    # inspector id is inferred by the add_*_inspector, so ignored,
    # but kept for future versions.
    # 
    return [add_source_inspector $inspector]
}

proc State:add_oe_inspector {oe inspector id} {
    #
    # inspector id is inferred by the add_*_inspector, so ignored,
    # but kept for future versions.
    # 
    return [add_oe_inspector $oe $inspector]
}

proc State:add_scr_inspector {oe scr inspector id} {
    #
    # inspector id is inferred by the add_*_inspector, so ignored,
    # but kept for future versions.
    # 
    return [add_scr_inspector $oe $scr $inspector]
}

# -------------------------- End Stubs -------------------------------#

#
# get the current date in standard Unix'y format. If running Tcl versions
# >= 7.5, we have clock command. Otherwise see if we have fmt/getclock
# command. Otherwise use Unix date command I guess.
#
proc State:_get_date {} {
    set datestring ""
    if {[info command clock] != ""} {
        set datestring [clock format [clock seconds]]
    } elseif {[info command fmtclock] != ""} {
        set datestring [fmtclock [getclock]]
    } else {
        set datestring [exec date]
    }
    return $datestring
}

proc State:_get_hostname {} {
    set hostname ""
    if {[catch "exec uname -n" hostname]} { 
	if {[catch "exec hostname" hostname]} { 
	    set hostname "unknown"
	}
    }
    return $hostname
}

proc State:_get_beamline {} {
    return [beamline list system]
}

proc State:_save_array {fid a {pattern *}} {
    upvar 1 $a array
    if ![array exists array] {
	error "\"$a\" isn't an array"
    }
    set maxl 0
    foreach name [lsort [array names array $pattern]] {
	if {[string length $name] > $maxl} {
	    set maxl [string length $name]
	}
    }
    set maxl [expr {$maxl + [string length $a] + 2}]
    foreach name [lsort [array names array $pattern]] {
	set nameString [format %s(%s) $a $name]
	puts $fid [format "set %-*s \"%s\"" $maxl $nameString $array($name)]
    }
}

proc State:_save_header fid {
    global gvars env
    puts $fid "# SHADOW GUI version 1.0\n#"
    puts $fid "# Do NOT change the line above. It is used internally to"
    puts $fid "# to recognize SHADOW Workspace saved previously by GUI"
    puts $fid "#\n"
 
    puts $fid "#\n# \[HEADER\]\n#\n"
    puts $fid "global workspace gvars"
    if [catch "set gvars(username)" workspace(username)] {
	set workspace(username) "UNKNOWN"
    }
    set workspace(timestamp) "[State:_get_date]"
    set workspace(machine) "[State:_get_hostname]"
    set workspace(beamline) "[State:_get_beamline]"

    puts $fid "[State:_save_array $fid workspace]"

    State:_save_annotation $fid
    #
    # if we're just peeking at load time, bail out now
    #
    puts $fid "if \$gvars(state_peek) {return}"

    puts $fid "\n"
}

proc State:_save_annotation fid {
    global workspace
    puts $fid "#\n# \[ANNOTATION\]\n#\n"
    puts $fid "proc State:_load_annotation \{\} \{"
    puts $fid "    global workspace"
    puts $fid "    set workspace(title) \{$workspace(title)\}"
    puts $fid "    set workspace(subtitle) \{$workspace(subtitle)\}"
    puts $fid "    set workspace(annotation) \{$workspace(annotation)\}"
    puts $fid "\}"
    puts $fid "State:_load_annotation"
    puts $fid "rename State:_load_annotation \{\}"
    puts $fid "\n"
}

proc State:_save_source_inspectors {fid} {
    global gtool2page gpage2tool
    #
    # check how many inspectors we have for SOURCE
    #
    set inspector_info [beamline get inspectors source]
    set num_inspectors [llength $inspector_info]
    if {$num_inspectors == 0} {return}
    
    for {set ins 1} {$ins <= $num_inspectors} {incr ins} {
        set this_inspector [lindex $inspector_info [expr $ins -1]]
	set name [lindex $this_inspector 0]
	set page $gtool2page($name)
	set id [lindex $this_inspector 1]
	vputs "_save_source:saving source inspector $this_inspector"
	puts $fid "State:add_source_inspector $page $id"
	State:_save_source_inspector_namelist $fid $name $ins
    }
    puts $fid "\n"
}

proc State:_save_source fid {
    puts $fid "#\n# \[SOURCE\]\n#\n"
    #
    # check if source exists first.
    #
    set source [join [beamline get source]]
    if {[llength $source] == 0} {
	puts $fid "#\n# No source defined in this beamline\n#\n"
        return
    }
    #
    # instead of the more generic "beamline add source", should use
    # add_src, since that takes care of a lot of other stuff as well
    # (eg., adding SOURCE to Editbar, etc).
    #
    puts $fid "State:add_source"
    State:_save_source_namelist $fid
    State:_save_source_inspectors $fid
    puts $fid "\n"
}

proc State:_save_screen_inspectors {fid oe scr} {
    global gtool2page gpage2tool
    #
    # check how many inspectors we have for this SCREEN.
    #
    set inspector_info [beamline get inspectors screen $oe $scr]
    set num_inspectors [llength $inspector_info]
    if {$num_inspectors == 0} {return}
    
    for {set ins 1} {$ins <= $num_inspectors} {incr ins} {
        set this_inspector [lindex $inspector_info [expr $ins -1]]
	set name [lindex $this_inspector 0]
	set page $gtool2page($name)
	set id [lindex $this_inspector 1]
	vputs "_save_oe:saving source inspector $this_inspector"
	puts $fid "State:add_scr_inspector $oe $scr $page $id"
	State:_save_scr_inspector_namelist $fid $name $oe $scr $ins
    }
    puts $fid "\n"
}

proc State:_save_screen {fid oe scr} {
    puts $fid "State:add_scr $oe $scr"
    State:_save_scr_namelist $fid $oe $scr
    puts $fid "\n"
}

proc State:_save_screens {fid oe} {
    #
    # check how many screens we have for this OE.
    #
    set num_screens [lindex [beamline get oe $oe] 0]
    if {$num_screens == 0} {return}
    
    for {set scr 1} {$scr <= $num_screens} {incr scr} {
	State:_save_screen $fid $oe $scr
	State:_save_screen_inspectors $fid $oe $scr
    }
}

proc State:_save_oe_inspectors {fid oe} {
    global gtool2page gpage2tool
    #
    # check how many inspectors we have for this OE.
    #
    set inspector_info [beamline get inspectors oe $oe]
    set num_inspectors [llength $inspector_info]
    if {$num_inspectors == 0} {return}
    
    for {set ins 1} {$ins <= $num_inspectors} {incr ins} {
        set this_inspector [lindex $inspector_info [expr $ins -1]]
	set name [lindex $this_inspector 0]
	set page $gtool2page($name)
	set id [lindex $this_inspector 1]
	vputs "_save_oe:saving source inspector $this_inspector"
	puts $fid "State:add_oe_inspector $oe $page $id"
	State:_save_oe_inspector_namelist $fid $name $oe $ins
    }
    puts $fid "\n"
}

proc State:_save_oe {fid oe} {
    puts $fid "#\n# \[OE $oe\]\n#\n"
    #
    # check how many OEs we have first.
    #
    set oe_info [beamline get oe $oe]
    if {[llength $oe_info] == 0} {
	puts $fid "#\n# OE $oe not defined in this beamline\n#\n"
        return
    }
    #
    # instead of the more generic "beamline add oe <oe#>", should use
    # add_oe <oe#>, since that takes care of a lot of other stuff as well
    # (eg., adding OE <oe#> to Editbar, etc).
    #
    puts $fid "State:add_oe $oe"
    State:_save_oe_namelist $fid $oe

    #
    # Note that we don't care that the Screens are not first-class SHADOW
    # objects (ie., they're part of OEs), but we treat them as first class
    # and store them separately. Only when Screens are run are the OEs and
    # Screens sych'd.
    #
    State:_save_screens $fid $oe
    State:_save_oe_inspectors $fid $oe
    puts $fid "\n"
}

proc State:_save_system fid {
    puts $fid "#\n# \[SYSTEM\]\n#\n"
    #
    # check if source exists first.
    #
    set system_info [beamline list system]
    set num_oe [expr [llength $system_info] - 2]
    if {$num_oe == 0} {
	puts $fid "#\n# No OEs defined in this beamline\n#\n"
        return
    }
    for {set oe 1} {$oe <= $num_oe} {incr oe} {
        State:_save_oe $fid $oe
    }
    puts $fid "\n"
}

# --------------------------------------------------------------------#
#
# State:save: Saves an entire Workspace that can be reloaded by
# State:load
#
# --------------------------------------------------------------------#

proc State:save file { 
    global env gvars gprefs workspace
    set_msg "Saving Workspace to $file ..."

    #
    # check file permissions and such
    #
    if [catch "open $file w" fid] {
        set_msg "State:save: Error opening save file \"$file\""
	dialog .info_d "Save Error" \
	    "Error opening save file \"$file\" ($fid)" \
	    {} 0 Dismiss
	return
    } else {
        set workspace(filename) "$file"
    }

    State:_save_header $fid
    State:_save_source $fid
    State:_save_system $fid

    close $fid
    set_msg "Saving Workspace to $file ... done."
    return
}

#
# Example args: $fid SRC _load_source "source"
# Example args: $fid OE _load_oe1 "oe 1"
# Example args: $fid SCR _load_scr1_2 "scr 2 1"
#
proc State:_save_tool_namelist {fid tool load_proc get_set_cmd} {
    vputs "get_set_cmd = $get_set_cmd"
    set variables [tool variables $tool]
    puts $fid "proc State:${load_proc} \{\} \{"
    puts $fid "    set nml \{"
    foreach var $variables {
	set value [eval beamline vget $get_set_cmd $var]
	puts $fid "        {$var \"$value\"}"
    }
    puts $fid "    \}"
    puts $fid "    foreach item \$nml \{"
    puts $fid {        set var [lindex $item 0]}
    puts $fid {        set value [lindex $item 1]}
    puts $fid "        beamline vset [join $get_set_cmd] \$var \$value"
    puts $fid "    \}"
    puts $fid "\}"
    puts $fid "State:$load_proc"
    puts $fid "rename State:$load_proc \{\}"
}

#
# Simply calls the more generic form State:_save_tool_namelist
#
proc State:_save_source_namelist fid {
    return [State:_save_tool_namelist $fid SRC _add_source source]
}

#
# Simply calls the more generic form State:_save_tool_namelist
#
proc State:_save_oe_namelist {fid oe} {
    return [State:_save_tool_namelist $fid OE _add_oe${oe} "oe $oe"]
}

#
# Simply calls the more generic form State:_save_tool_namelist
#
proc State:_save_scr_namelist {fid oe scr} {
    return [State:_save_tool_namelist $fid SCR \
        _add_scr${scr}_of_${oe} "scr $oe $scr"]
}

#
# Simply calls the more generic form State:_save_tool_namelist
#
proc State:_save_source_inspector_namelist {fid tool id} {
    return [State:_save_tool_namelist $fid $tool \
         _add_src_ins${id} \
        "inspector source $id"]
}

#
# Simply calls the more generic form State:_save_tool_namelist
#
proc State:_save_oe_inspector_namelist {fid tool oe id} {
    return [State:_save_tool_namelist $fid $tool \
        _add_oe${oe}_ins${id} \
        "inspector oe $oe $id"]
}

#
# Simply calls the more generic form State:_save_tool_namelist
#
proc State:_save_scr_inspector_namelist {fid tool oe scr id} {
    return [State:_save_tool_namelist $fid $tool \
        _add_scr${scr}_of${oe}_ins${id} \
	"inspector scr $oe $scr $id"]
}

