#
# $RCSfile: run.itcl,v $ --
#
#      This file contains ...
#
# Copyright (c) 2004  Anton Kokalj   Email: tone.kokalj@ijs.si
#
#
# This file is distributed under the terms of the GNU General Public
# License. See the file `COPYING' in the root directory of the present
# distribution, or http://www.gnu.org/copyleft/gpl.txt .
#
# 
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
# ANTON KOKALJ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
#
# $Id: run.itcl,v 1.8 2009-07-16 16:58:45 kokalj Exp $ 
#

proc ::pwscf::run::run {guiObj {init 1} {use_defaults 1}} {
    variable ::pwscf::settings
    variable run

    # initializing ...
    if { $init == 1 } {
	_init $guiObj
    }

    set moduleObj   [$guiObj getSelected moduleObj]
    set moduleIdent [$guiObj getSelected moduleIdent]

    if { $use_defaults == 1 } {
	# use default values
	set _run(RUN.prefix,$moduleObj)  $::pwscf::settings(RUN.prefix)
	set _run(RUN.postfix,$moduleObj) $::pwscf::settings(RUN.postfix)
	set _run(PW,$moduleObj)          $::pwscf::settings(PW)
	set _run(PH,$moduleObj)          $::pwscf::settings(PH)
	set _run(PP,$moduleObj)          $::pwscf::settings(PP)
	set _run(PROJWFC,$moduleObj)     $::pwscf::settings(PROJWFC)
	set _run(D3,$moduleObj)          $::pwscf::settings(D3)
	set _run(LD1,$moduleObj)         $::pwscf::settings(LD1)
    } else {
	# use values as specified in the current configuration
	set _run(RUN.prefix,$moduleObj)  $run(RUN.prefix,$moduleObj)  
	set _run(RUN.postfix,$moduleObj) $run(RUN.postfix,$moduleObj) 
	set _run(PW,$moduleObj)          $run(PW,$moduleObj)          
	set _run(PH,$moduleObj)          $run(PH,$moduleObj)          
	set _run(PP,$moduleObj)          $run(PP,$moduleObj)          
	set _run(PROJWFC,$moduleObj)     $run(PROJWFC,$moduleObj)     
	set _run(D3,$moduleObj)          $run(D3,$moduleObj)          
	set _run(LD1,$moduleObj)         $run(LD1,$moduleObj)
    }

    #
    # determine what program to run
    #

    switch -glob -- $moduleIdent {
	*pw {
	    set runCmd "$_run(RUN.prefix,$moduleObj) $_run(PW,$moduleObj) $_run(RUN.postfix,$moduleObj)"

	    # find out the outdir and create it !!!
	    _mkdirOutdir $moduleObj
	}
	*ph {
	    set runCmd "$_run(RUN.prefix,$moduleObj) $_run(PH,$moduleObj) $_run(RUN.postfix,$moduleObj)"

	    # find out the outdir and create it !!!
	    _mkdirOutdir $moduleObj
	}
	*pp {
	    set runCmd "$_run(RUN.prefix,$moduleObj) $_run(PP,$moduleObj) $_run(RUN.postfix,$moduleObj)"

	    # find out the outdir and create it !!!
	    _mkdirOutdir $moduleObj
	}
	*pr {
	    set runCmd "$_run(RUN.prefix,$moduleObj) $_run(PROJWFC,$moduleObj) $_run(RUN.postfix,$moduleObj)"

	    # find out the outdir and create it !!!
	    _mkdirOutdir $moduleObj
	} 
	*d3 {
	    set runCmd "$_run(RUN.prefix,$moduleObj) $_run(D3,$moduleObj) $_run(RUN.postfix,$moduleObj)"

	    # find out the outdir and create it !!!
	    _mkdirOutdir $moduleObj
	}
	*ld1 {
	    set runCmd "$_run(RUN.prefix,$moduleObj) $_run(LD1,$moduleObj) $_run(RUN.postfix,$moduleObj)"

	    # find out the outdir and create it !!!
	    _mkdirOutdir $moduleObj
	}
	default {
	    # module not yet supported ...
	    return 0
	}
    }

    #
    # run the calculation
    #
    if { $run(mode,$moduleObj) == "nonblocking" } {

	# run in non-blocking mode (i.e. on the fly)

	set execID [::tclu::nonblocking open]

	# display stdout into pager while calculating
	::tclu::nonblocking stdout $execID "::pwscf::run::defaultStdOutPager [list $moduleObj]"

	# run the calculation
	set status [eval ::tclu::nonblocking exec $execID $runCmd < $run(inpFile,$moduleObj) 2> $run(errFile,$moduleObj)]

	# CALCULATION is finished, hence ...

	# save the stdout to outFile
	::tclu::nonblocking save $execID $run(outFile,$moduleObj)
	
	# we are done
	::tclu::nonblocking unset $execID 

	return $status
    } else {

	# run in background and forget about it ...

	eval exec $runCmd < $run(inpFile,$moduleObj) > $run(outFile,$moduleObj) 2> $run(errFile,$moduleObj) &
	set run(run.done,$moduleObj) 1
	return 1
    }
}


proc ::pwscf::run::runAs {guiObj} {
    variable run

    # first configure and then run ...

    set t [::tku::toplevelExists .pwgui_settings]

    # initializing ...
    _init $guiObj
    
    set moduleObj   [$guiObj getSelected moduleObj]
    set moduleIdent [$guiObj getSelected moduleIdent]
    set prog        [getProg $moduleIdent]

    # create a configure module
    set obj [_configureModule $moduleObj $prog]

    ::guib::widgets::dialogshell $t -title "Configure calculation" -separator 1 -transient 0
    $t add Cancel -text "Cancel"        -command [list ::pwscf::run::_cancel $t $guiObj]    
    $t add Apply  -text "Apply & Run"   -command [list ::pwscf::run::_configApply $t $obj $guiObj]
    $t default Apply

    #
    # build the config-GUI recursively
    #
    set cs [$t childsite]    
    $obj makeEmbedGUI $cs
    
    ::tku::centerWindow $t
}


proc ::pwscf::run::runAndXC {guiObj} {
    variable run

    # first run the calculation

    set status [run $guiObj]

    # now display with xcrysden if status is OK, and program is pw.x or pp.x

    _displayXC $guiObj $status
}

proc ::pwscf::run::runAsAndXC {guiObj} {
    variable run

    # first run the calculation
    runAs $guiObj

    # wait for calculation to finish 
    set moduleObj [$guiObj getSelected moduleObj]
    tkwait variable ::pwscf::run::run(run.done,$moduleObj)

    # now display with xcrysden if status is OK, and program is pw.x or pp.x
    _displayXC $guiObj $run(status,$moduleObj)
}


proc ::pwscf::run::defaultStdOutPager {moduleObj id newLine} {
    variable run

    set create 0
    if { ! [info exists run(textWidget,$moduleObj)] } {
	set create 1
    } elseif { ! [winfo exists $run(textWidget,$moduleObj)] } {
	set create 1
    }
    
    if { $create } {
	# calling the pager for the first time: create the toplevel
	# and text widgets ...
	set t [::guib::widgets::dialogshell [::tku::widgetName] -title "Output file: $run(outFile,$moduleObj)" -separator 1 -transient 0]
	$t add Close  -text Close  -command [list destroy $t]
	$t default Close
	
	set w [$t childsite]
	set run(textWidget,$moduleObj) [::iwidgets::scrolledtext $w.text \
					    -labeltext "Output file:\n$run(outFile,$moduleObj)" \
					    -hscrollmode dynamic -vscrollmode dynamic \
					    -wrap none -state normal]
	pack $run(textWidget,$moduleObj) -side top -fill both -expand 1
    }

    $run(textWidget,$moduleObj) configure -state normal
    $run(textWidget,$moduleObj) insert end $newLine
    $run(textWidget,$moduleObj) yview moveto 1
    $run(textWidget,$moduleObj) configure -state disabled
}


proc ::pwscf::run::_init {guiObj} {
    variable ::pwscf::settings
    variable run

    # initializing ...

    set moduleObj   [$guiObj getSelected moduleObj]
    set moduleIdent [$guiObj getSelected moduleIdent]

    ::tclu::newset run(mode,$moduleObj)        $run(mode)
    ::tclu::newset run(RUN.prefix,$moduleObj)  $::pwscf::settings(RUN.prefix)
    ::tclu::newset run(RUN.postfix,$moduleObj) $::pwscf::settings(RUN.postfix)
    ::tclu::newset run(PW,$moduleObj)          $::pwscf::settings(PW)
    ::tclu::newset run(PH,$moduleObj)          $::pwscf::settings(PH)
    ::tclu::newset run(PP,$moduleObj)          $::pwscf::settings(PP)
    ::tclu::newset run(PROJWFC,$moduleObj)     $::pwscf::settings(PROJWFC)
    ::tclu::newset run(D3,$moduleObj)          $::pwscf::settings(D3)
    ::tclu::newset run(LD1,$moduleObj)         $::pwscf::settings(LD1)

    #
    # determine what are the IO-files
    #

    set inpFile [$guiObj getSelected saveFile]
    if { $inpFile == "" } {	
	# save the file
	set inpFile [$guiObj saveAs]
	if { $inpFile == "" } {
	    # Cancel button was pressed
	    return -code return 0
	}
    } else {
	$guiObj save 1 ; # "1" here is for nocomplain. I am not sure if nocomplain is good idea ???
    }
    
    # get the output-file from the input-file; also the error file
    
    #set head [regsub {\.(inp|in)$} $inpFile {}]
    regsub {\.(inp|in)$} $inpFile {} head
    set run(inpFile,$moduleObj)  $inpFile
    set run(outFile,$moduleObj)  $head.out
    set run(errFile,$moduleObj)  $head.err

    set run(status,$moduleObj) 1
    set run(run.done,$moduleObj) 0
}


proc ::pwscf::run::getProg {moduleIdent} {
    switch -glob -- $moduleIdent {
	*pw  { return pw.x }
	*ph  { return ph.x }
	*pp  { return pp.x }
	*pr  { return projwfc.x }
	*d3  { return d3.x }
	*atomic { return ld1.x }
	default {
	    # module not yet supported ...
	    return ""
	}
    }    
}


proc ::pwscf::run::_configureModule {moduleObj prog} {

    set var    [string toupper [string trimright $prog .x]]

    set script [subst -nocommands {

	optionSetDefault line     decor normal
	optionSetDefault namelist decor normal
	
	separator -label "--- PWscf settings ---\n\n The calculation will be executed as:  prefix $prog postfix < input > output"
	line pwscf_fix -name "Prefix & postfix (i.e. Prefix = mpirun -np 2  .AND.  Postfix = -npool 2)" {
	    var RUN.prefix  -variable ::pwscf::run::run(RUN.prefix,$moduleObj)  -label "Prefix:"
	    var RUN.postfix -variable ::pwscf::run::run(RUN.postfix,$moduleObj) -label "Postfix:"
	}
	line pwscf_exe -name "Specify $prog executable (i.e. /usr/local/bin/$prog)" {
	    var $var -variable ::pwscf::run::run($var,$moduleObj) -label "Executable \\\"pw.x\\\":" -widget entryfileselect
	}
	line io_files -name "Input & Output files" {
	    var inFile  -variable ::pwscf::run::run(inpFile,$moduleObj) -label "Input file:" -widget entryfileselect
	    var outFile -variable ::pwscf::run::run(outFile,$moduleObj) -label "Output file:" -widget entryfileselect
	    var errFile -variable ::pwscf::run::run(errFile,$moduleObj) -label "Error file:" -widget entryfileselect
	}
	line run_mode -name "Mode of calculation:" {
	    var mode {
		-variable ::pwscf::run::run(mode,$moduleObj) 
		-label "Mode of calculation:" 
		-textvalue {
		    "on the fly (for short jobs)" 
		    "in background (for longer jobs)" 
		} 
		-value { 
		    nonblocking  
		    background 
		}
		-widget radiobox
	    }
	}

	postprocess {
	    loadFromVar
	    widgetconfigure inFile -width 60
	}
    }]

    set obj [::guib::moduleObj settings\#auto -title "PWgui: settings" -script $script]
}
      

proc ::pwscf::run::_configApply {t configObj guiObj} {
    variable run

    if { [winfo exists $t] } {
	destroy $t
    }

    # save "config" variables
    $configObj saveToVar ::pwscf::run
    
    # run the calculation
    set status [run $guiObj no_init no_defaults]

    # check the status of the run
    set moduleObj [$guiObj getSelected moduleObj]
    if { $status == 1 } {
	set run(status,$moduleObj) 1
    } else {
	set run(status,$moduleObj) 0
    }
    set run(run.done,$moduleObj) 1
}


proc ::pwscf::run::_cancel {t guiObj} {
    variable run

    # destroy "config" toplevel
    if { [winfo exists $t] } {
	destroy $t
    }
    
    # set the status to zero
    set moduleObj [$guiObj getSelected moduleObj]
    set run(status,$moduleObj) 0    
    set run(run.done,$moduleObj) 1
}


proc ::pwscf::run::_mkdirOutdir {moduleObj} {
    set outdir [string trim [namespace eval ::guib "$moduleObj varvalue outdir"] '\"]
    if { $outdir != "" } {
	if { ! [file isdirectory $outdir] } {
	    if { [catch {file mkdir $outdir} errMsg] } {
		::tclu::warningDialog "cannot create temporary directory \"$outdir\". Aborting the calculation !!!\n\nERROR message: $errMsg"
		return -code return 0
	    }
	} 
	if { ! [file writable $outdir] } {
	    ::tclu::warningDialog "temporary directory \"$outdir\" is not writable"
	    return -code return 0
	}
    }
}


proc ::pwscf::run::_displayXC {guiObj status} {
    variable run

    set moduleObj   [$guiObj getSelected moduleObj]
    set moduleIdent [$guiObj getSelected moduleIdent]
    set prog        [getProg $moduleIdent]
    if { $status == 1 } {
	if { $prog == "pw.x" } {
	    # launch XCRYSDEN in background mode and display the
	    # structure from output file 
	    exec xcrysden --pwo $run(outFile,$moduleObj) &
	    
	} elseif { $prog == "pp.x" } {
	    # check if the output_format == 3 || output_format == 5, and display
	    # the structure/property from fileout
	    
	    set output_format [namespace eval ::guib "$moduleObj vartextvalue output_format"]
	    set fileout       [string trim [namespace eval ::guib "$moduleObj varvalue fileout"] '\"]
	    if { [string match *XSF* $output_format] } {
		# it is an XSF format
		exec xcrysden --xsf $fileout &
	    }
	}
    }
}

