
proc aie_hosts_hostfile {{prefix icemcfd}} {
  global file_name
  if {$file_name(settings) == ""} {
    return $prefix.hosts
  }
  return [file rootname $file_name(settings)].hosts
}

proc aie_hosts_sysfile {} {
  return "[home_directory]/.icemcfd_hosts"
}

proc aie_hosts_nprocs {hostfile} {
  set nprocs 0
  if {$hostfile == ""} {
    set hostfile [aie_hosts_hostfile]
  }
  if {[file exists $hostfile]} {
    set fd [open $hostfile r]
    while {[gets $fd line] >= 0} {
        set i [string first \# $line]
        if {$i != 0} {
            if {$i > 0} {
                set line [string range $line 0 [expr $i - 1]]
            }
            if {[llength $line] > 0} {
                set np [lindex $line 2]
                if {[catch {expr $np < 1} zero] || $zero} {
                    set np 1
                }
                incr nprocs $np
            }
        }
    }
    close $fd
  }
  return $nprocs
}

proc aie_hosts_nhosts {hostfile} {
  set nhosts 0
  if {$hostfile == ""} {
    set hostfile [aie_hosts_hostfile]
  }
  if {[file exists $hostfile]} {
    set fd [open $hostfile r]
    while {[gets $fd line] >= 0} {
        set i [string first \# $line]
        if {$i != 0} {
            if {$i > 0} {
                set line [string range $line 0 [expr $i - 1]]
            }
            if {[llength $line] > 0} {
                incr nhosts
            }
        }
    }
    close $fd
  }
  return $nhosts
}

proc aie_hosts_setup {hostfile {exit_proc ""} {smoother 0}} {
  global _Hosts

  set top .hosts_setup
  if {[winfo exists $top]} {
  destroy $top
#    wm deiconify $top
#    return
  }
  if {$hostfile == ""} {
    set hostfile [aie_hosts_hostfile]
  }
  set sysfile [aie_hosts_sysfile]

  array set _Hosts {
    nparts 0
    params {type nprocs speed flags rshcmd icemdir license workdir}
    name ""
    type ""
    passwd 0
    tunnel 0
    syshosts {}
    curhosts {}
    selhosts {}
    seltype ""
    modsys 0
    modcur 0
    done 0
    newname ""
    newtype win
  }
  foreach val $_Hosts(params) {
    set _Hosts($val) ""
  }
  set _Hosts(curfile) $hostfile
  set _Hosts(sysfile) $sysfile
  set _Hosts(onexit) $exit_proc

  if {[file exists $sysfile]} {
    set _Hosts(syshosts) [hosts:read $sysfile sys]
  }
  if {[file exists $hostfile]} {
    set _Hosts(curhosts) [hosts:read $hostfile cur]
    hosts:partitions
  }

  toplevel $top
  wm title $top "Host Setup"
  wm protocol $top WM_DELETE_WINDOW "hosts:quit $top"

  # number of processes

  set f [frame $top.np]
  pack $f -side top

  if {$smoother} {
    label $f.lab -text "Number of Processes:"
  } else {
    label $f.lab -text "Number of Partitions:"
  }
  label $f.parts -textvariable _Hosts(nparts)

  pack $f.lab $f.parts -side left -padx 5

  entry $f.ent
  set fgColor [$f.ent cget -fg]
  set bgColor [$f.ent cget -bg]
  destroy $f.ent

  #----- hosts assignments

  set hosts [frame $top.hosts]
  pack $hosts -side top -fill both -expand 1

  # available hosts

  set f [frame $hosts.avail]
  pack $f -side left -fill both -expand 1

  label $f.lab -text "Available Hosts"
  pack $f.lab -side top -fill x

  set list [listbox $f.list -fg $fgColor -bg $bgColor \
    -export 0 -width 20 -height 5 -selectmode extended \
    -yscroll "$f.vs set"]

  scrollbar $f.vs -command "$f.list yview"

  pack $f.vs -side right -fill y
  pack $f.list -side left -fill both -expand 1

  bind $list <ButtonRelease-1> {hosts:config sys}
  bind $list <Double-Button-1> hosts:assign

  set _Hosts(listsys) $list

  foreach host $_Hosts(syshosts) {
    $list insert end [lindex $host 0]
  }

  # assigned host list

  set f [frame $hosts.assign]
  pack $f -side right -fill both -expand 1

  label $f.lab -text "Assigned Hosts"
  pack $f.lab -side top -fill x

  set list [listbox $f.list -fg $fgColor -bg $bgColor \
    -export 0 -width 20 -height 5 -selectmode extended \
    -yscroll "$f.vs set"]

  scrollbar $f.vs -command "$f.list yview"

  pack $f.vs -side right -fill y
  pack $f.list -side left -fill both -expand 1

  bind $list <ButtonRelease-1> {hosts:config cur}
  bind $list <Double-Button-1> hosts:unassign
  bind $list <Map> "catch {focus .hosts_setup}"

  set _Hosts(listcur) $list

  foreach host $_Hosts(curhosts) {
    $list insert end [lindex $host 0]
  }

  # buttons

  set f [frame $hosts.buttons]
  pack $f -side left -fill y -padx 2 -pady 2

  label $f.lab -text ""
  pack $f.lab -side top -fill x

  button $f.add -text "Add ->" -state disabled -command hosts:assign
  button $f.rem -text "<- Remove" -state disabled -command hosts:unassign
  button $f.new -text "New Host" -command "hosts:add $top"
  button $f.del -text "Delete Host" -state disabled \
    -command "hosts:delete $top"
  pack $f.add $f.rem $f.new $f.del -side top -fill x -expand 1

  set _Hosts(hostbut) $f

  #----- host configuration

  set config [frame $top.config -bd 2 -relief groove]
  pack $config -side top -fill x -pady 5

  frame $config.info
  pack $config.info -side top -fill x -pady 2

  set f [frame $config.info.mt]
  pack $f -side left -padx 5 -anchor w
  label $f.lab -text "Machine Type:" -anchor w
  pack $f.lab -side left
  menubutton $f.but -width 12 -menu $f.but.m -relief raised \
    -indicatoron 1 -padx 0 -textvariable _Hosts(type)
  pack $f.but -side left -padx 2

  menu $f.but.m -tearoff 0
  foreach i {win win64_amd linux linux64 linux64_amd hp11 hpit \
             ibm64 sgi64 sol64} {
    $f.but.m add command -label $i -command "set _Hosts(type) $i"
  }

  set f [frame $config.info.rs]
  pack $f -side left -expand 1
  label $f.lab -text "Relative Speed"
  entry $f.ent -width 6 -textvariable _Hosts(speed)
  pack $f.lab $f.ent -side left

  set f [frame $config.info.np]
  pack $f -side right -padx 5
  label $f.lab -text "Number Processors"
  entry $f.ent -width 5 -textvariable _Hosts(nprocs)
  pack $f.lab $f.ent -side left

  frame $config.sep1 -relief groove -bd 1 -height 2
  pack $config.sep1 -side top -fill x -pady 2

  set f [frame $config.rsh]
  pack $f -side top -fill x -padx 5
  label $f.lab -text "Startup Command" -width 15 -anchor w
  pack $f.lab -side left
  button $f.but -text Check -highlightthickness 0 \
    -padx 0 -pady 0 -command "hosts:run $top"
  pack $f.but -side right
  entry $f.ent -width 20 -textvariable _Hosts(rshcmd)
  pack $f.ent -side left -fill x -padx 3 -expand 1
  checkbutton $f.passwd -text "Password" \
    -variable _Hosts(passwd) -onvalue 1 -offvalue 0
  checkbutton $f.tunnel -text "Tunneling" \
    -variable _Hosts(tunnel) -onvalue 1 -offvalue 0
  pack $f.passwd $f.tunnel -side left

  frame $config.sep2 -relief groove -bd 1 -height 2
  pack $config.sep2 -side top -fill x -pady 2

  set f [frame $config.icemdir]
  pack $f -side top -fill x -padx 5
  label $f.lab -text "ICEM_ACN Path" -width 15 -anchor w
  pack $f.lab -side left
  button $f.but -text Check -highlightthickness 0 \
    -padx 0 -pady 0 -command "hosts:run $top icemdir"
  pack $f.but -side right
  entry $f.ent -width 40 -textvariable _Hosts(icemdir)
  pack $f.ent -side left -fill x -padx 3 -expand 1

  set f [frame $config.license]
  pack $f -side top -fill x -padx 5
  label $f.lab -text "License File" -width 15 -anchor w
  pack $f.lab -side left
  button $f.but -text Check -highlightthickness 0 \
    -padx 0 -pady 0 -command "hosts:run $top license" -state disabled
  pack $f.but -side right
  entry $f.ent -width 40 -textvariable _Hosts(license)
  pack $f.ent -side left -fill x -padx 3 -expand 1

  set f [frame $config.workdir]
  pack $f -side top -fill x -padx 5
  label $f.lab -text "Working Directory" -width 15 -anchor w
  pack $f.lab -side left
  button $f.but -text Check -highlightthickness 0 \
    -padx 0 -pady 0 -command "hosts:run $top workdir"
  pack $f.but -side right
  entry $f.ent -width 40 -textvariable _Hosts(workdir)
  pack $f.ent -side left -fill x -padx 3 -expand 1

  #----- buttons

  set f [frame $top.but]
  pack $f -side top -fill x -pady 3

  button $f.sys -text "Save Available" -width 14 -command {hosts:save sys}
  button $f.cur -text "Save Assigned" -width 14 -command {hosts:save cur}
  button $f.close -text "Close" -width 14 -command "hosts:quit $top"

  pack $f.sys $f.cur $f.close -side left -expand 1
}

#----------

proc hosts:center {w par} {
  wm withdraw $w
  update idletasks
  set ww [winfo reqwidth $w]
  set wh [winfo reqheight $w]
  set x [expr [winfo rootx $par] + ([winfo width  $par] - $ww) / 2]
  set y [expr [winfo rooty $par] + ([winfo height $par] - $wh) / 2]
  if {$x < 0} {
    set pos +0
  } elseif {[expr $x + $ww] > [winfo screenwidth $w]} {
    set pos -0
  } else {
    set pos +$x
  }
  if {$y < 0} {
    set pos $pos+0
  } elseif {[expr $y + $wh] > [winfo screenheight $w]} {
    set pos $pos-0
  } else {
    set pos $pos+$y
  }
  wm geometry $w $pos
  update idletasks
  wm deiconify $w
}

proc hosts:dialog {par title msg} {
  global _Hosts

  set w .hosts_dialog
  catch {destroy $w}
  toplevel $w
  wm title $w $title
  wm transient $w $par
  wm protocol $w WM_DELETE_WINDOW {set _Hosts(done) 2}

  label $w.msg -text $msg -anchor w
  pack $w.msg -side top -padx 5 -pady 5

  set f [frame $w.b]
  pack $f -side top -fill x -padx 5 -pady 5

  button $f.yes -text Yes -width 6 -default active \
    -command {set _Hosts(done) 0}
  button $f.no -text No -width 6 -command {set _Hosts(done) 1}
  button $f.cancel -text Cancel -width 6 -command {set _Hosts(done) 2}
  pack $f.yes $f.no $f.cancel -side left -expand 1

  bind $w <Return> "$f.yes invoke"
  bind $w <Escape> "$f.no invoke"

  hosts:center $w $par

  set oldFocus [focus]
  set oldGrab [grab current $w]
  if {$oldGrab != ""} {
    set grabStatus [grab status $oldGrab]
  }
  catch {grab $w}
  tkwait visibility $w
  focus $w
  tkwait variable _Hosts(done)
  catch {focus $oldFocus}
  destroy $w
  if {$oldGrab != ""} {
    if {$grabStatus == "global"} {
      grab -global $oldGrab
    } else {
      grab $oldGrab
    }
  }
  return $_Hosts(done)
}

proc hosts:quit {w} {
  global _Hosts

  hosts:update
  if {$_Hosts(modcur)} {
    set res [hosts:dialog $w "Save Hosts" \
      "Assigned Hosts data has changed.\nDo you want to save it ?"]
    if {$res == 2} return
    if {$res == 0} {hosts:save cur}
  }
  if {$_Hosts(modsys)} {
    set res [hosts:dialog $w "Save Hosts" \
      "Available Hosts data has changed.\nDo you want to save it ?"]
    if {$res == 2} return
    if {$res == 0} {hosts:save sys}
  }
  if {$_Hosts(onexit) != ""} {$_Hosts(onexit) $_Hosts(curfile)}
  destroy $w
}

proc hosts:read {hostfile type} {
  global _Hosts
  set hostlist {}
  if [catch {open $hostfile r} fd] {
    return $hostlist
  }
  while {[gets $fd line] >= 0} {
    set data [string trim $line]
    set i [string first \# $data]
    if {$i == 0} continue
    if {$i > 0} {
      set data [string range $data 0 [expr $i - 1]]
    }
    if {[llength $data] == 0} continue
    set name [lindex $line 0]
    lappend hostlist $name
    set n 1
    foreach val $_Hosts(params) {
      set _Hosts($type,$name,$val) [lindex $data $n]
      incr n
    }
  }
  close $fd
  return $hostlist
}

proc hosts:save {type} {
  global _Hosts

  set hosts $_Hosts(${type}hosts)
  if {$hosts != ""} {
    hosts:update
    set hostfile $_Hosts(${type}file)
    set fd [open $hostfile w+]
    puts $fd "\# $_Hosts(params)"
    foreach name $hosts {
      set line $name
      foreach val $_Hosts(params) {
        lappend line $_Hosts($type,$name,$val)
      }
      puts $fd $line
    }
    close $fd
  }
  set _Hosts(mod$type) 0
}

proc hosts:partitions {} {
  global _Hosts

  set nparts 0
  foreach name $_Hosts(curhosts) {
    set np $_Hosts(cur,$name,nprocs)
    if {[catch {expr int($_Hosts(cur,$name,nprocs))} np] || $np < 1} {
      set np 1
    }
    incr nparts $np
  }
  set _Hosts(nparts) $nparts
}

proc hosts:update {} {
  global _Hosts

  if {$_Hosts(selhosts) == ""} return
  set type $_Hosts(seltype)
  set _Hosts(flags) 0
  if {$_Hosts(passwd)} {incr _Hosts(flags) 1}
  if {$_Hosts(tunnel)} {incr _Hosts(flags) 2}
  foreach val $_Hosts(params) {
    set param [string trim $_Hosts($val)]
    if {$param != ""} {
      foreach name $_Hosts(selhosts) {
        if {$_Hosts($type,$name,$val) != $param} {
          set _Hosts($type,$name,$val) $param
          incr _Hosts(mod$type)
        }
      }
    }
  }
  if {$type == "cur"} hosts:partitions
}

proc hosts:clear {type} {
  global _Hosts
  foreach val $_Hosts(params) {
    set _Hosts($val) ""
  }
  set _Hosts(passwd) 0
  set _Hosts(tunnel) 0
  set _Hosts(selhosts) ""
  if {$type != $_Hosts(seltype) && $_Hosts(seltype) != ""} {
      $_Hosts(list$_Hosts(seltype)) selection clear 0 end
  }
}

proc hosts:buttons {} {
  global _Hosts

  foreach b {add rem del} {
    $_Hosts(hostbut).$b configure -state disabled
  }
  if {$_Hosts(selhosts) == ""} return
  if {$_Hosts(seltype) == "sys"} {
    $_Hosts(hostbut).add configure -state normal
    $_Hosts(hostbut).del configure -state normal
  } else {
    $_Hosts(hostbut).rem configure -state normal
  }
}

proc hosts:config {type} {
  global _Hosts

  hosts:update
  hosts:clear $type

  set list $_Hosts(list$type)
  set ind [$list curselection]
  if {$ind != {}} {
    set hosts {}
    foreach i $ind {
      lappend hosts [$list get $i]
    }
    foreach val $_Hosts(params) {
      set data $_Hosts($type,[lindex $hosts 0],$val)
      foreach h $hosts {
        if {$_Hosts($type,$h,$val) != $data} {
          set data ""
          break
        }
      }
      set _Hosts($val) $data
    }
    set _Hosts(passwd) 0
    set _Hosts(tunnel) 0
    if {$_Hosts(flags) != ""} {
      if {$_Hosts(flags) == 1 || $_Hosts(flags) == 3} {
        set _Hosts(passwd) 1
      }
      if {$_Hosts(flags) == 2 || $_Hosts(flags) == 3} {
        set _Hosts(tunnel) 1
      }
    }
    set _Hosts(selhosts) $hosts
    set _Hosts(seltype) $type
  }
  hosts:buttons
}

proc hosts:assign {} {
  global _Hosts

  if {$_Hosts(seltype) != "sys" || $_Hosts(selhosts) == ""} return
  hosts:update
  foreach name $_Hosts(selhosts) {
    $_Hosts(listcur) insert end $name
    lappend _Hosts(curhosts) $name
    foreach val $_Hosts(params) {
      set _Hosts(cur,$name,$val) $_Hosts(sys,$name,$val)
    }
  }
  hosts:partitions
  set _Hosts(modcur) 1
}

proc hosts:unassign {} {
  global _Hosts
  if {$_Hosts(seltype) != "cur" || $_Hosts(selhosts) == ""} return
  set list $_Hosts(listcur)
  set ind [$list curselection]
  set hosts {}
  set n 0
  foreach name $_Hosts(curhosts) {
    if {[lsearch -exact $ind $n] < 0} {
      lappend hosts $name
    }
    incr n
  }
  $list delete 0 end
  foreach name $hosts {
    $list insert end $name
  }
  set _Hosts(curhosts) $hosts
  set _Hosts(selhosts) ""
  set _Hosts(seltype) ""
  set _Hosts(modcur) 1
  hosts:buttons
  hosts:partitions
}

proc hosts:add {par} {
  global _Hosts

  set w .hosts_add
  toplevel $w
  wm title $w "Add Host"
  wm transient $w $par
  wm protocol $w WM_DELETE_WINDOW {set _Hosts(done) 0}

  set f [frame $w.name]
  pack $f -side top -padx 5 -pady 5 -fill x
  label $f.lab -text Name -width 6 -anchor w
  pack $f.lab -side left
  entry $f.ent -width 20 -textvariable _Hosts(newname)
  pack $f.ent -side left -fill x

  set f [frame $w.type]
  pack $f -side top -padx 5 -anchor w
  label $f.lab -text Type -width 6 -anchor w
  pack $f.lab -side left
  menubutton $f.but -width 12 -menu $f.but.m -relief raised \
    -indicatoron 1 -padx 0 -textvariable _Hosts(newtype)
  pack $f.but -side left

  menu $f.but.m -tearoff 0
  foreach i {win win64_amd linux linux64 linux64_amd hp11 hpit \
             ibm64 sgi64 sol64} {
    $f.but.m add command -label $i -command "set _Hosts(newtype) $i"
  }

  set f [frame $w.but]
  pack $f -side top -fill x -pady 5
  button $f.accept -text Accept -default active \
    -command hosts:checkname
  button $f.cancel -text Cancel -command {set _Hosts(done) 0}
  pack $f.accept $f.cancel -side left -expand 1

  bind $w <Return> "$f.accept invoke"
  bind $w <Escape> "$f.cancel invoke"

  hosts:center $w $par
  focus $w.name.ent
  tkwait variable _Hosts(done)

  if {$_Hosts(done)} {
    set name $_Hosts(newname)
    set type $_Hosts(newtype)
    set list $_Hosts(listsys)
    lappend _Hosts(syshosts) $name
    set _Hosts(sys,$name,type) $type
    set _Hosts(sys,$name,nprocs) 1
    set _Hosts(sys,$name,speed) 100
    set _Hosts(sys,$name,flags) 0
    set _Hosts(sys,$name,rshcmd) rsh
    if {[string match win* $type]} {
      set _Hosts(sys,$name,icemdir) "C:\\Program Files\\Ansys Inc\\v120\\icemcfd\\$type"
      set _Hosts(sys,$name,license) "C:\\Program Files\\Ansys Inc\\v120\\icemcfd\\$type\\lic\\license.dat"
      set _Hosts(sys,$name,workdir) "C:\\Temp"
    } else {
      set _Hosts(sys,$name,icemdir) "/usr/ansys_inc/v120/icemcfd/$type"
      set _Hosts(sys,$name,license) "/usr/ansys_inc/v120/icemcfd/$type/lic/license.dat"
      set _Hosts(sys,$name,workdir) "/tmp"
    }
    $list selection clear 0 end
    $list insert end $name
    $list selection set end
    $list see end
    hosts:config sys
    incr _Hosts(modsys)
  }

  destroy $w
}

proc hosts:checkname {} {
  global _Hosts

  set name $_Hosts(newname)
  if {$name != "" && [lsearch $_Hosts(syshosts) $name] < 0} {
    set _Hosts(done) 1
  }
}

proc hosts:delete {par} {
  global _Hosts
  if {$_Hosts(seltype) != "sys" || $_Hosts(selhosts) == ""} return
  set msg "Delete the hosts:"
  foreach name $_Hosts(selhosts) {
    append msg "\n$name"
  }
  append msg " ?"
  if {[hosts:dialog $par "Delete Hosts" $msg]} return
  set list $_Hosts(listsys)
  set ind [$list curselection]
  set hosts {}
  set n 0
  foreach name $_Hosts(syshosts) {
    if {[lsearch -exact $ind $n] < 0} {
      lappend hosts $name
    }
    incr n
  }
  $list delete 0 end
  foreach name $hosts {
    $list insert end $name
  }
  set _Hosts(syshosts) $hosts
  set _Hosts(selhosts) ""
  set _Hosts(seltype) ""
  set _Hosts(modsys) 1
  hosts:buttons
}

proc hosts:run {par {dir ""}} {
  global _Hosts is_win
  
  set top .hosts_setup
  if {[winfo exists $top]} {
    wm deiconify $top
  }
  
  if {[llength $_Hosts(selhosts)] != 1} return
  set cmd [string trim $_Hosts(rshcmd)]
  if {$cmd == ""} return
  lappend cmd $_Hosts(selhosts)
  if {$dir == ""} {
    if {[string match win* $_Hosts(type)]} {
      lappend cmd cmd /c cd
    } else {
      lappend cmd pwd
    }
  } else {
    set dir [string trim $_Hosts($dir)]
    if {$dir == ""} return
    if {[string match win* $_Hosts(type)]} {
      lappend cmd dir [join [split $dir /] \\]
    } else {
      lappend cmd ls $dir
    }
  }

  set w .hosts_run
  toplevel $w
  wm title $w "Executing..."
  wm transient $w $par

  label $w.lab -text $cmd
  pack $w.lab -side top -anchor w -padx 5 -pady 5

  set f [frame $w.output]
  pack $f -side top -fill both -padx 5 -expand 1

  text $f.text -width 40 -height 10 -yscroll "$f.scroll set"
  pack $f.text -side left -fill both -expand 1

  scrollbar $f.scroll -command "$f.text yview"
  pack $f.scroll -side right -fill y

  button $w.close -text Close -default active -state disabled \
    -command "destroy $w"
  pack $w.close -side bottom -padx 5 -pady 5

  hosts:center $w $par
  update

  if {$_Hosts(passwd)} {
    if {$is_win} {
      catch {eval exec cmd /c $cmd} msg
    } else {
      catch {eval exec xterm -e $cmd} msg
    }
    if {$msg != ""} {
      $f.text insert end "$msg\n"
    }
  } else {
    if {[catch {open |$cmd r} fd]} {
      $f.text insert end "Error:$fd\n"
    } else {
      while {[gets $fd line] >= 0} {
        $f.text insert end "$line\n"
        update idletasks
      }
      if {[catch {close $fd} msg]} {
        $f.text insert end "Error:$msg\n"
      }
    }
  }

  $w.close configure -state normal
}

