
# This code creates the user interface for the mesh editor.
#
# Copyright (c) 1997 Pacific Marketing and Consulting, Inc,
#       and ICEM Systems, GmbH.  All rights reserved.
# Author:  Wayne A. Christopher, wayne@icemcfd.com

proc application_wait {} {
    global please_wait_font gray_background

    toplevel .wait
    if { [info exists please_wait_font] } {
	label .wait.l -text "Please wait ..." \
	    -font $please_wait_font -fg blue -padx 10 -pady 10
    } else {
	label .wait.l -text "Please wait ..." -fg blue -padx 10 -pady 10 
    }
    if ![info exists gray_background] { .wait.l configure -bg bisque }
    pack .wait.l
    wm geometry .wait +300+300
    update
    return .wait
}

# This function optionally runs a command on another system.  This returns
# the output or if logfile was given it returns the pid

proc application_exec {cmd {logfile ""} {quiet 0} {bg_if_logfile 1}} {
    global options is_win warned_once_no_remote env application_is_remote

    set application_is_remote 0

    set rh ""
    if [info exists options(remote_host)] { set rh $options(remote_host) }
    if {$is_win && ($rh != "")} {
	if ![info exists warned_once_no_remote_windows] {
	    mess "Warning: remote host is ignored on Windows.\n" red
	    set warned_once_no_remote 1
	}
	set rh ""
    }
    if {$rh != ""} {
	foreach rp {/usr/bin/rsh /usr/ucb/rsh /usr/bsd/rsh /bin/rsh} {
	    if [file exists $rp] { set rsh $rp; break }
	}
	if ![info exists rsh] {
	    if ![info exists warned_once_no_remote] {
		mess "Warning: can't find rsh program: remote execution\
			not possible\n" red
		set warned_once_no_remote 1
	    }
	    set rh ""
	}
    }

    set cmdfile [cmd_tempfile medcmd .tmp]

    if {$rh == ""} {
	if {$logfile == ""} {
	    if !$quiet {
		mess "Running: $cmd\n"
	    }
	    set ret [eval exec $cmd]
	    if !$quiet {
		mess "Done.\n"
	    }
	    return $ret
	} else {
	    cmd_rm $logfile
	    if !$quiet {
		mess "Running: $cmd >& $logfile\n"
	    }
	    if $bg_if_logfile {
		if $is_win {
		    set pid [eval exec $cmd >& $logfile &]
		} else {
		    set fp [open $cmdfile w]
		    puts $fp "\#!/bin/csh -f"
		    puts $fp $cmd
		    puts $fp "set stat = \$status"
		    puts $fp "echo status \$stat"
		    puts $fp "exit \$stat"
		    close $fp
		    cmd_chmod 0755 $cmdfile
		    set pid [exec $cmdfile >& $logfile &]
		}
		return [list $pid $cmdfile]
	    } else {
		return [eval exec $cmd >& $logfile]
	    }
	}
    } else {
	set user [cmd_whoami]
	if {$options(remote_user) != ""} { set user $options(remote_user) }
	set path [pwd]
	if {$options(remote_path) != ""} { set path $options(remote_path) }
	set acn $env(ICEM_ACN)
	if {$options(remote_acn) != ""} { set acn $options(remote_acn) }
	set disp $env(DISPLAY)
	if [string match :* $disp] { set disp [cmd_uname_n]$disp }
	if [string match localhost:* $disp] { set disp [cmd_uname_n]:0 }

	if {$options(remote_sh) == "csh"} {
	    set evars "setenv DISPLAY $disp; setenv ICEM_ACN $acn"
	    set outerr1 {>&}; set outerr2 ""
	} else {
	    set evars "DISPLAY=$disp; export DISPLAY;\
		ICEM_ACN=$acn; export ICEM_ACN"
	    set outerr1 >; set outerr2 {2>&1}
	}

	set application_is_remote 1

	if {$logfile == ""} {
	    set cmd2 "$rsh $rh -l $user \"cd $path; $evars; $cmd\""
	    puts $cmd2
	    if !$quiet {
		mess "Running: $cmd2\n"
	    }
	    set ret [eval exec $cmd2]
	    if !$quiet {
		mess "Done.\n"
	    }
	    return $ret
	} else {
	    set fp [open $cmdfile w]
	    puts $fp "\#!/bin/csh -f"
	    puts $fp $cmd
	    close $fp
	    cmd_chmod 0755 $cmdfile
	    set cmd2 "$rsh $rh -l $user \"cd $path; $evars; $cmdfile $outerr1 $logfile $outerr2\" &"
	    if !$quiet {
		mess "Running: $cmd2\n"
	    }
	    eval exec $cmd2
	    return [list unknown $cmdfile]
	}
    }
}

proc run_application_guibase {path path2 envname tcltool gets_tetin extra \
				  {use_ic 0}} {
    global env black_and_white use_xwindows is_win tetin_file toplevel
    global file_name options program_path_list program_path env

    if $black_and_white { set bw -bw } else { set bw "" }
    switch $use_xwindows {
	0 { set x "" }
	1 { set x -x }
	2 { set x -ogl }
    }
    if {$gets_tetin && ($file_name(tetin) != "")} {
	set tetin "-tetin \"$file_name(tetin)\""
    } else {
	set tetin ""
    }
    set dlrem ""
    if {[info exists options(use_dlremote)] && $options(use_dlremote)} {
	if [ info exists env(DISPLAY) ] {
		set dsp [ split $env(DISPLAY) : ]
		set host [ lindex $dsp 0 ]
		if { $host != "" } {
			set dlrem "-host $host"
		}
	}
    }
    if [info exists env($envname)] {
	set oenv $env($envname)
    } else {
	set oenv ""
    }
    set first_args ""
    if $is_win {
	if [info exists env($envname)] {
	    set prog1 $env($envname)
	} else {
	    set prog1 [get_program [file dirname $path2] [file tail $path2]]
	}
	if [file exists $env(ICEM_ACN)/[file dirname $tcltool]/guibase] {
	    set guibase $env(ICEM_ACN)/[file dirname $tcltool]/guibase
	} else {
	    set guibase $env(ICEM_ACN)/lib/guibase
	}
	set prog $prog1
	set first_args "-f \"$guibase/guibase.tcl\" -tool \"$env(ICEM_ACN)/$tcltool\""
    } else {
	set prog [get_program [file dirname $path] [file tail $path]]
	set p2 [file tail $path2]
	if {[info exists program_path($p2)] && ($program_path($p2) != "")} {
	    set env($envname) $program_path($p2)
	}
    }

    if $use_ic {
	set command "$first_args \"$file_name(project_dir)\" $bw $x $tetin $dlrem $extra"
	ic_run_application_command . "" $prog $command
    } else {
	set command "$prog $file_name(project_dir) $bw $x $tetin $dlrem $extra"
	run_application_command $command
    }

    if {$oenv == ""} {
	catch {unset env($envname)}
    } else {
	set env($envname) $oenv
    }
}

proc run_application_direct {path rootenv extra} {
    global env black_and_white use_xwindows is_win
    global file_name

    if $black_and_white { set bw -bw } else { set bw "" }
    switch $use_xwindows {
	0 { set x "" }
	1 { set x -x }
	2 { set x -ogl }
    }

    set env($rootenv) $env(ICEM_ACN)

    set prog [get_program [file dirname $path] [file tail $path]]
    set command "$prog $bw $x $file_name(project_dir) $extra"
    run_application_command $command
}

# When we are running inside of med this file is loaded into both the
# gui interp and the batch interp.  The following functions are not available
# in the batch interp but they are exported from the gui interp
# (in replay_med.tcl) so they are executed there.

if {[info command wm] != ""} {
    proc run_application_before {} {
	global toplevel
	if $toplevel(single) {
	    wm iconify .
	} else {
	    top_iconify .v
	}
	update
	return [application_wait]
    }
    proc run_application_after {ww} {
	global toplevel
	destroy $ww
	if $toplevel(single) {
	    wm deiconify .
	} else {
	    top_deiconify .v
	}
    }
}

# If bg_if_logfile is 1 then when a logfile is given the job will be run
# in the background.  If it's 0 then even though there is a log file we will
# wait for it to finish and return the value of the exec

proc run_application_command {command { logfile "" } {bg_if_logfile 1}} {
    mess "$command\n"

    set ww [run_application_before]

    if [catch {application_exec $command $logfile 0 $bg_if_logfile} ret] {
	mess "$ret\n" red
    } else {
	mess "$ret\n" blue
    }
    run_application_after $ww
    update
}

# This assumes that the program will print "process hostname pid" unless
# exec_direct is 1

#  returns 1 for okay 0 for error

proc run_application_batch {path envname extra {logfile ""} {exec_direct 1}} {
    global tail_application_log_counter
    global application_batch_done env is_win application_pid
    global application_running
    global application_but_status application_but_terminate
    global application_is_remote application_exec_direct
	global application_was_terminated

    set logfile_tmp 0
    if [info exists application_running] {
	    set mess "An application ($application_running) is already running. Terminate it and run new application?"
	    set ret [tk_dialog .tivc "Terminate" $mess "" 0 "Terminate it" "Don't run new application"]
	    if { $ret } {
		    return
	    }
	    application_terminated
    }

    if {($envname != "") && [info exists env($envname)]} {
	if $exec_direct {
	    # The value of the environment variable is the path to the exec
	    set progpath $env($envname)
	    if $is_win {
		    regsub -all \\\\ $progpath / progpath
	    }
	} else {
	    set progpath $env($envname)
	}
    } else {
	    set progpath [get_program [file dirname $path] [file tail $path] 1]
    }
    set command "\"$progpath\" $extra"

    if {$logfile == ""} {
        set logfile_tmp 1
	    set logfile [cmd_tempfile medlog .tmp]
    }

    set application_running [file tail $path]

    if $exec_direct {
	    set application_is_remote 0
	    set application_exec_direct 1
	    mess "Running: $command\n"
	    set pid [eval exec $command >& $logfile &]
	    set cmdfile ""
    } else {
	    mess "Running: $command\n"
	    set xxx [application_exec $command $logfile]
	    set pid [lindex $xxx 0]
	    set cmdfile [lindex $xxx 1]
    }
    # Now wait for the log file.
    for {set i 0} {$i < 60} {incr i} {
	    if [file exists $logfile] break
	    if {$i > 40} {
	        mess "Warning: file exists $logfile -> 0\n" red
	    }
	    after 1000
    }
    if {$i == 60} {
	    mess "Error: program didn't start -- can't find log file\n" red
	    unset application_running
	    return 0
    }
    mess "Program is running: pid = $pid\n"

    catch {unset application_batch_done}

    if {$is_win || $exec_direct} {
	    set application_pid $pid
	    aie_set_global application_pid $pid
	    application_check_exists
    } else {
	    # The pid that we get above is not the right one because it's for
	    # the 2-line shell script so doing a ps wouldn't help.  The
	    # program should print "process hostname pid" and then we will
	    # have the correct information.
	    catch {unset application_pid}
    }

	set application_was_terminated 0
    set application_but_status [message_add_button status "Show status" "application_status"]
    set application_but_terminate [message_add_button terminate "Terminate" "application_terminated"]
    if [info exists application_pid] {
    	message_enable_button $application_but_status active
	    message_enable_button $application_but_terminate active
    } else {
	    message_enable_button $application_but_status disabled
	    message_enable_button $application_but_terminate disabled
    }

    set fp [open $logfile r]
    set tail_application_log_counter 0
    tail_dashf $fp "tail_application_log $fp $pid"

    if ![info exists application_batch_done] {
	    vwait application_batch_done
    }

    update idle
    tail_dashf_cancel $fp

    close $fp

    set fp [open $logfile r]
    set line ""
    while { ![eof $fp ] } {
	    set last_line $line
	    set line [ gets $fp ]
    }
    close $fp
    set error_exit 0
    if { $last_line == "status 1" } {
	    mess "Application did not finish correctly.\n" red
	    set error_exit 1
    } else {
        mess "Application is finished.\n"
    }

    message_remove_button status
    message_remove_button terminate
    if {$cmdfile != ""} { rm_tmp_file $cmdfile }
    if { $logfile_tmp } {
# may fail immediately after the process was killed under windows
# so wait a bit for the file to close and try
        if $is_win {
            after 500 "rm_tmp_file $logfile"
        } else {
            rm_tmp_file $logfile
        }
    }
    catch {unset application_running}

    if { $application_batch_done && $error_exit == 0 } {
# okay exit
	    return 1
    } else {
# error exit
	    return 0
    }
}

#  i know this kind of sucks, but i mod'd the proc above to suit my needs since
#  not is not time to be breaking stuff. --jka
#  returns 1 for okay; 0 for error; -1 for killed

proc run_application_batch_exec_direct {path envname extra {logfile ""}} {
    global tail_application_log_counter
    global application_batch_done env is_win application_pid
    global application_running
    global application_but_status application_but_terminate
    global application_is_remote application_exec_direct

    set logfile_tmp 0
    if [info exists application_running] {
	set mess "An application ($application_running) is already running. Terminate it and run new application?"
	set ret [tk_dialog .tivc "Terminate" $mess "" 0 "Terminate it" "Don't run new application"]
	if { $ret } {
		return -1
	}
	application_terminated
    }

    if {($envname != "") && [info exists env($envname)]} {
	# The value of the environment variable is the path to the exec
	set progpath $env($envname)
	if $is_win {
	    regsub -all \\\\ $progpath / progpath
	}
    } else {
	set progpath [get_program [file dirname $path] [file tail $path] 0]
    }
    if $is_win {
        set command "\"$progpath\" $extra"
    } else {
        set command "$progpath $extra"
    }

    if {$logfile == ""} {
        set logfile_tmp 1
	set logfile [cmd_tempfile medlog .tmp]
    }

    set application_running [file tail $path]

    set application_is_remote 0
    set application_exec_direct 1
    mess "Running: $command\n"
    set pid [eval exec "$command >& {$logfile} &"]
    aie_set_global application_job [file tail [lindex $command 0]]
    set cmdfile ""

    # Now wait for the log file.
    for {set i 0} {$i < 60} {incr i} {
	if [file exists "$logfile"] break
	if {$i > 40} {
	    mess "Warning: file exists $logfile -> 0\n" red
	}
	after 1000
    }
    if {$i == 60} {
	mess "Error: program didn't start -- can't find log file\n" red
	unset application_running
	return 0
    }
    mess "Program is running: pid = $pid\n"

    catch {unset application_batch_done}

    set application_pid $pid
    aie_set_global application_pid $pid
    application_check_exists

    set application_but_status [message_add_button status \
				    "Show status" "application_status"]
    set application_but_terminate [message_add_button terminate \
				       "Terminate" "application_terminated"]
    message_enable_button $application_but_status active
    message_enable_button $application_but_terminate active

    set fp [open $logfile r]
    set tail_application_log_counter 0
    tail_dashf $fp "tail_application_log $fp $pid"

    if ![info exists application_batch_done] {
	vwait application_batch_done
    }

    update idle
    tail_dashf_cancel $fp

    close $fp

    set fp [open $logfile r]
    set line ""
    while { ![eof $fp ] } {
	set last_line $line
	set line [ gets $fp ]
    }
    close $fp
    set error_exit 0
    if { $last_line == "status 1" } {
	mess "Application did not finish correctly.\n" red
	set error_exit 1
    } else {
        mess "Application is finished.\n"
    }

    message_remove_button status
    message_remove_button terminate
    if {$cmdfile != ""} { rm_tmp_file $cmdfile }
    if { $logfile_tmp } {
        rm_tmp_file $logfile
    }
    catch {unset application_running}

    if { $application_batch_done && $error_exit == 0 } {
# okay exit
	return 1
    } elseif { $application_batch_done } {
# error exit
	return 0
    } elseif { 0 == $application_batch_done} {
# killed exit
	return -1
    } else {
	error "ill-considered case in run_application_batch_exec_direct"
    }
}

proc application_quit_okay {} {
    global application_running
    if ![info exists application_running] { return 1 }
    set mess "An application ($application_running) is running.  If you quit now the job will keep running but the diagnostic output will be lost, in case there is some problem."
    return [expr ![tk_dialog .quit "Quit" $mess "" -1 Okay "Cancel quit"]]
}


proc tail_application_log {fp pid line} {
    global application_pid is_win ic_application_output_hook
    global application_but_status application_but_terminate
    global application_exec_direct application_batch_done

    global tail_application_log_counter

    if { ![ info exists application_pid ] &&
         ![ info exists application_batch_done ] } {
        incr tail_application_log_counter
        if { $tail_application_log_counter > 20 } {
		mess "application_pid never got set\n"
		set application_pid -1
		application_check_exists
	}
    }
# this line is here just to check if the application exists
    if { $line == "<empty line>" } {
	return
    }
#    puts [list tail_application_log $fp $pid $line]

    if {$is_win || ([info exists application_exec_direct] && $application_exec_direct && [info exists application_pid])} {
# application pid set already
	message_enable_button $application_but_status normal
	message_enable_button $application_but_terminate normal
    } else {
        if {![info exists application_pid] && \
	    [string match "process *" $line]} {
	    set application_pid [lindex $line 2]
	    message_enable_button $application_but_status normal
	    message_enable_button $application_but_terminate normal
	    application_check_exists
	}
    }

    # This is so that other modules can also get the output
    if [info exists ic_application_output_hook] {
	eval $ic_application_output_hook $pid {$line}
    } else {
        mess "$line\n" blue
    }
}

proc application_check_exists {} {
    global application_pid application_batch_done application_is_remote

    if {![info exists application_pid] || $application_is_remote} {
	return
    }
    if ![check_process_exists $application_pid] {
	set application_batch_done 1
	unset application_pid
	return
    }
    after 2000 application_check_exists
}

proc application_status {} {
    global application_pid is_win application_is_remote application_job

    # This is because when we press the button we're in the gui interp but
    # the real work is being done in the batch interp.
    if [interp exists batch_interp] {
        batch_interp eval application_status
        return
    }

    if ![info exists application_pid] {
        mess "Can't check process status: PID is not known (yet).\n" red
        return
    }
    if !$is_win {
        if $application_is_remote {
            if [catch {application_exec "ps -lp$application_pid" "" 1} psout] {
                mess "Process $application_pid does not exist\n" red
                return
	        }
	    } else {
	        if [catch {exec ps -lp$application_pid} psout] {
		        mess "Process $application_pid does not exist\n" red
		        return
	        }
	    }
	    set line ""
        foreach wd [split [lindex [split $psout \n] 1] " "] {
	        if [string compare $wd ""] { lappend line $wd }
	    }
	    if {$line == ""} {
	        mess "Can't get process status: process is not running, or is running on a different machine.\n" red
	        return
	    }
	    switch [llength $line] {
	        14 {
		        set tsize [lindex $line 9]
		        set ttime [lindex $line 12]
	        }
	        13 {
		        set tsize [lindex $line 9]
		        set ttime [lindex $line 11]
	        }
	        15 {
		        set tsize [lindex $line 10]
		        set ttime [lindex $line 13]
	        }
	        default {
		        error "Can't parse output of ps: \"$line\""
	        }
	    }
	    set psize [cmd_pagesize]
	    set xxx $psize
	    set sz [lindex [split $tsize :] 0]
	    if [string match *K $sz] {
	        set psize 1024
	        scan $sz %d sz
	    } elseif [string match *M $sz] {
	        set psize 1048576
	        scan $sz %d sz
	    }
	    set tm $ttime
	    set meg [format %.1f [expr {$sz * $psize / 1048576.0}]]
	    mess "\nSize = $meg MB; CPU time = $tm\n"
	    if {($meg < 1) || ($meg > 4000)} {
	        mess "Hmm, that can't be right...\n"
	    }
    } else {
        set size ""
        set time ""
	    set psout [exec tasklist /V /NH /FI "PID eq $application_pid"]
	    foreach line [split $psout \n] {
            if {[lindex $line 1] == $application_pid} {
		        set size [lindex $line 4]
		        set time [lindex $line 8]
		        regsub -all "," $size "" size
		        break
	         }
	    }
	    if {$size == ""} {
	        mess "Can't get process status: process is not running, or is running on a different machine.\n" red
	        return
	    }
	    set meg [format %.1f [expr $size / 1024]]	    
	    set tm "(unknown)"
	    if {$time != ""} { set tm "$time" }
	    mess "\nSize = $meg MB; CPU time = $tm\n"
	    if {($meg < 1) || ($meg > 4000)} {
	        mess "Hmm, that can't be right...\n"
	    }
    }
}

proc application_terminated {{ask 1}} {
    global application_pid application_batch_done application_running
    global application_is_remote application_is_terminating
	global application_was_terminated
	set application_was_terminated 1

    # This is because when we press the button we're in the gui interp but
    # the real work is being done in the batch interp.
    if [interp exists batch_interp] {
	batch_interp eval application_terminated $ask
	return
    }

    if ![info exists application_pid] {
	mess "Can't terminate process: PID is not known (yet).\n" red
	catch {unset application_running}
	return
    }
    if [info exists application_is_terminating] {return}
    set application_is_terminating 1
    if {$ask && [tk_dialog .really_kill "Kill job" " Really terminate job?\n\
	    Intermediate data will be lost." "" -1 Yes No]} {
        unset application_is_terminating
        return
    }

    if $application_is_remote {
	if [catch {application_exec "kill -9 $application_pid" "" 1} res] {
	    mess "$res\n" red
	}

    } else {
	if [catch {cmd_kill $application_pid 9} res] {
	    mess "$res\n" red
	}
    }

    mess "Job killed.\n"
    catch {after cancel application_check_exists}
    set application_batch_done 0
    unset application_is_terminating
}

# This is the simplest way to do it ...  We don't go through application_exec
# because these are not going to be large programs.

proc run_application_exec {command} { 
    mess "$command\n"
    busy 1
	if [ic_wb2_is_running] {
	    aie_set_global wb2_icemcfd_running 1
	}
    if [catch "exec $command" res] {
        mess "$res\n" red
    } else {
        mess "$res\n"
    }
	if [ic_wb2_is_running] {
	    aie_unset_global wb2_icemcfd_running
	}
    busy 0
}

proc get_program {dir name {try_batch 0}} {
    global env program_path is_win bindir

    if !$is_win { set try_batch 0 }

    if { [info exists program_path($name)] } {
	set pth [ glob -nocomplain [string trim $program_path($name)] ]
	if { $pth != "" && $pth != "."} {
	    return $pth
	}
    }
# this is causing an invalid return if the user happens to have
# a $dir/$name file or directory at their current directory, since
# the ICEM installation is not checked. I'm changing the order
# so that the ICEM installation is checked first - Bruce
#    set dirs $dir
    set dirs ""
    if [info exists bindir] {
	lappend dirs $bindir
    }
    foreach e {ICEM_ACN MED_ROOT AUTOHEX_ROOT ICEPAK_ROOT} {
	if [info exists env($e)] {
	    lappend dirs $env($e)/$dir
	}
    }
    lappend dirs $dir
    lappend dirs .

    regsub -all \\\\ $dirs / dirs

    if $try_batch {
	set names [list ${name}_batch $name]
    } else {
	set names $name
    }

    foreach name $names {
	foreach root $dirs {
	    if {[info exists env(ICEM_64)] &&
                ([file exists $root/${name}64] ||
                 [file exists $root/${name}64.bat] ||
                 [file exists $root/${name}64.exe])} {
                return $root/${name}64
	    }
            if {[file exists $root/$name] ||
                [file exists $root/$name.bat] ||
                [file exists $root/$name.exe]} {
                return $root/$name
            }
	}
    }
    error "Can't find program \"$name\" in directories: $dirs"
}
