#
# Copyright 2004-2008 University of Zagreb, Croatia.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#
# This work was supported in part by the Croatian Ministry of Science
# and Technology through the research contract #IP-2003-143.
#


#****f* exec.tcl/nexec
# NAME
#   nexec -- execute program
# SYNOPSIS
#   set result [nexec $args]
# FUNCTION
#   Executes the sting given in args variable. The sting is not executed
#   if IMUNES is running in editor only mode. Execution of a string can
#   be local or remote. If socket can not be opened in try of a remote
#   execution, mode is switched to editor only mode.
# INPUTS
#   * args -- the string that should be executed localy or remotely.
# RESULT
#   * result -- the standard output of the executed string.
#****
proc nexec { node args } {
    global exec_servers

# debug output of every command
#puts "nexec($node): $args"
#if {[lsearch $args ngctl] != -1 } {
#	puts "XXX $args"
#    set fileId [open "nexec.log" a]
#    puts $fileId $args
#    close $fileId
#}
    # safely exec the given command, printing errors to stdout
    if { $node == "localnode" } { ;# local execution
	if { [ catch {eval exec $args} e ] } {
	    if { $e == "child process exited abnormally" } { return };# ignore
	    puts "error executing: exec $args ($e)"
	}
	return $e
    } else {
	puts "error: nexec called with node=$node"
    }
}


proc acquireOperModeLock { mode } {
    global setOperMode_lock oper_mode

    if { ![info exists setOperMode_lock] } { set setOperMode_lock 0 }
    if { $oper_mode == "exec" } {
	# user somehow pressed start while we were still shutting down...
	if { $mode == "exec" } {
	    set choice [tk_messageBox -type yesno -default no -icon warning \
			-message "You have selected to start the session while the previous one is still shutting down. Are you sure you want to interrupt the shutdown? (not recommended)"]
	    if { $choice == "no" } {
		set activetool select
		return; # return and allow previous setOperMode to finish...
	    }
	# check if user pressed stop while we were starting up...
	} elseif { $setOperMode_lock } { ;# mode == "edit"
	    set choice [tk_messageBox -type yesno -default no -icon warning \
			-message "You are trying to stop the session while it is still starting. Are you sure you want to interrupt the startup? (not recommeded)"]
	    if { $choice == "no" } {
		set activetool select
		return; # return and allow previous setOperMode to finish...
	    }
	}
    }

    set setOperMode_lock 1
}

proc releaseOperModeLock { } {
    global setOperMode_lock
    set setOperMode_lock 0
}

proc checkRJ45s {} {
    global systype node_list g_prefs

    if { [lindex $systype 0] == "Linux" } {
	if { [file isdirectory /sys/class/net] } {
	    set extifcs [nexec localnode ls /sys/class/net]
	} else {
	    set extifcs [nexec localnode /sbin/ifconfig -a -s | tail -n +2 | awk "{ print \$1 }" | xargs]
	}
        set extifcs \
           [lreplace $extifcs [lsearch $extifcs lo] [lsearch $extifcs lo]]
    } else {
        set extifcs [nexec localnode ifconfig -l]
        set extifcs \
           [lreplace $extifcs [lsearch $extifcs lo0] [lsearch $extifcs lo0]]
    }

    foreach node $node_list {
	if { [getNodeLocation $node] != "" } { continue }
        if { [nodeType $node] == "rj45" } {
        set i [lsearch $extifcs [getNodeName $node]]
        if { $i >= 0 } { continue }
        set msg "Error: external interface \"[getNodeName $node]\""
        set msg "$msg does not exist. Press OK to continue with RJ45s"
        set msg "$msg disabled. NOTE: this setting can be re-enabled"
        set msg "$msg through Session > Options..."
        set choice [tk_messageBox -type okcancel -icon error -message $msg]
        if { $choice == "cancel" } {
	    return -1
        }
	setSessionOptions "" "enablerj45=0"
        break;
        }
    }
    return 0
}

proc drawToolbar { mode } {
    global CORE_DATA_DIR
    global activetoolp defSelectionColor
    set activetoolp ""
    markerOptions off

    #
    # edit mode button bar
    #
    set buttons [list start link]
    foreach b $buttons {
        if { $mode == "exec"} { destroy .left.$b } else {
	    # add buttons when in edit mode
	    set imgf "$CORE_DATA_DIR/icons/tiny/$b.gif"
	    set image [image create photo -file $imgf]
	    catch {
	    radiobutton .left.$b -indicatoron 0 \
		-variable activetool -value $b -selectcolor $defSelectionColor \
		-width 32 -height 32 -image $image \
		-command "popupMenuChoose \"\" $b $imgf"
	        leftToolTip $b .left
	    	pack .left.$b -side top
	    }
	}
    }
    # popup toolbar buttons have submenus
    set buttons {routers hubs bgobjs}
    foreach b $buttons {
        if { $mode == "exec"} { destroy .left.$b } else {
	    # create buttons for parent items
	    set menubuttons { }
	    if { $b == "routers" } {
	    	set menubuttons [getNodeTypeNames]
	    } elseif { $b == "hubs" } {
	        set menubuttons { hub lanswitch wlan rj45 tunnel }
	    } elseif { $b == "bgobjs" } {
	    	set menubuttons { marker oval rectangle text }
	    }
	    set firstb [lindex $menubuttons 0]
	    set firstfn "$CORE_DATA_DIR/icons/tiny/$firstb.gif"
	    set image [image create photo -file $firstfn]
    	    $image read "$CORE_DATA_DIR/icons/tiny/arrow.gif" -to 27 22
	    # create the parent menu
	    menubutton .left.$b -indicatoron 0 -direction right \
	    			-width 32 -height 32 -image $image \
				-padx 0 -pady 0 -relief raised \
				-menu .left.${b}.menu
	    set buttonmenu [menu .left.${b}.menu \
	    			-activebackground $defSelectionColor \
	    			-borderwidth 1 -tearoff 0]
	    # create the child menutbuttons
	    drawToolbarSubmenu $b $menubuttons
	    # tooltips for parent and submenu items
	    leftToolTip $b .left
	    bind $buttonmenu <<MenuSelect>> {leftToolTipSubMenu %W}
 	    bind $buttonmenu <Leave> {
		set newlen [expr {[string length %W] - 6}]
		set w [string range %W 0 $newlen]
		destroy ${w}.balloon
	    }
	    # set submenu tooltips for user-defined types to type name
	    setLeftTooltips $b $menubuttons
	    pack .left.$b -side top
	}
    }

    #
    # Exec mode button bar
    #
    if { "$mode" == "edit" } {
	.left.start configure -command "startStopButton exec"
    }
    foreach b {stop observe plot marker twonode run } {
	if { "$mode" != "exec" } { destroy .left.$b } else {
	    set cmd ""
	    set fn "$CORE_DATA_DIR/icons/tiny/$b.gif"
	    set image [image create photo -file $fn]
	    if { $b == "stop" } {
		set cmd "startStopButton edit"
	    } elseif { $b == "observe" } {
	    	set cmd "popupObserverWidgets"
	    } elseif { $b == "marker" } {
		set cmd "markerOptions on"
	    } elseif { $b == "mobility" } {
		set cmd "popupMobilityDialog"
	    } elseif { $b == "twonode" } {
		set cmd "popupTwoNodeDialog"
	    } elseif { $b == "run" } {
		set cmd "popupRunDialog"
	    }
	    # add more cmds here
	    radiobutton .left.$b -indicatoron 0 \
		-variable activetool -value $b -command $cmd \
		-selectcolor [.left cget -bg] \
		-width 32 -height 32 -activebackground gray -image $image
	    leftToolTip $b .left
	    pack .left.$b -side top
	}
    }
    # turn off any existing tooltip
    balloon .left ""
}

proc drawToolbarSubmenu { b menubuttons } {
    global CORE_DATA_DIR
    set buttonmenu .left.${b}.menu

    if { ![winfo exists $buttonmenu] } {
	return ;# this may occur in exec mode
    }

    $buttonmenu delete 0 end

    foreach menubutton $menubuttons {
	if { $b == "routers" } {
	    set imgf [getNodeTypeImage $menubutton tiny]
	} else {
	    set imgf "$CORE_DATA_DIR/icons/tiny/${menubutton}.gif"
	}
	if { ![file exists $imgf] } { ;# pop custom filename from list
	    puts "**warning: missing icon $imgf"
	    continue
	}
	set img [createImageButton $imgf 0]
	$buttonmenu add command -image $img -columnbreak 1  \
		-command "popupMenuChoose $b $menubutton $imgf"
    }
    # add an edit button to the end of the row
    if { $b == "routers" } {
	set imgf "$CORE_DATA_DIR/icons/normal/document-properties.gif"
	set img [createImageButton $imgf 0]
	$buttonmenu add command -image $img -columnbreak 1 \
		-command "popupNodesConfig"
    }
}

proc setSessionStartStopMenu { mode } {
    if { $mode == "exec" } {
	catch   {.menubar.session entryconfigure "Start" \
		-label "Stop" -command "startStopButton edit"}
    } else {
	catch  {.menubar.session entryconfigure "Stop" \
	   	-label "Start" -command "startStopButton exec"}
    }
}

proc updateMenus { mode } {
    set s "normal"
    if { $mode == "exec" } {
	set s "disabled"
    }
    .menubar.tools entryconfigure "Auto rearrange all" -state $s
    .menubar.tools entryconfigure "Auto rearrange selected" -state $s
    .menubar.session entryconfigure "Node types..." -state $s
    .menubar.session entryconfigure "Emulation servers..." -state $s

    if { $s == "normal" } { set s "" }
    updateUndoRedoMenu $s
}

proc startStopButton { mode } {
    global activetool
    #
    # Prepare API channel for emulation. Do this before any GUI changes
    # so that connect failures leave the GUI in edit mode.
    setSystype
    global systype
    set emul_sock 0
    if { $mode == "exec" } {
	set msg "The CORE daemon must be running and your kernel must support"
	set msg "$msg virtualization for the emulation to start."
    } elseif { $mode == "edit" } {
	set msg "Communication with daemon was lost."
    } else {
	puts "unsupported mode: $mode"
	return
    }

    set plugin [lindex [getEmulPlugin "*"] 0]
    set emul_sock [pluginConnect $plugin connect true]

    if { $emul_sock == "" || $emul_sock == -1 } {
	tk_messageBox -type ok -icon warning -message $msg
	releaseOperModeLock
	set activetool select
	return
    }
    setOperMode $mode
}

#****f* exec.tcl/setOperMode
# NAME
#   setOperMode -- set operating mode
# SYNOPSIS
#   setOperMode $mode
# FUNCTION
#   Sets imunes operating mode to the value of the parameter mode.
#   The mode can be set only to edit or exec.
#   When changing the mode to exec all the emulation interfaces are
#   checked (if they are nonexistent the message is displayed, and
#   mode is not changed), all the required buttons are disabled
#  (except the simulation/Terminate button, that is enabled) and
#   procedure deployCfg is called.
#   When changing the mode to edit, all required buttons are enabled
#   (except for simulation/Terminate button that is disabled) and
#   procedure cleanupCfg is called.
# INPUTS
#   * mode -- the new operating mode. Can be edit or exec.
#****
proc setOperMode { mode { type "" } } {
    global oper_mode activetool
    global undolevel redolevel
    global g_prefs

    # special handling when experiment is already running
    acquireOperModeLock $mode

    # Verify that links to external interfaces are properly configured
    if { $mode == "exec" && [getSessionOption enablerj45 1]==1 } {
	if { [checkRJ45s] < 0 } {
	    releaseOperModeLock
	    set activetool select
	    return
	}
    }

    # start/stop menu item
    setSessionStartStopMenu $mode

    #
    # take care of GUI changes and bindings when switching modes
    #
    drawToolbar $mode
    if { $mode == "edit" } { ;# these are the default bindings
	.c bind node <Double-1> "popupConfigDialog .c"
	.c bind nodelabel <Double-1> "popupConfigDialog .c"
    } else { ;# double-click to open shell
	.c bind node <Double-1> "button3node .c %x %y shift"
	.c bind nodelabel <Double-1> "button3node .c %x %y shift"
    }
    set activetool select
    .left.select configure -state active
    updateMenus $mode
    blinkCEL "stop"

    #
    # Start/stop the emulation
    #
    ### start button is pressed
    if { "$mode" == "exec" } {
	rearrange_off
	set oper_mode exec
	resetAllNodeCoords save
	clearExceptions "" ""
	throwCEL true

	# Bind left mouse click to displaying the CPU usage in
	# a graph format
	bind .bottom.cpu_load <1> {manageCPUwindow %X %Y 1}

	monitor_loop
        set plugin [lindex [getEmulPlugin "*"] 0]
        set emul_sock [pluginConnect $plugin connect false]
	if {$type != "connect"} {
	    deployCfgAPI $emul_sock
	}
	widget_loop
	mobility_script_loop
    ### stop button is pressed
    } else {
	if {$oper_mode != "edit"} {
	    set t_start [clock seconds]
	    shutdownSession
	    statgraph off 0
	    set t_elapsed [expr {[clock seconds] - $t_start}]
	    statline "Cleanup completed in $t_elapsed seconds."
	}
	clearLinkHighlights
	catch { destroy .popup }
	clearWlanLinks ""
	widgets_stop
	set oper_mode edit

	# Bind left mouse click to clearing the CPU graph
	bind .bottom.cpu_load <1> {manageCPUwindow %X %Y 0}
	manageCPUwindow %X %Y 0
    }
    .c config -cursor left_ptr
    releaseOperModeLock
}


#****f* exec.tcl/statline
# NAME
#   statline -- status line
# SYNOPSIS
#   statline $line
# FUNCTION
#   Sets the string of the status line. If the execution mode is
#   set to batch the line is just printed on the standard output.
# INPUTS
#   * line -- line to be displayed
#****
proc statline {line} {
    global execMode

    if {$execMode == "batch" || $execMode == "addons"} {
	puts $line
    } else {
	.bottom.textbox config -text "$line"
	animateCursor
    }
}

proc getNextMac {} {
    global mac_byte4 mac_byte5

    set a [format %.2x $mac_byte4]
    set b [format %.2x $mac_byte5]
    incr mac_byte5
    if { $mac_byte5 >= 255 } {
	set mac_byte5 0
	incr mac_byte4
    }
    return "00:00:00:aa:$a:$b"
}


#****f* exec.tcl/monitor_loop
# NAME
#   monitor_loop -- monitor loop
# SYNOPSIS
#   monitor_loop
# FUNCTION
#   Calculates the usage of cpu, mbuffers and mbuf clusters.
#   The results are displayed in status line and updated
#   every two seconds.
#****
proc monitor_loop {} {
    global oper_mode systype
    global server_cpuusage
    global exec_servers


    if {$oper_mode != "exec"} {
	.bottom.cpu_load config -text ""
	.bottom.mbuf config -text ""
	return
    }

    if { [lindex $systype 0] == "Linux" } {
	set cpuusage [getCPUUsage]

	#TODO: get the cpu usage on all the assigned server
	# store usage history for each server stored in an array list
	set assigned_servers [getAssignedRemoteServers]
	for {set i 0} {$i <= [llength $assigned_servers]} {incr i} {
	    if {$i == [llength $assigned_servers]} {
		# localhost
		set ip [getMyIP]
		set cpuusageforserver [lindex $cpuusage 0]
	    } else {
		set server [lindex $assigned_servers $i]
                set srv [array get exec_servers $server]
                if { $srv == "" } { continue }
                set ip [lindex $srv 0]
		# TODO: receive CPU usage from other servers
		set cpuusageforserver 0
	    }

	    # append the latest cpu value to the end of list and
	    # only keep and display the last 20 values for each server
	    set server_cpuusage($ip) [lappend server_cpuusage($ip) $cpuusageforserver]
	    if { [llength $server_cpuusage($ip)] > 20 } {
		set server_cpuusage($ip) [lreplace $server_cpuusage($ip) 0 0]
	    }
	}


	#plot the usage data if cpu windows already opened
	# for all servers
	if { [winfo exists .cpu]} {
	    plotCPUusage
	}

	set cputxt "CPU [lindex $cpuusage 0]% ("
	set cpuusage [lreplace $cpuusage 0 0]
	for { set n 0 } { $n < [llength $cpuusage] } { incr n } {
	    if { $n > 0 } { set cputxt "${cputxt}/" }
	    set cputxt "${cputxt}[lindex $cpuusage $n]"
	}
	set cputxt "$cputxt)"
	set cpulen [expr {[string length $cputxt] - 2}]
	set labellen [.bottom.cpu_load cget -width]
	if { $cpulen < $labellen } { set cpulen $labellen }
	set stats [nexec localnode vmstat | tail -n 1 ]
	set mem_free [lindex $stats 3]
	set mem_free_mb [expr { $mem_free / 1024 }]
        .bottom.cpu_load config -text $cputxt -width $cpulen
	.bottom.mbuf config -text " ${mem_free_mb}M free"
	after 2000 { monitor_loop }
	return
    }

    set defaultname "."
	set cpun 4

    # CPU usage from `vimage -l`
    set vimagetext [nexec localnode vimage -l $defaultname | xargs]
    set vimagelist [split $vimagetext :]
    set cpuline [lindex $vimagelist $cpun]
    set cpu [lindex [split $cpuline %] 0]

    .bottom.cpu_load config -text "CPU $cpu%"

    # mbuf usage from `netstat -m`
    set nstout [split [nexec localnode netstat -m] ]
    set mbufline [split [lindex $nstout 0] /]
    set mbufs [lindex $mbufline 0]
    set nmbufs [lindex [split [lindex $mbufline 2] " "] 0]
    set mbufp [expr $mbufs * 100 / $nmbufs]
    .bottom.mbuf config -text "mbuf $mbufp%"

    after 2000 { monitor_loop }
}


#****f* exec.tcl/execSetLinkParams
# NAME
#   execSetLinkParams -- in exec mode set link parameters
# SYNOPSIS
#   execSetLinkParams $eid $link
# FUNCTION
#   Sets the link parameters during execution.
#   All the parameters are set at the same time.
# INPUTS
#   eid -- experiment id
#   link -- link id
#****
proc execSetLinkParams { eid link } {
    set lnode1 [lindex [linkPeers $link] 0]
    set sock [lindex [getEmulPlugin $lnode1] 2]
    sendLinkMessage $sock $link modify
    return
}


# command executed when popup menu item is selected
proc popupMenuChoose { parent b imgf } {
    global activetool activetool_prev activetool_prev_imgf activetoolp
    #puts "popupMenuChoose $parent -> $b ($activetoolp -> $activetool)"

    # deselect previous item
    if { $activetoolp != "" } {
	set img [createImageButton $activetool_prev_imgf 1]
	.left.$activetoolp configure -image $img -relief raised
    }
    if {$activetool_prev == "marker"} { markerOptions off }

    # change the active item; we need activetool_prev b/c of radio button
    set activetoolp $parent
    set activetool $b
    set activetool_prev $b	;# previously-selected activetool
    set activetool_prev_imgf $imgf ;# previously-selected button image file

    # hint for topogen
    global g_last_selected_node_type
    if { $activetoolp == "routers" || $activetoolp == "hubs" } {
	set g_last_selected_node_type [list $parent $b $imgf]
    }

    # select a new button
    if { $parent != "" } {
	set img [createImageButton $imgf 2]
	.left.$parent configure -image $img -relief sunken
    }

    if {$activetool == "marker"} { markerOptions on }

    # status message
    .bottom.textbox config -text "$b tool selected"

}

#
# Boeing - create an image for use on the button toolbar
# style 0 = no arrow, 1 = arrow, 2 = arrow + select color
proc createImageButton { imgf style } {
    global CORE_DATA_DIR defSelectionColor

    if { [catch {set img [image create photo -file $imgf] } e] } {
	puts "warning: error opening button image $imgf ($e)"
	set img [image create photo] ;# blank button
    }
    # add an arrow to the button
    if { $style > 0 } {
	$img read "$CORE_DATA_DIR/icons/tiny/arrow.gif" -to 27 22
	if { $style == 2 } { ;# highlight the button
	    set img2 [image create photo]
	    $img2 put [$img data -background $defSelectionColor]
	    set img $img2
	}
    }
    return $img

}

# Boeing: status bar graph
proc statgraph { cmd n } {
    global statgraph_max statgraph_current tk_patchLevel execMode

    if { $execMode != "interactive" || ![info exists tk_patchLevel] } {
	return
    }

    switch -exact $cmd {
	on {
		if { $n == 0 } { return } ;# Boeing: prevent div by 0
		set statgraph_max $n
		set statgraph_current 0
		label .bottom.status -relief sunken -bd 1 -anchor w -width 2 \
			-background green -justify center
		label .bottom.status2 -relief sunken -bd 1 -anchor w -width 12
		pack .bottom.status .bottom.status2  -side left \
			-before .bottom.textbox
	}
	off {
		set statgraph_max 0
		set statgraph_current 0
		destroy .bottom.status
		destroy .bottom.status2
	}
	inc {
		# Boeing: prevent div by 0
		if { $n == 0 || $statgraph_max == 0 } { return }
		incr statgraph_current $n
		set p [expr $statgraph_current.0 / $statgraph_max.0]
		set width [expr int($p * 15)]
		.bottom.status config -width $width
    		.bottom.status config -text "  ([expr int($p*100)]%)"
		.bottom.status2 config -width [expr 15-$width]
	}
    }
}

proc popupConnectMessage { dst } {
    global CORE_DATA_DIR execMode

    if { $execMode != "interactive" } { return }

    set wi .popupConnectMessage
    catch { destroy $wi }
    toplevel $wi
    wm transient $wi .
    wm title $wi "Connecting"

    set i [image create photo -file $CORE_DATA_DIR/icons/normal/lanswitch.gif]
    frame $wi.txt
    label $wi.txt.ico -image $i
    label $wi.txt.label1 -text "Please wait, connecting to $dst..."
    pack $wi.txt.ico $wi.txt.label1 -side left -padx 4 -pady 4
    pack $wi.txt -side top

    if { [exec id -u] != 0 } {
	frame $wi.txt2
	set longtext "(Note: remote emulation may have been enabled\n"
	set longtext "$longtext because you are not running as root.)"
	label $wi.txt2.label2 -text $longtext
	pack $wi.txt2.label2 -side left -padx 4 -pady 4
	pack $wi.txt2 -side bottom
    }

    after 100 { catch { grab .popupConnectMessage } }
    update
}

proc popdownConnectMessage { } {
	catch { destroy .popupConnectMessage }
}

proc updateConnectMessage { dst } {
    set wi .popupConnectMessage
    catch {
    $wi.txt.label1 configure -text "Please wait, connecting to $dst..."
    }
}

# helper to return the list of assigned remote execution servers
proc getAssignedRemoteServers {} {
    global node_list
    set servers {}
    foreach node $node_list {
	set server [getNodeLocation $node]
	if { $server == "" } { continue }
	if { [lsearch -exact $servers $server] < 0 } {
	    lappend servers $server
	}
    }
    return $servers
}

# generate a separate window for displaying CPU
proc manageCPUwindow {xpos ypos start} {

    global exec_servers
    global server_cpuusage

    if {$start == 1} {
	if { ![winfo exists .cpu]} {
            toplevel .cpu
	    wm geometry .cpu 200x210+$xpos+$ypos
	    wm resizable .cpu 0 0
	    wm title .cpu "CPU Usage"
	    canvas .cpu.graph -width 200 -height 210
	    pack .cpu.graph
	}
    } else {
	if { [winfo exists .cpu]} {
	    destroy .cpu
	    set assigned_servers [getAssignedRemoteServers]

	    for {set i 0} {$i <= [llength $assigned_servers]} {incr i} {
		if {$i == [llength $assigned_servers]} {
		    set ip [getMyIP]
		} else {
		    set server [lindex $assigned_servers $i]
		    set srv [array get exec_servers $server]
		    if { $srv == "" } { continue }
		    set ip [lindex $srv 0]
		}
		set server_cpuusage($ip) [lreplace $server_cpuusage($ip) 0 end]
	    }
	}
    }
}

proc getMyIP { } {
    variable myIP
    if { ![info exists myIP] } {
	if { [catch {set theServer [socket -server none \
					   -myaddr [info hostname] 0]} ] } {
	    set myIP "127.0.0.1"
	} else {
	    set myIP [lindex [fconfigure $theServer -sockname] 0]
	    close $theServer
	}
    }
    return $myIP
}

# display all values stored in cpu usage history for each server
proc plotCPUusage { } {
    global cpu_palettes
    global exec_servers
    global server_cpuusage

    .cpu.graph delete "all"
    .cpu.graph create line 0 100 200 100 -width 2
    .cpu.graph create line 0 80 200 80 -width 1
    .cpu.graph create line 0 60 200 60 -width 1
    .cpu.graph create line 0 40 200 40 -width 1
    .cpu.graph create line 0 20 200 20 -width 1
    .cpu.graph create line 0 0 200 0 -width 1

    .cpu.graph create line 40 0 40 100 -width 1
    .cpu.graph create line 80 0 80 100 -width 1
    .cpu.graph create line 120 0 120 100 -width 1
    .cpu.graph create line 160 0 160 100 -width 1
    .cpu.graph create line 200 0 200 100 -width 1

    # for each server create a plot of cpu usage
    set assigned_servers [getAssignedRemoteServers]
    for {set i 0} {$i <= [llength $assigned_servers]} {incr i} {
	if {$i == [llength $assigned_servers]} {
	    set ip [getMyIP]
	} else {
	    set server [lindex $assigned_servers $i]
            set srv [array get exec_servers $server]
            if { $srv == "" } { continue }
            set ip [lindex $srv 0]
	}

	#need to add multiple cpuusgaehistory (array)
	for { set n 1 } { $n < [llength $server_cpuusage($ip)] } { incr n } {
	    set prevn [expr {$n - 1}]
	    set x1 [expr {$prevn * 10}]
	    set y1 [expr {100 - [lindex $server_cpuusage($ip) $prevn]}]
	    set x2 [expr {$n * 10}]
	    set y2 [expr {100 - [lindex $server_cpuusage($ip) $n]}]
	    if {$i < [llength $cpu_palettes]} {
		    .cpu.graph create line $x1 $y1 $x2 $y2 -fill [lindex $cpu_palettes $i]
		} else {
		    .cpu.graph create line $x1 $y1 $x2 $y2 -fill [lindex $cpu_palettes end]

		}
	    #debug
	    #puts " cpu $x1 $y1 $x2 $y2"
	}

	#for each server create a legend (limited to 8)
	set legendtext $ip
	append legendtext " " [lindex $server_cpuusage($ip) end] "%"

	set legendy [expr {($i * 10) + 120}]
	set legendx 10
	if {$i < [llength $cpu_palettes]} {
	    .cpu.graph create rectangle $legendx $legendy \
		[expr {$legendx + 8}] [expr {$legendy + 8}] \
		-fill [lindex $cpu_palettes $i]
	    .cpu.graph create text [expr {$legendx + 15}] [expr {$legendy + 4}]\
		-text $legendtext -fill [lindex $cpu_palettes $i] \
		-anchor w -justify left
	} else {
	    .cpu.graph create rectangle $legendx $legendy \
		[expr {$legendx + 8}] [expr {$legendy + 8}] \
		-fill [lindex $cpu_palettes end]
	    .cpu.graph create text [expr {$legendx + 15}] [expr {$legendy + 4}]\
		-text $legendtext -fill [lindex $cpu_palettes end] \
		-anchor w -justify left

	}

    }
}