
# 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
# $Revision$ on $Date$

# This file contains special comments of the form ## html ... which are
# used to automatically generate documentation

## <h2>Scripting commands</h2>
## These commands are used in med for writing scripts.
## All script commands begin with ic, for IcemCfd.<p>

## <h3>File functions</h3>
## These functions do various high-level and misc things.

## Return the list of command line arguments that were passed after
## <b>-batch</b> and <b>-script</b>

proc ic_argv {} {
    global argv
    
    set ret ""
    set ac [llength $argv]
    for {set i 0} {$i < $ac} {incr i} {
	switch -- [lindex $argv $i] {
	    -tool - -script { incr i }
	    -batch { }
	    default { lappend ret [lindex $argv $i] }
	}
    }
    return $ret
}

## Save project data to a file.

proc ic_save_project_file_old {file data {AppName ""}} {
    global Version env

    set fp [open $file w]
    puts $fp "\# ICEM CFD Project Settings"
    puts $fp "\# Saved by [cmd_whoami] on [cmd_uname_n] on [cmd_date]"

    if {$AppName == ""} {
	    set AppName "ANSYS ICEM CFD Environment"
    }

    if [file exists $env(ICEM_ACN)/icemcfd/version] {
        set f [open $env(ICEM_ACN)/icemcfd/version r]
        set ver "[lindex [gets $f] end]"
        set msg "$AppName - version $ver"
	    close $f
    } else {
        set msg "$AppName - version $Version"
    }

    if [file exists $env(ICEM_ACN)/release_date] {
	    set f [open $env(ICEM_ACN)/release_date r]
	    append msg " - Release date: [gets $f]"
	    close $f
    }
    puts $fp "\# $msg"
    puts $fp ""

    foreach line $data {
	    puts $fp $line
    }
    close $fp
}

proc ic_save_project_file {file {data ""} {AppName ""}} {
    global Version env batch_mode

    if !$batch_mode {
        if [ic_wb2_input_parameter_mode] return
    }
    if {$data == ""} {
        set data [save_project_file $file]
    }
    set fp [open $file w]
    puts $fp "\# ICEM CFD Project Settings"
    puts $fp "\# Saved by [cmd_whoami] on [cmd_uname_n] on [cmd_date]"
    if {$AppName == ""} {
	    set AppName "ANSYS ICEM CFD Environment"
    }
    if [file exists $env(ICEM_ACN)/icemcfd/version] {
        set f [open $env(ICEM_ACN)/icemcfd/version r]
        set ver "[lindex [gets $f] end]"
        set msg "$AppName - version $ver"
	    close $f
    } else {
        set msg "$AppName - version $Version"
    }
    if [file exists $env(ICEM_ACN)/release_date] {
	    set f [open $env(ICEM_ACN)/release_date r]
	    append msg " - Release date: [gets $f]"
	    close $f
    }
    puts $fp "\# $msg"
    puts $fp ""
    foreach line $data {
	    puts $fp $line
    }
    close $fp
}

proc ic_load_project_file {prj_file {convert 0} {quiet 0}} {
    global version env is_win options beta_mode file_name fam_visible batch_mode
    global bc_name_list env
    global material_data material_type material_tbl_data material_tbl_type
    global material_dpnd_data mat_name_list tbl_name_list
    global params_data params_type subcase_data param_name_list
    global load_only_prism_params prism_parts
    global prism_family_on prism_values prism_families
    global prism_family_height prism_family_ratio prism_family_n_layers

    if [ic_boco_is_loaded] {
        ic_boco_unload
    }
    set file_name(family_boco) ""
    set file_name(attributes) ""
    catch { unset bc_name_list }

    if {[ic_uns_is_loaded] || [ic_str_is_loaded]} {
        ic_unload_mesh $quiet
    }
    set file_name(domain) ""
    
    if [ic_hex_blocking_loaded] {
        ic_hex_unload_blocking
    }
    set file_name(blocking) ""
    
    if [ic_cart_is_loaded] {
        ic_cart_unload
    }
    set file_name(cart_file) ""   
    set file_name(solver_params) ""
  
    if [ic_geo_is_loaded] {
        ic_unload_tetin $quiet
    }
    set file_name(tetin) ""
    ic_empty_tetin

    catch { unset material_data }
    catch { unset material_type }
    catch { unset material_tbl_data }
    catch { unset material_tbl_type }
    catch { unset material_dpnd_data }
    catch { unset mat_name_list }
    catch { unset tbl_name_list }

    catch {
       foreach mat [ic_param_list_children :Isotropic_Material] {
          ic_param_delete_node $mat
       }
    }
    catch {
      foreach mat [ic_param_list_children :Anisotropic_Shell_Material] {
          ic_param_delete_node $mat
       }
    }
    catch {
       foreach mat [ic_param_list_children :Anisotropic_Solid_Material] {
           ic_param_delete_node $mat
       }
    }
    catch {
       foreach mat [ic_param_list_children :Orthotropic_Shell_Material] {
          ic_param_delete_node $mat
       }
    }
    catch {
       foreach mat [ic_param_list_children :Isotropic_Thermal_Material] {
          ic_param_delete_node $mat
       }
    }
    catch {
       foreach mat [ic_param_list_children :Anisotropic_Thermal_Material] {
          ic_param_delete_node $mat
       }
    }

    catch {
       foreach mat_tbl [ic_param_list_children :Material_Property_Table] {
          ic_param_delete_node $mat_tbl
       }
    }
    catch {
       foreach mat_tbl [ic_param_list_children :Stress_Dependent_Material_Property_Table] {
          ic_param_delete_node $mat_tbl
       }
    }
    catch {
       foreach mat_tbl [ic_param_list_children :Nonlinear_Material_Temp_Dependence_Tables] {
          ic_param_delete_node $mat_tbl
       }
    }
    catch {
       foreach mat_tbl [ic_param_list_children :Dynamic_Load_Table] {
          ic_param_delete_node $mat_tbl
       }
    }
    catch {
       foreach mat_tbl [ic_param_list_children :Modal_Damping_Table] {
          ic_param_delete_node $mat_tbl
       }
    }

    catch {
       foreach ch [ic_param_list_children :EXEC] {
          ic_param_delete_node $ch
       }
    }
    catch {
       foreach ch [ic_param_list_children :Output_Selection] {
          ic_param_delete_node $ch
       }
    }
    catch {
       foreach ch [ic_param_list_children :Parameter] {
          ic_param_delete_node $ch
       }
    }
    catch { unset subcase_data }
    catch {
       foreach ch [ic_param_list_children :Subcase] {
          ic_param_delete_node $ch
       }
    }
    foreach type {EIGRL EIGB NLPARM TSTEP FREQ FREQ1 FREQ2 DAREA DELAY DPHASE TLOAD1 TLOAD2 RLOAD1 RLOAD2 \
                  DLOAD LOAD EIGR SPCADD MPCADD TEMPD Gravity_Load INCLUDE} {
       catch {
          foreach ch [ic_param_list_children :$type] {
             ic_param_delete_node $ch
          }
       }
    }
    catch {
        foreach lcname [ic_csystem_list] {
	        ic_csystem_delete $lcname
        }
    }
    catch { unset lcs_data }
    catch { unset fam_visible }
    catch { array unset prism_family_on }

    foreach p [array names file_name] {
	    if {[string match *_dir $p] && ($file_name($p) == "")} {
	        set file_name($p) .
	    }
    }
    
    ic_undo_clear
    set file_name(settings) ""
    
    if {$prj_file != ""} {
        regsub -all \\\\ $prj_file / prj_file
    }
    if {[file extension $prj_file] == ".prj"} {
        uplevel \#0 [list source $prj_file]
    } elseif {[file extension $prj_file] == ".wbpj"} {
        set wb_prj_file $prj_file
        if [catch {set fp [open $wb_prj_file r]} res] {
            mess "Can't open workbench project file $wb_prj_file: $res\n" red
            return
        }
        set i 0
        while {![eof $fp]} {
            set line($i) [gets $fp]
            incr i
        }
        close $fp
        set cur_pwd [pwd]
        set new_pwd [file dir $wb_prj_file]
        if [expr [file exists $new_pwd] && [file isdirectory $new_pwd]] {
            cd $new_pwd
        } else {
            mess "Path to project $new_pwd not found\n" red
            return
        }
        set wb_prj_file_name [file rootname [file tail $wb_prj_file]]
        set wb_prj_file_dir ${wb_prj_file_name}_files
        set len $i
        set found_projects ""
        if !$convert {
            for {set i 0} {$i < $len} {incr i} {
                if [string match -nocase *\"FileType\":*\"DependentFiles\":*\"Location\":*r"\$\(ProjectName\)_files* $line($i)] {
                    set location1 [string first \"\$\(ProjectName\)_files $line($i)]
                    set location2 [expr [string last , $line($i)] - 1]
                    if {$location1 == -1 || $location2 == -1 || $location1 >= $location2} continue
                    set path [string range $line($i) $location1 $location2]
                    if [string match -nocase *global* $path] {
                        lappend found_projects $path
                    }
                }
            }
        } else {
            for {set i 0} {$i < $len} {incr i} {
                if [string match *\"FileType\":*\"DependentFiles\":*\"Location\":*"\$\(ProjectName\)_files* $line($i)] {
                    set location1 [string first \"\$\(ProjectName\)_files $line($i)]
                    set location2 [expr [string last , $line($i)] - 1]
                    if {$location1 == -1 || $location2 == -1 || $location1 >= $location2} continue
                    set path [string range $line($i) $location1 $location2]
                    if {[string match *.agdb* $path] || [string match *.meshdat* $path] || [string match *.mshdat* $path]} {
                        lappend found_projects $path
                    }
                    if {[string match *.mshdb* $path] || [string match *.dsdb* $path] || [string match *.mechdat* $path] || [string match *.mechdb* $path]} {
                        lappend found_projects $path
                    }
                    if {[string match *.cmdb* $path] || [string match  *.pmdb* $path] || [string match *.meshdat* $path]} {
                        lappend found_projects $path
                    }
                }
            }
        }
        set projects ""
        foreach wbpath $found_projects {
            set path2 [string trimleft $wbpath \"\$\(ProjectName\)_files]
            set path [file rootname [string trimright $path2 \"]]
            set path $wb_prj_file_dir$path
            regsub -all \\\\ $path / path
            if $convert {
                set path [file dirname $path]
            }
            if [expr [file exists $path] && [file isdirectory $path]] {
                if !$convert {
                    foreach file [glob -nocomplain $path/*.prj] {
                        if [info exists added($file)] continue
                        lappend projects $file
                        set added($file) 1
                    }
                } else {
                    foreach file [glob -nocomplain $path/*.*] {
                        if [info exists added($file)] continue
                        set ext [file extension $file]
                        switch -- $ext {
                            .agdb - .meshdat - .mshdat - .mshdb - .dsdb - .cmdb - .pmdb - .mechdat - .meshdat - .mechdb {
                                lappend projects $file
                                set added($file) 1
                            }
                        }
                    }
                }
            }
        }
        if $convert {
            return $projects
        }
        if {$projects != ""} {
            if {[llength $projects] > 1} {
                set multi_paths ""
                foreach p $projects {
                    append multi_paths " [file dirname $p]"
                }
                mess "Found multiple associated ICEM CFD projects files in [llength $projects] directories: $multi_paths\n"
                cd $cur_pwd
                return
            } else {
                if $is_win {
                    set projects [win_shortname $projects]
                }
                set prj_file $projects
                uplevel \#0 [list source $prj_file]
            }
        } else {
            mess "This workbench project files has no associated ICEM CFD project file\n"
            cd $cur_pwd
            return
        }
    }
     
    set file_name(settings) $prj_file
    foreach d [array names file_name] {
	    if [string match *_dir $d] {
	        set file_name($d) [pwd]
	    }
    }

    set prj_dir [file dirname $prj_file]
    if {([pwd] != $prj_dir) && ($prj_dir != ".")} {
	    if [catch {ic_chdir $prj_dir} res] {
	        mess "Error in changing project directory to $res\n"
	        return
	    } else {
	        foreach p [array names file_name] {
		        if [string match *_dir $p] {
		            set file_name($p) $prj_dir
		        }
	        }
	        if {$file_name(project_dir) != "."} {
		        mess "Project directory changed to $file_name(project_dir)\n"
	        }
	        set file_name(settings) [file tail $file_name(settings)]
	    }
    } else {
        if {$prj_dir != "."} {
            set file_name(project_dir) $prj_dir
        }
	    mess "Project directory set to $file_name(project_dir)\n"
	}

    if {$file_name(tetin) != ""} {
        if [file exists $file_name(tetin)] {
            if [catch {ic_load_tetin $file_name(tetin) 0 1 0 $quiet} res] { 
                mess "Error in opening tetin file $file_name(tetin); $res\n" red
            } else {
                set file_name(tetin_loaded) 1
            }
        } else {
            mess "Warning: missing tetin file $file_name(tetin)\n" red
            set file_name(tetin) ""
        }
    }
    if {$file_name(domain) != ""} {
        if [file exists $file_name(domain)] {
            if [catch {ic_uns_load $file_name(domain) 3 $quiet "" 1} res] {
                mess "Error in opening domain file $file_name(domain); $res\n" red
            } else {
                set file_name(domain_loaded) 1
            }
        } else {
            mess "Warning: missing domain file $file_name(domain)\n" red
            set file_name(domain) ""
        }
    }
    
    if {$file_name(blocking) != ""} {
        if [file exists $file_name(blocking)] {
            if !$quiet { mess "Loading blocking file $file_name(blocking)\n" }
            if [catch {ic_hex_restore_blocking $file_name(blocking)} res] {
                mess "Error in opening blocking file $file_name(blocking); $res\n" red
            }
        } else {
            mess "Warning: missing blocking file $file_name(blocking)\n" red
            set file_name(blocking) ""
        }
    }
    if {$file_name(cart_file) != ""} {
        if [file exists $file_name(cart_file)] {
            if [catch {ic_cart_load $file_name(cart_file)} res] {
                mess "Error in opening cart file $file_name(cart_file); $res\n" red
            }
        } else {
            mess "Warning: missing cart file $file_name(cart_file)\n" red
            set file_name(cart_file) ""
        }
    }

    if {![info exists env(AI_ENV_CFX)] && ($file_name(solver_params) != "") && [file exists $file_name(solver_params)]} {
	    if [catch {ic_param_load $file_name(solver_params) 1} res] {
	         mess "Error in opening solver params file $file_name(solver_params); $res\n" red
	    } else {
	        set file_name(solver_params_loaded) 1
	    }
    }
    
    if {($file_name(family_boco) != "") || ($file_name(attributes) != "")} {
        if {$file_name(attributes) != "" && [file isfile $file_name(attributes)]} {
            if [catch {ic_boco_load_atr $file_name(attributes)} res] {
                mess "Error in opening attribute file $file_name(attributes); $res\n" red
            } else {
                set file_name(attributes_loaded) 1
            }
        }
        if {$file_name(family_boco) != "" && [file isfile $file_name(family_boco)]} {
            if [catch {ic_boco_load $file_name(family_boco)} res] {
	            mess "Error in opening boundary condition file $file_name(family_boco); $res\n" red
	        } else {
                set file_name(family_boco_loaded) 1
            }
        }
    } 
    set prism_parts ""
    set prism_families ""
    if {[ic_geo_is_loaded] && [array exists prism_family_on]} {
        foreach ff [ic_geo_list_parts] {
            if {[info exists prism_family_on($ff)] && $prism_family_on($ff) == 1} {
                lappend prism_families $ff
            }
        }
        if {$prism_families != ""} {
            ic_geo_set_prism_families $prism_families
        }
        set params ""
        foreach i {law height ratio layers total_height} {
            if {[info exists prism_values($i)]} {
                lappend params $i $prism_values($i)
            }
        }
        if {$params != ""} {
            eval ic_set_meshing_params prism 0 $params
        }
    }
}
set dont_record_command(ic_load_project_file) 1

## Unload the current mesh.  This doesn't save it if it hasn't been already
## saved.

proc ic_unload_mesh {{quiet 0}} {
    global uns_cur str_cur undo_redo unstruct_show_selected_map
    global projlib uns_current_edge_list
    
    busy 1
    if {$uns_cur != ""} {
        uninitialize_unstruct_visibility
        undo_on 0
	    uns_mesh_attach_family_boco 0
        $uns_cur delete
        set uns_cur ""
        if {[info exists uns_current_edge_list]} {
            unset uns_current_edge_list
        }
        ic_undo_clear
        display_update unstruct 1
        catch {unset unstruct_show_selected_map}
        erase_flood_fill_lines
        if {!$quiet} {
            mess "Unloaded unstructured domain\n"
        }
    }
    if {$str_cur != ""} {
        undo_on 0
        ic_str_smooth_glsm_clear
        struct_close_all_open_windows
        ic_str_scanp_delete ""
        ic_str_clear_diagnostics
        str_topo_clear_surfs $str_cur
        $str_cur delete
        set str_cur ""
        ic_undo_clear
        display_update struct 1
        if {!$quiet} {
            mess "Unloaded structured domains\n"
        }
    }
    fam_delete_unused_on_unload $quiet
    
    # In case there is family data and we don't need it any more, unload it now
    # Note this should go away before 5.0
    # FIXME get rid of this
    if {![info exists projlib] || ($projlib == "")} pj_free_model

    domain_clear_info
    undo_on 1
    busy 0
}

proc ic_unload_uns_mesh {} {
    global uns_cur unstruct_show_selected_map
    global uns_current_edge_list

    busy 1
    if {$uns_cur != ""} {
        uninitialize_unstruct_visibility
        uns_mesh_attach_family_boco 0
        $uns_cur delete
        set uns_cur ""
        if [info exists uns_current_edge_list] {
            unset uns_current_edge_list
        }
        ic_undo_clear
        display_update unstruct 1
        catch { unset unstruct_show_selected_map }
        erase_flood_fill_lines
    }
    busy 0
}

## General resultlib read function.  Reads the files specified by the
## file list using the reader specified by the backend.  If create
## is one, the unstruct/struct meshes will be recreated

proc ic_read_external {backend files {create 1} {prec 1} {tstep 0} args } {
    global uns_cur str_cur errorInfo batch_mode is_win 

    if {$uns_cur == "" || $create} {
	    ic_uns_create_empty
	    set unscreate 1
    } else {
	    set unscreate 0
    }
    if {$str_cur == "" || $create} {
        set str_cur [str_mesh -update_undo_cmd undo_redo_update]
        $str_cur initialize_mesh
        undo_redo_update 0 0
        set strcreate 1
    } else {
        set strcreate 0
    }
    busy 1
    catch {
        rfint__re delete       
        set errorInfo ""
    }
    rf_med_interface rfint__re
    rfint__re configure_mesh $uns_cur $str_cur
    set cmd [list rfint__re load_mesh -prec $prec -tstep $tstep $backend [list $files]]
    regsub "backend_option" $args "format" args
    set args [join $args]
    append cmd " $args"
    
    set reload_geom 0
    if {([file tail $backend] == "readcgns") || ([file tail $backend] == "readcgns.exe")} {
        if [ic_geo_is_loaded] {
            set reload_geom 1
            set temp_tetin "./temp[pid].tin"
            ic_save_tetin "$temp_tetin" 0 0 "" "" 0 1 1
            ic_unload_tetin
        }
    }
    
    mess "Running $backend [list $files] $args\n" blue
    if [catch [eval $cmd] res] {
        mess "Error: $res\n" red
        set desc ""
    } else {
        ic_delete_empty_parts
        if {[info exists errorInfo] && ($errorInfo != "" && $errorInfo != "(No Error)")} {
            if !$batch_mode { mess "$errorInfo\n" red }
            set errorInfo ""
        }
        # get information about contents of file and return number
        # unstructured nodes, number of structured nodes and on whether
        # or not we had to create a uns mesh and/or a str mesh

        set linfo [rfint__re list_info]
        set info [lindex $linfo 0]
        set desc [list [lindex $info 1] [lindex $info 0] [list $unscreate $strcreate]]
        rfint__re delete
    }
    # If we didn't need the str_mesh delete it
    if ![$str_cur num_topo_objects domain] {
        $str_cur delete
        set str_cur ""
    }
 
    if $reload_geom {
        if [file exists $temp_tetin] {
            ic_load_tetin "$temp_tetin"
            ic_rm "$temp_tetin"
        }
    }
    
    if ![info exists desc] {
        global errorInfo
        mess "$errorInfo\n" red
        ic_unload_mesh 1
        return "0 0 {0 0}"
    } else {
        if {([file tail $backend] == "readcgns") || ([file tail $backend] == "readcgns.exe")} {
            set temp_file "[file dir [lindex $args 1]]/___tmpdomain[pid]___.tcl"
            if [file exists $temp_file] {
                if ![ic_geo_is_loaded] ic_empty_tetin
                if [ic_replay "$temp_file"] {
                    mess "Updated Periodicity\n" blue
                }
                ic_rm $temp_file
            }
        }
    }
    busy reset
    mess "Done\n" blue
    return $desc
}

proc ic_rfint_re_delete {} {
    catch {
        rfint__re delete
    }
}

proc ic_delete_empty_parts {} {
    global fam_visible fam_visible_color 

    if {![ic_geo_is_loaded] && ![ic_uns_is_loaded] && ![ic_hex_blocking_loaded] && ![ic_str_is_loaded] && ![ic_boco_is_loaded]} return
    set parts [concat [ic_geo_list_families] [ic_uns_list_families] [ic_str_list_families] [ic_hex_list_families] [ic_boco_list_families] [aie_get_global_array_names fam_visible]]
    set parts [lsort -unique $parts]
    set bodies [ic_geo_get_objects body]
    set deleted ""
    foreach p [join $parts] {
        set fams [ic_geo_list_families_in_part $p]
        if {[llength $fams] == 0} { set fams $p }
        foreach f $fams {
            if [ic_geo_subset_exists $f] continue
            if {$f == "" || $f == "ORFN" || $f == "VORFN"} continue
            if {($f == "LUMP" || $f == "SHELL" || $f == "SHEET") && $bodies != ""} continue
            if {[ic_meshdirect_get_directive_of_fam $f] != ""} continue
            if {[ic_geo_get_objects all $f 1] != ""} continue
            if [ic_uns_count_in_families $f] continue
            if [ic_str_count_in_families $f] continue
            if [ic_hex_blocking_loaded] {
                if [ic_hex_subset_named_selection $f] continue
                if {[llength [ic_hex_print_topo_tree]] == "1"} {
                    if [aie_get_global use_output_blocks] {
                        if {[string trimright [eval ic_hex_get_block_families_and_types $f output_blocks] \n] != ""} continue
                    } else {
                        if {[string trimright [eval ic_hex_get_block_families_and_types $f] \n] != ""} continue
                    }
                } else {
                    continue
                }
            }
            set bocos [ic_boco_get $f]
            if {$bocos != ""} {
                if {[llength $bocos] == 1} {
                     if {![string match *color* $bocos]} continue
                } else {
                    continue
                }
            }
            # part contains body without a material point if true
            if {[lsearch [ic_geo_non_empty_families] $f] >= 0} continue
            ic_geo_delete_family $f
            ic_uns_delete_family $f
            ic_boco_clear_family $f
            lappend deleted $f
            if {[aie_get_global fam_visible $f] != "___NA___"} { aie_unset_global fam_visible $f }
            if {[aie_get_global fam_visible_color $f] != "___NA___"} { aie_unset_global fam_visible_color $f }
        }
    }
    return $deleted
}

# ---------------

## Set the maximum memory size used for CAD or mesh, in bytes.
## If <i>size</i> is "" return the current value

proc ic_set_max_map_size {what { size "" } } {
    return [set_max_map_size $what $size]
}

## Set the current values for alternate pathnames for subprograms that are
## run by icemcfd.  The <i>vals</i> argument is a list of name/file pairs.
## The names are the base names of the subprograms, such as <tt>tetra</tt>.
## The files can be either relative or absolute and don't need to include
## the <tt>.exe</tt> in the case of Windows.

proc ic_set_program_paths {vals} {
    global program_path
    catch {unset program_path}
    array set program_path $vals
}

## Set the "remove temp files" flag.  If this is 0 then various <tt>.tmp</tt>
## files will not get cleaned up.  This can help with debugging.

proc ic_set_clean_up_tmp_files {on} {
    global options
    set options(clean_up_tmp_files) $on
}

## Say what types these domain files are.  Returns a list of struct, unstruct,
## or bad.

proc ic_domain_types {args} {
    return [eval domain_types $args]
}
set dont_record_command(ic_domain_types) 1

proc ic_set_project { project } {
# there is nothing to do for this but I am including it for
# backwards compatibilty
}

proc ic_temp_dir {{dir ""}} {
    global env is_win
    
    if {$dir != ""} {
        if $is_win { set dir [win_shortname $dir] }
        if {[file exists $dir] && [file isdirectory $dir]} {
            regsub -all \\\\ $dir / dir
            set env(ICEMCFD_TEMPDIR) $dir
            if $is_win {
                set env(TEMP) $dir
                set env(TMP) $dir
            } else {
                set env(TEMPDIR) $dir
            }
            return "$dir"
         }
         mess "Unable to set temporary directory to $dir\n" red
         return ""
    }
    if {[info exists env(ICEMCFD_TEMPDIR)] && [file exists $env(ICEMCFD_TEMPDIR)] && [file isdirectory $env(ICEMCFD_TEMPDIR)]} {
        return $env(ICEMCFD_TEMPDIR)
    }
    if $is_win {
        if [info exists env(TEMP)] {
            regsub -all \\\\ $env(TEMP) / tmp
            return $tmp
        }
        if [info exists env(TMP)] {
            regsub -all \\\\ $env(TMP) / tmp
            return $tmp
        }
    } else {
        if [info exists env(TMPDIR)] {
            return $env(TMPDIR)
        }
    }
    return ""
}

# ---------------------------------------------------------

## Convert a structured set of domains to unstructured.

proc ic_convert_struct_to_unstruct {domains_dir numbers topofile famtopo \
					bocofile unsfile {options ""} {prefix ""}} {
    set dlist [cmd_tempfile domain_list]
    set fp [open $dlist w]
    foreach num $numbers {
        puts $fp $domains_dir/${prefix}domain.$num
    }
    close $fp
    
    set cmd [list -dom_list $dlist -t $topofile -o $unsfile]
    if {$bocofile != "" && [file isfile $bocofile]} { lappend cmd -b $bocofile }
    if {$famtopo != "" && [file isfile $famtopo]} { lappend cmd -f $famtopo }
    set cmd [concat $cmd $options]
    ic_run_application_exec . icemcfd/output-interfaces str_to_unstr $cmd
    cmd_rm $dlist
    if ![file exists $unsfile] {
        mess "Error: domain conversion failed\n" red
        return 0
    } else {
        mess "Structured -> unstructured conversion done\n"
        return 1
    }
}

## Convert raw domains (no topology) into an unstructured mesh

proc ic_convert_domains_to_unstruct {domains_dir {numbers ""} {unsfile ""}} {
    
    if {"" == $unsfile} { set unsfile $domains_dir/tmpdomain.uns }
    
    set tmpdomains $domains_dir/domain
    set auto_topo 0 ;# auto_topo makes it (much) slower, but get curves/fams
    ic_str_load $tmpdomains {} {} {} $numbers $auto_topo
    ic_str_topo_make_surfs domain 0
    
    set topofile $domains_dir/topo_mulcad_out.tmp
    lappend tmpfiles $topofile
    set familytopo $domains_dir/family_topo.tmp
    lappend tmpfiles $familytopo
    set familytopo $domains_dir/subface_proj.tmp
    lappend tmpfiles $subface_proj
    
    ic_str_save $domains_dir $topofile $familytopo $subface_proj
    
    set domain_list ""
    for {set i 1} {[file exists $tmpdomains.$i]} {incr i} {
        lappend tmpfiles $tmpdomains.$i
        append domain_list "$i "
    }
    
    set options "" ;#-tria
    ic_convert_struct_to_unstruct $domains_dir $domain_list $topofile {} {} $unsfile $options
    ic_unload_mesh
    display_update
    
    foreach f $tmpfiles { cmd_rm $f }
    return $unsfile
}

## Convert a structured multi-block mesh to single-block

proc ic_convert_struct_to_super {domains_dir numbers topofile \
					singledomain listfile {pref ""}} {
    set dlist [cmd_tempfile domain_list]
    set fp [open $dlist w]
    foreach num $numbers {
	puts $fp $domains_dir/${pref}domain.$num
    }
    close $fp
    
    set cmd [list -dom_list $dlist -t $topofile -o $singledomain \
		 -list $listfile]
    ic_run_application_exec . icemcfd supdomain $cmd
    cmd_rm $dlist
    if ![file exists $singledomain] {
	mess "Error: domain conversion failed\n" red
	return 0
    } else {
	mess "Structured -> superdomain conversion done\n"
	return 1
    }
}

## Return 1 if file is ascii, 0 if not.  An error message is returned if file doesn't exist,
## is a directory, or doesn't have read permissions by the current user.

proc ic_file_is_ascii {file} {
    if ![file exists $file] {return -code error "\"$file\" doesn't exist"}
    if ![file isfile $file] {return -code error "\"$file\" is not a file"}
    if ![file readable $file] {return -code error "no read permissions to \"$file\""}
    set ch [open $file r]
    fconfigure $ch -translation binary
    # No encoding translations are done when using binary translation
    # Hopefully, checking the first 1000 characters (4000 bytes) should be enough
    # Binaries should not have much comments, if any
    set txt [read $ch 1000]
    close $ch
    if ![string is ascii $txt] {return 0}
    return 1
}

proc ic_linux_certified {{ignore_enterprise_version 0}} {
    global tcl_platform

    if {$tcl_platform(platform) == "windows"} {
        return ""
    } elseif {$tcl_platform(platform) == "unix"} {
        switch -- $tcl_platform(os) {
            Linux {
                set rr [exec uname -a]
                if {[lsearch $rr "ia64"] >= 0} {
                    set plat "64"
                } elseif {[lsearch $rr "x86_64"] >= 0} {
                    set plat "64"
                } else {
                    set plat "32"
                }
                if {[file exists /etc/SuSE-release]} {
                    set file /etc/SuSE-release
                    set vendor suse
                } elseif {[file exists /etc/redhat-release]} {
                    set file /etc/redhat-release
                    set vendor redhat
                } else {
                    return ""
                }
            }
            default {
                return ""
            }
        }
        if ![info exists file] { return "" }
        set f [open $file r]
        while {![eof $f]} {
            set line [string trim [gets $f]]
            if [string match "*Enterprise*" $line] {
                set enterprise 1
            }
            if [string match "*VERSION = 10*" $line] {
                set version10 1
            }
            if [string match "*VERSION = 11*" $line] {
                set version11 1
            }
            if [string match "*PATCHLEVEL = 1*" $line] {
                if [info exists version11] {
                    set patchlevel_11_1 1
                }
            }
            if [string match "*release 5*" $line] {
                set release5 1
            }
            if [string match "*Update 3*" $line] {
                set update3 1
            }
            if [string match "*release 5.3*" $line] {
                set release5 1
                set update3 1
            }
            if [string match "*release 6.4*" $line] {
                set release6 1
                set update4 1
            }
            if [string match "*release 6.5*" $line] {
                set release6 1
                set update5 1
            }
        }
        close $f
        
        if !$ignore_enterprise_version { if ![info exists enterprise] { return "" } }
        
        switch $vendor {
            suse {
                if {[info exists version10] && $plat == "64"} {
                    return SLES10-64
                } elseif {([info exists version11] && [info exists patchlevel_11_1]) && $plat == "64"} {
                    return SLES11-64
                } else {
                    return ""
                }
            }
            redhat {
                if {[info exists release6] && ([info exists update4] || [info exists update5]) && $plat == "64"} {
                    return RHEL6
                } else {
                    return ""
                }
            }
            default {
                return ""
            }
        }
    } else {
        return ""
    }
}

proc ic_set_project_names {{names ""}} {
    if {$names == ""} return
    set_project_names $names
}
set dont_record_command(ic_set_project_names) 1

proc ic_exit_icemcfd {{exit 0}} {
    global batch_mode
    
    # in batch mode exit with status
    # exit: 0 just exit
    # exit: 1 clean up and exit with status (iemcfd standalone) 
    #         clean up, copy project files to backup directory, and exit with project status (icemcfd addin)
    # exit: 2 just exit with status 0

    if $batch_mode {
        exit $exit
    } else {
        if {$exit != "2"} {
            exitProgram $exit
        } else {
            exit 0
        }
    }
}

proc ic_save_project {settings} {
    global env file_name batch_mode

    if {$settings == ""} {
        mess "Missing project settings file\n" red
        return 0
    }
    if {[ic_wb2_is_running] && !$batch_mode} {
        set upstream [aie_get_global file_name upstream_data]
        if {$upstream != "___NA___" && [file exists $upstream]} {
            switch [aie_get_global wb_upstream_data_status] {
                1 {
                    mess "Warning: upstream data needs to be refreshed\n" red
                }
            }
        }
    }
    if [ic_wb2_input_parameter_mode] {
        set dir [aie_get_global wb_curdir]
        set name [aie_get_global wb_initProjName]
        if {$dir != "___NA___" && $name != "___NA___"} {
            set settings $dir/$name.prj
        }
    }
    
    if $batch_mode {
        if {[file exists $settings] && [file isfile $settings]} {
            source $settings
        } else {
            if [ic_wb2_is_running] {
                mess "Creating project settings file $settings ...\n"
            } else { 
                mess "Project settings file $settings does not exist\n" red
                return 0
            }
        }
    }
    ic_set_project_names $settings
    set dir [aie_get_global file_name project_dir]
    if {$dir != "___NA___" && $dir != ""} {
        if {$dir != [pwd]} {
            if [catch {ic_chdir $dir} res] {
                mess "Can't change directory: $res\n" red
                return 0
            }
        }
        if [ic_wb2_is_running] {
            set wb_curdir [aie_get_global wb_curdir]
            if {$dir != $wb_curdir} {
                aie_set_global wb_curdir "$dir"
                set bdir1 [file dir [file dir [file dir [file dir $file_name(project_dir)]]]]
                set bdir2 .[file tail [file dir [file dir [file dir $file_name(project_dir)]]]].backup
                set bdir $bdir1/$bdir2
                aie_set_global wb_backup $bdir
            }
        }
    } else {
        mess "Can't find project directory\n" red
        return 0
    }
    set ret [aie_saveProject]
    return $ret
}

proc ic_replay {file} {
    global batch_mode

    if {$file == "" || ![file exists $file]} {
        mess "Replay failed; file \"$file\" not found\n" red
        return 0
    }
    if !$batch_mode { busy 1 }
    if [catch {source $file} res] {
        mess "Replay failed; $res\n" red
        if !$batch_mode { busy reset }
        return 0
	} 
	if !$batch_mode {
	    ic_gui_update
	    busy reset
	}
	return 1
}

proc ic_get_ic_functions {type} {
    set functions ""
    foreach i [info procs ic_${type}_*] {
        if [replay_filter $i] {
            lappend functions $i
        }
    }
    return $functions
}

proc ic_load {file {quiet 0} {arg ""}} {
    global batch_mode env
    
    if ![file exists $file] {
        return 0
    }
    set ext [file extension $file]
    if {$ext == ".prj"} {
        if [catch { ic_load_project_file $file 0 $quiet } res] {
            return 0
        }
    } elseif {$ext == ".tin"} {
        if [ic_geo_is_loaded] { ic_unload_tetin $quiet }
        if [catch { ic_load_tetin "$file" 0 1 0 $quiet } res] {
            return 0
        }
        if [info exists env(AI_ENV_IN_WB1)] {
            if {[ic_get_global wb1_options subset2parts] == "1"} {
                mess "Creating parts from subsets...\n" blue
                ic_geo_subset_names_to_parts
            }
        }
    } elseif {$ext == ".uns"} {
        if [ic_uns_is_loaded] { ic_unload_mesh $quiet }
        if [catch { ic_uns_load "$file" 3 $quiet } res] {
            return 0
        }
    } elseif {$ext == ".blk"} {
        if [ic_hex_blocking_loaded] { ic_hex_unload_blocking }
        if {$arg == ""} {
            if [catch { ic_hex_restore_blocking "$file"} res] {
                return 0
            }
        } else {
            if [catch { ic_hex_restore_blocking "$file" "$arg"} res] {
                return 0
            }
        }
        if [info exists env(AI_ENV_IN_WB1)] {
            if {[ic_get_global wb1_options updatebunching] == "1"} {
                mess "Updating blocking sizes from geometry...\n" blue
                ic_hex_set_mesh_params -version 110
            }
        }
    } else {
        return 0
    }
    if !$batch_mode {
        ic_gui_update
    }
    return 1
}

proc ic_save {file {quiet 0}} {
    global env

    set ext [file extension $file]
    if {$ext == ".prj"} {
        if [catch { ic_save_project "$file" } res] {
            return 0
        }
    } elseif {$ext == ".tin"} {
        if [ic_geo_is_loaded] {
            if [catch { ic_save_tetin "$file" 0 0 "" "" 0 $quiet } res] {
                return 0
            }
        } else {
            return 0
        }
    } elseif {$ext == ".uns"} {
        if [ic_uns_is_loaded] {
            if [info exists env(AI_ENV_IN_WB1)] {
                if {[ic_get_global wb1_options lin2quad] == "1"} {
                    mess "Converting elements from linear to quadratic...\n" blue
                    ic_change_linear_quadratic iters 10 project 1 max_dev_half 0.1666666 check_max_dev_half 0 max_dev_nproj 30.0 check_max_dev_nproj 0 \
                        max_dev_chord 35.0 check_max_dev_chord 0 check_tet10_standard 0 check_tet10_strong 0 check_proj 0 max_leap 3.5 midface 0 \
                        create_interf 0 refine 0 smooth_refine 0 calc_projection 1 calc_projection_tol 0.001 all_elem 1 name {}
                }
            }
            if [catch { ic_save_unstruct "$file" 1 "" "" "" "" 0 $quiet } res] {
                return 0
            }
        } else {
            return 0
        }
    } elseif {$ext == ".blk"} {
        if [ic_hex_blocking_loaded] { 
            if [catch { ic_hex_save_blocking "$file" "" } res] {
                return 0
            }
        } else {
            return 0
        }
    } else {
        return 0
    }
    return 1
}
