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

proc layer_fam_list {how} {
    global uns_cur
    switch $how {
	active {
	    set sel [$uns_cur get_map Selected]
	    return [lindex [$sel get_family_type] 0]
	}
	all {
	    return [$uns_cur list_families]
	}
	default {
	    # added this so that i can specify to ic_uns_subset_add_layer
	    # an explicit list of families --jka
	    return $how
	}
    }
    return $how
}

proc layer_type_list {how} {
    global uns_cur
    switch $how {
	active {
	    set sel [$uns_cur get_map Selected]
	    return [lindex [$sel get_family_type] 1]
	}
	all {
	    return [$uns_cur list_types]
	}
	surf {
	    return "NODE BAR_2 BAR_3 TRI_3 TRI_6 QUAD_4 QUAD_8 QUAD_9 LINE_2 LINE_3"
	}
	default {
	    # added this so that i can specify to ic_uns_subset_add_layer
	    # an explicit list of types --jka
	    return $how
	}
    }
    return $how
}

proc ic_smooth_decide_post_smoother {} {
    if { [ic_count_elements HEXA_8] && ![ic_count_elements TETRA_4] } {
        return 1
    } else {
        return 0 
    }
}

proc domain_after_load_mesh {{quiet 0} {check_orientation 1}} {
    global uns_cur batch_mode
    global tdv_default
    global ai_environment

    if [ic_geo_is_loaded] {
        set fams [ $uns_cur list_families ]
        foreach f $fams {
            if { ! [ ic_geo_check_family $f ] } {
                ic_geo_new_family $f
            }
        }
    }

    if {$check_orientation == 1} {
	set res [[$uns_cur get_map All] check_orientation 1 NULL "" 0]
    } elseif {$check_orientation == 2} {
	set res [[$uns_cur get_map All] check_orientation 1 NULL CREATED_FACES 0]
    } else {
	if {!$quiet} {
	    mess "skipping orientation check\n"
	}
	set res ""
    }
    set modified 0
    if {$res != ""} {
        if {[string match "*problems with open faces fixed*" $res]} {
            set modified 1
            mess "Problems with open faces fixed.\n"
        }
        if {![string match "*faces are correctly oriented*" $res]} {
            mess "Checking orientation:\n" red
            print_orientation_messages $res
        }
    }

    if { !$quiet && !([info exists ai_environment] && $ai_environment) } {
        unstruct_summary $uns_cur 100
    }

    foreach im [$uns_cur list_maps] {
	if ![$im get -dont_change_color_or_shade] {
	    $im set_pid_colors
	}
        if !$batch_mode {
            $im configure -visible 0
            $tdv_default add_image $im
        }
    }
    set sm [$uns_cur get_map Selected]
    $uns_cur set_visible_map $sm
    $sm configure -visible 1

    $uns_cur configure -modified $modified
    undo_on 1
    initialize_unstruct_visibility
}

proc print_orientation_messages {res} {
    set np 0
    foreach r $res {
	mess "\t$r"
	incr np
	if {$np > 64} {
	    set file [cmd_tempfile ERROR_LOG .tmp]
	    set fp [open $file w]
	    puts $fp $res
	    close $fp
	    mess "[expr [llength $res] - 64] more messages - not printed\n" red
	    mess "All messages saved to file $file\n" red
	    break
	}
    }
}

proc unstruct_summary {{uns ""} {maxfams 0} {calc_min_max 0} {subset_name1 "max_edge_sides"} {subset_name2 "min_edge_sides"}} {
    global uns_cur
    global ai_environment

    set subset1 ""
    set subset2 ""
    if {$uns == ""} {
	    set uns $uns_cur
    }
    mess "\n--- Mesh Info ---\n"
    mess "Element types :\n"
    foreach t [$uns list_types] {
	    mess "        $t : [$uns count $t]\n"
    }

    if { ([info exists ai_environment] && $ai_environment) } {
	    mess "Element parts :\n"
	    # First look up the counts.
	    foreach {f c} [$uns count_in_family] {
	        set p [lindex [split $f :] 0]
            set pp [lindex [split $f :] 1]
            if {$pp != "" && $f != $pp && $c} {
                if [info exists counts_comp($p)] {
                    incr counts_comp($p) 
                } else {
                    set counts_comp($p) 1
                }
            }
	        if [info exists counts($p)] {
		        incr counts($p) $c
	        } else {
		        set counts($p) $c
	        }
	    }
	    set i 0
	    foreach p [lsort [array names counts]] {
	        if [info exists counts_comp($p)] {
	            if $counts($p) {
		            mess "        $p : $counts($p) (contains $counts_comp($p) hidden component part(s))\n"
	            }
	        } else {
	            if $counts($p) {
		            mess "        $p : $counts($p)\n"
	            }
	        }
	        incr i
	        if {[expr $i % 50] == 0} update
	    }
    } else {
	    set fams [lsort [$uns list_families]]
	    set nfams [ llength $fams ]
	    if {!$maxfams || ($nfams <= $maxfams)} {
	        mess "Element families :\n"
	        set counts [eval $uns count_in_family $fams]
	        for { set i 0 } { $i < $nfams } { incr i } {
		        set n [ lindex $counts $i ] 
		        set f [ lindex $fams $i ]
		        if $n {
		            mess "        $f : $n\n"
		        }
	        }
	    } else {
	        mess "$nfams Element families, not listing\n"
	    }
    }
    if $calc_min_max {
	set min_max_info [$uns get_min_max_side 0]
	if {$min_max_info != ""} {
	    set nn [lrange $min_max_info 1 end]  
	    if {$nn != ""} {
		set mi [lindex $min_max_info 0]
		mess "Maximum edge sides $mi\n"
		set subset1 $subset_name1
		if [ic_uns_subset_exists $subset1] {
		    for {set i 0} {1} {incr i} {
			set subset1 "${subset_name1}_${i}"
			if {![ic_uns_subset_exists $subset1]} { break }
		    }
		}
		set nodessubset [ic_uns_subset_create $subset1 1]
		ic_uns_subset_add_node_numbers $nodessubset $nn
		mess "Nodes are in subset $subset1\n"
	    }
	}
	set min_max_info [$uns get_min_max_side 1]
	if {$min_max_info != ""} {
	    set nn [lrange $min_max_info 1 end]  
	    if {$nn != ""} {
		set mi [lindex $min_max_info 0]
		mess "Minimum edge sides $mi\n"
		set subset2 $subset_name2
		if [ic_uns_subset_exists $subset2] {
		    for {set i 0} {1} {incr i} {
			set subset2 "${subset_name2}_${i}"
			if {![ic_uns_subset_exists $subset2]} { break }
		    }
		}
		set nodessubset [ic_uns_subset_create $subset2 1]
		ic_uns_subset_add_node_numbers $nodessubset $nn
		mess "Nodes are in subset $subset2\n"
	    }
	}
    }
    mess "Total elements : [$uns count]\n"
    mess "Total nodes : [$uns count nodes]\n"
    mess "Min : [lindex [$uns get_bbox] 0]\n"
    mess "Max : [lindex [$uns get_bbox] 1]\n"
    mess \n
    return [list $subset1 $subset2]
}

proc make_standard_maps { uns } {
    global ai_environment
    
    set m [$uns get_map All]
    if {$m != ""} { $m delete }
    uns_map -mesh $uns -list_type 0 \
	-name All -visible 0 -editable 0

    
    set m [$uns get_map Selected]
    if {$m != ""} { $m delete }
    uns_map -mesh $uns -name Selected -visible 1 \
	-update_family_type 1 -list_type 1 -editable 0

    if {![info exists ai_environment] || ($ai_environment == 0)} {
	if {[$uns get_map Subset0] == ""} {
	    uns_map -mesh $uns -name Subset0 -visible 0 \
		-reset_first 1 -editable 1 -list_type 2
	}
    }
}
proc make_new_uns_mesh {dfile} {
    global undo_redo
    global uns_cur
    
    if ![check_mesh_functions_enabled] {
	license_unavailable_warning
	return ""
    }
    
    set uns [uns_mesh -filename $dfile]
    set uns_cur $uns
    uns_mesh_attach_family_boco 1
    
    return $uns
}

proc uns_mesh_attach_family_boco {on} {
    global uns_cur famboco_obj
    if {($uns_cur != "") && ($famboco_obj != "")} {
	$uns_cur set_family_boco_callbacks $famboco_obj $on
    }
}

proc domain_save_unstruct {mesh dfile {typevar ""} {famvar ""}
			   {inc_maps 0} {resnames ""} {surfs_near_vols 0}
			   {only_map ""}} {
    global undo_redo
    
    set maps ""
    if $inc_maps {
	foreach m [$mesh list_maps] {
	    if {([$m get -list_type] != 2) || [$m get -reset_first] || \
		    [$m get -update_family_type] || \
		    ([$m get -name] == "")} continue
	    lappend maps $m
	}
    }
    set mark 0
    if {$only_map != ""} {
	[$mesh get_map All] mark_elements 0
	foreach om $only_map {
	    $om mark_elements 1
	}
	set mark 1
    }
    if [catch {
	$mesh save_domain $dfile $maps $typevar $famvar $resnames \
	    $surfs_near_vols $mark
    } res] {
	user_error $res
    } else {
	$mesh configure -modified 0 -filename $dfile
    }
    ic_undo_clear
}

proc setup_pid_colors {map} {
    $map set_pid_colors
}

proc start_new_elems {{no_sw_tdv_active 0}} {
    global undo_redo uns_cur batch_mode
    global tdv_default
    
    # in case it doesn't exist already
    if 0 {
	ic_uns_create_empty
	# don't do this becuase the quad functions are used for geom too
	# and we don't want any uns_cur
    } else {
	if ![ic_uns_is_loaded] { return }	
    }    
    
    if [info exists undo_redo] {
	if { !$no_sw_tdv_active } {
	    tdv_active 0
	}
	[$uns_cur get_map visible] start_collecting_new_elements $undo_redo
    }
}

proc finish_new_elems {} {
    global undo_redo uns_cur batch_mode
    global tdv_default
    
    if ![ic_uns_is_loaded] { return }
    
    if [info exists undo_redo] {
	[$uns_cur get_map visible] finish_collecting_new_elements $undo_redo
	tdv_active 1
    }
}

proc big_uns_op {do_save cmd {reset_verts 0} {no_sw_tdv_active 0}} {
    global batch_mode
    global tdv_default
    
    set pa 0
    if { !$no_sw_tdv_active } {
	set pa [tdv_active 0]
    }
    if ![ic_undo_suspended] {
	if $do_save {
	    newop_save 1 $reset_verts
	} else {
	    ic_newop
	}
	start_new_elems $no_sw_tdv_active
	if $do_save {
	    undo_on 0
	}
    }
    busy 1
    if [catch {uplevel 1 $cmd} res] {
	user_error "Internal error: $res"
    }
    busy 0
    
    if {($do_save && [ic_undo_suspended]) || \
        (![ic_undo_suspended])} {
	if $do_save {
	    undo_on 1
	}
	finish_new_elems
    }
    if { !$no_sw_tdv_active } {
	tdv_active $pa
    }
}

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

proc ensure_unstruct_mesh_exists {} {
    global projlib uns_cur batch_mode
    global tdv_default ai_environment
    
    if {$uns_cur != ""} { return 0 }
    set uns [make_new_uns_mesh "unstruct_mesh"]
    make_standard_maps $uns_cur
    if {$uns == ""} { return 0 }
    if {![info exists ai_environment] || ($ai_environment == 0)} {
	uns_map -mesh $uns -name Subset0 -visible 0 -reset_first 1 -editable 1 \
	    -list_type 2
    }
    $uns initialize_mesh
    if 0 {
	not necessary
	if {$projlib != ""} {
	    foreach f [$projlib list_families 0] {
		$uns create_family $f -1
	    }
	}
    }
    if !$batch_mode {
        foreach im [$uns list_maps] {
	    $im set_pid_colors
	    $tdv_default add_image $im
        }
        initialize_unstruct_visibility
        display_update unstruct
    }
    return 1
}

proc uns_subset_maps {mesh} {
    set ret ""
    foreach m [$mesh list_maps] {
	set n [$m get -name]
	if {($n != "") && ([$m get -list_type] == 2) && \
		([$m get -name] != "Selected")} {
	    lappend ret $m
	}
    }
    return $ret
}

proc domain_list_all_selections {} {
    global uns_cur
    
    set ret ""
    foreach m [$uns_cur list_maps] {
	if {([$m get -name] != "") && ([$m get -list_type] == 2)} {
	    lappend ret $m
	}
    }
    return $ret
}

proc uns_get_subset {name} {
    global uns_cur
    
    if {"" != $uns_cur} {
	return [$uns_cur get_map $name]
    }
    return ""
}

# Return the list_type == 2 subsets

proc uns_all_subsets {} {
    global uns_cur
    if {$uns_cur == ""} { return "" }
    set ret ""
    foreach map [$uns_cur list_maps] {
	if {([$map get -list_type] == 2) && [$map get -editable] && ![string match uns_sel_* [$map get -name]]} {
	    lappend ret $map
	}
    }
    return $ret
}

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

array set sub_custom_metric {
    warpage,name "Max warp"
    warpage,active 1
    warpage,quad,acc_min  0
    warpage,quad,des_min  0
    warpage,quad,id       0
    warpage,quad,des_max 10
    warpage,quad,acc_max 30
    warpage,tri,acc_min  -1
    warpage,tri,des_min  -1
    warpage,tri,id   -1
    warpage,tri,des_max  -1
    warpage,tri,acc_max  -1
    
    aspect,name "Aspect ratio"
    aspect,active 1
    aspect,quad,acc_min 0.1 
    aspect,quad,des_min 0.3
    aspect,quad,id      1
    aspect,quad,des_max 1
    aspect,quad,acc_max 1
    aspect,tri,acc_min  0.1
    aspect,tri,des_min  0.3
    aspect,tri,id       1
    aspect,tri,des_max  1
    aspect,tri,acc_max  1
    
    distortion,name "Distortion"
    distortion,active 1
    distortion,quad,acc_min 0.4
    distortion,quad,des_min 0.7
    distortion,quad,id      1
    distortion,quad,des_max 1
    distortion,quad,acc_max 1
    distortion,tri,acc_min -1
    distortion,tri,des_min -1
    distortion,tri,id      -1
    distortion,tri,des_max -1
    distortion,tri,acc_max -1
    
    skew,name "Skew"
    skew,active 1
    skew,quad,acc_min  0
    skew,quad,des_min  0
    skew,quad,id       0
    skew,quad,des_max 45
    skew,quad,acc_max 60
    skew,tri,acc_min   0
    skew,tri,des_min   0
    skew,tri,id        0
    skew,tri,des_max  45
    skew,tri,acc_max  60
    
    angle,name "Angle"
    angle,active 1
    angle,quad,acc_min  30
    angle,quad,des_min  45
    angle,quad,id       90
    angle,quad,des_max 135
    angle,quad,acc_max 150
    angle,tri,acc_min   10
    angle,tri,des_min   30
    angle,tri,id        60
    angle,tri,des_max  120
    angle,tri,acc_max  140
    
    minside,name "Min side"
    minside,active 0
    minside,quad,acc_min  4.0
    minside,quad,des_min  4.5
    minside,quad,id       5.0
    minside,quad,des_max  5.5
    minside,quad,acc_max  6.0
    minside,tri,acc_min   4.0
    minside,tri,des_min   4.5
    minside,tri,id        5.0
    minside,tri,des_max   5.5
    minside,tri,acc_max   6.0
    
    surfdev,name "Surface dev"
    surfdev,active 0
    surfdev,quad,acc_min  0
    surfdev,quad,des_min  0
    surfdev,quad,id       0
    surfdev,quad,des_max  2
    surfdev,quad,acc_max  3
    surfdev,tri,acc_min   0
    surfdev,tri,des_min   0
    surfdev,tri,id        0
    surfdev,tri,des_max   2
    surfdev,tri,acc_max   3
}    

proc ic_uns_sub_custom_metric_keys {} {
    return [list angle distortion aspect warpage skew minside surfdev]
}

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

array set sub_diagnostic_data {  
    
    duplicate,def_check   1
    duplicate,desc     duplicate
    duplicate,opts     {select addselect fix}
    duplicate,howfix   "deleting the duplicate elements"
    duplicate,fixfunc  ic_uns_delete_contents
    duplicate,type     derror
    duplicate,verbose  "Duplicate elements"
    
    multiple,def_check    1
    multiple,desc     multiple-edge
    multiple,opts     {select addselect}
    multiple,howfix   ""
    multiple,fixfunc  ""
    multiple,type     dprob
    multiple,verbose  "Multiple edges"
    
    triangle_box,def_check   1
    triangle_box,desc     triangle-box
    triangle_box,opts     {select addselect fix}
    triangle_box,howfix   "replacing the triangles with a tetra"
    triangle_box,fixfunc  diag_replace_triangles_with_tetra
    triangle_box,type     dprob
    triangle_box,verbose  "Triangle boxes"
    
    single_2,def_check    0
    single_2,desc      single/2
    single_2,opts      {delete select addselect}
    single_2,howfix    ""
    single_2,fixfunc   ""
    single_2,type      dprob
    single_2,verbose   "2-Single edges"
    
    single_multiple,def_check   0
    single_multiple,desc     single/multiple,
    single_multiple,opts     {delete select addselect}
    single_multiple,howfix   ""
    single_multiple,fixfunc  ""
    single_multiple,type     dprob
    single_multiple,verbose  "Single-Multiple edges"
    
    standalone,def_check    0
    standalone,desc     standalone
    standalone,opts     {delete select addselect}
    standalone,howfix   ""
    standalone,fixfunc  ""
    standalone,type     dprob
    standalone,verbose  "Stand-alone surface mesh"
    
    single,def_check    1
    single,desc     single-edge
    single,opts     {delete select addselect}
    single,howfix   ""
    single,fixfunc  ""
    single,type     dprob
    single,verbose  "Single edges"
    
    delaunay,def_check  0
    delaunay,desc   Delaunay-violating
    delaunay,opts   {select addselect}
    delaunay,howfix   ""
    delaunay,fixfunc  ""
    delaunay,type     dprob
    delaunay,verbose  "Delaunay-violation"
    
    overlap,def_check    0
    overlap,desc     overlapping
    overlap,opts     {select addselect fix}
    overlap,howfix   "trying to remesh the element regions"
    overlap,fixfunc  ic_uns_fix_subset_by_remeshing
    overlap,type     dprob
    overlap,verbose  "Overlapping elements"
    
    3_surface_node_internal_faces,def_check  0
    3_surface_node_internal_faces,desc   corner
    3_surface_node_internal_faces,opts   {select addselect fix}
    3_surface_node_internal_faces,howfix   ""
    3_surface_node_internal_faces,fixfunc  ""
    3_surface_node_internal_faces,type     ""
    3_surface_node_internal_faces,verbose  "Three surface node internal faces"
    
    missing_internal,def_check    1
    missing_internal,desc     missing-internal
    missing_internal,opts     {select addselect fix}
    missing_internal,howfix   "adding the missing faces"
    missing_internal,fixfunc  diag_fix_missing_internal_faces
    missing_internal,type     derror
    missing_internal,verbose  "Missing internal faces"
    
    periodic_problem,def_check    0
    periodic_problem,desc     "bad periodic"
    periodic_problem,opts     {select addselect}
    periodic_problem,howfix   ""
    periodic_problem,fixfunc  ""
    periodic_problem,type     derror
    periodic_problem,verbose  "Periodic problems"
    
    bound_edge,def_check    0
    bound_edge,desc     "boundary"
    bound_edge,opts     {select addselect}
    bound_edge,howfix   ""
    bound_edge,fixfunc  ""
    bound_edge,type     ""
    bound_edge,verbose  ""
    
    nmanvert,def_check    1
    nmanvert,desc     "non-manifold vertex"
    nmanvert,opts     {select addselect fix}
    nmanvert,howfix   "splitting the vertices in two"
    nmanvert,fixfunc  diag_fix_nmanvert
    nmanvert,type     dprob
    nmanvert,verbose  "Non-manifold vertices"
    
    disconnected_vert,def_check    1
    disconnected_vert,desc     "disconnected vertices"
    disconnected_vert,opts     {delete select addselect}
    disconnected_vert,howfix   ""
    disconnected_vert,fixfunc  ""
    disconnected_vert,type     dprob
    disconnected_vert,verbose  "Unconnected vertices"
    
    vol_orient,def_check    1
    vol_orient,desc     "misoriented volume elements"
    vol_orient,opts     {select addselect fix}
    vol_orient,howfix   "reorienting problem elements"
    vol_orient,fixfunc  diag_fix_volume_orientation
    vol_orient,type     derror
    vol_orient,verbose  "Volume orientations"
    
    surf_orient,def_check    1
    surf_orient,desc     ""
    surf_orient,opts     {select addselect fix}
    surf_orient,howfix   ""
    surf_orient,fixfunc  ""
    surf_orient,type     derror
    surf_orient,verbose  "Surface orientations" 
    
    hanging,def_check 1
    hanging,desc      "hanging"
    hanging,opts      {select addselect fix}
    hanging,howfix    delete
    hanging,fixfunc   ic_uns_delete_contents
    hanging,type      derror
    hanging,verbose   "Hanging elements"   
    
    penetrating,def_check 0
    penetrating,desc      "penetrating"
    penetrating,opts      {select addselect}
    penetrating,howfix    ""
    penetrating,fixfunc   ""
    penetrating,type      derror
    penetrating,verbose   "Penetrating elements" 
    
    disconnbars,def_check 0
    disconnbars,desc      "disconnected bar"
    disconnbars,opts      {select addselect fix}
    disconnbars,howfix    delete
    disconnbars,fixfunc   ic_uns_delete_contents
    disconnbars,type      derror
    disconnbars,verbose   "Disconnected bar elements"
}

proc ic_uns_sub_get_diagnotic_info {diagnostic param} {
    global sub_diagnostic_data
    
    if ![info exist sub_diagnostic_data($diagnostic,$param)] {
	mess "Error: no param \"$param\" defined for diagnostic \"$diagnostic\"\n" red
	return ""
    }
    
    return $sub_diagnostic_data($diagnostic,$param)
}

proc ic_uns_sub_set_diagnotic_info {diagnostic param val} {
    global sub_diagnostic_data
    
    if ![info exist sub_diagnostic_data($diagnostic,$param)] {
	mess "Warning: no such param \"$param\" already exists for diagnostic \"$diagnostic\"\n" red      
    }
    
    set sub_diagnostic_data($diagnostic,$param) $val
}
