
# this routine puts the data into different categories according to the 
# data type of parameter associated with each object. This is done before 
# the user edits what values to export
proc flotherm_data_equivalent { title } {
    array set flo2ice [list "Geometry Model" name "Type" ignore "Level" ignore "Status" ignore "X Position  (m)" xs "Y Position  (m)" ys "Z Position  (m)" zs "X Size (m)" xd "Y Size (m)" yd "Z Size (m)" zd "Collapsed" ignore "Block Correction" ignore "Heat Applied (W)" ignore]
    
    return $flo2ice([set title])
}

proc default_csv_output_params {} {
    global csv_output_params object_type_title object_params
    global export_ob_sh shape_title

    busy 1
    
    foreach {key value} [array get  export_ob_sh] {
	if  {[llength $key] == 3} {	    
	    set name_list ""
	    # want to find what are all the object parameter associated with things with this key    
	    foreach v $value {
		foreach {vn vv} $v {
		    if {[lsearch $name_list $vn] == -1} {
			lappend name_list $vn
		    } 
		}
	    }
	    set num_vars [llength $name_list]
	    set var_list ""
	    set groups ""
	    if {[array exists group_list]} {		    
		unset group_list
	    }
	    
	    foreach name $name_list {
		set name_without_units [string trimright $name "_units"]
		set this_index [lsearch -regexp $object_params([lindex $key 0]) $name_without_units]
		# if there are units, need to export these too
		set this_var ""
		

		if {$this_index != -1} {			
		    set this_datatype [lindex $object_params([lindex $key 0]) $this_index]
		    set this_var [lindex $this_datatype 6]
		} 
		
		# does it have a data type associated with this type
		if {$this_var != ""} {			
		    
		    if {[info exists csv_output_params([lindex $key 0],[lindex $key 1],$this_var)]} { 
			set group_list(${this_var}) [set csv_output_params([lindex $key 0],[lindex $key 1],$this_var)]
		    } else  {
			
			set csv_output_params([lindex $key 0],[lindex $key 1],$this_var,active) 0
			if {$this_var == "General"} {
			    set csv_output_params([lindex $key 0],[lindex $key 1],$this_var,active) 1				
			}
			set csv_output_params([lindex $key 0],[lindex $key 1],$this_var) ""
			lappend groups $this_var
		    }			
		    
		    lappend group_list(${this_var}) $name
		    set csv_output_params([lindex $key 0],[lindex $key 1],$this_var) [set group_list(${this_var})]
		}
	    }	    	    
	}	
    }
    busy 0
}


proc csv_export_simple_object_parameters { o } {
    global csv_text_data export_ob_sh object_edit_info

    #    set bad_shape_types [list polygon none container pipe ]    
    set bad_shape_types [list none container pipe ]    
    set type [$o getval obtype]
    set genus [$o getval current_genus ]
    # what is the shape that goes with it?
    # want to use  the genus to get the correct sect_info list
    foreach sect_list $object_edit_info($type) {
	if {[lindex $sect_list 0] == [$o getval current_genus ]} {
	    set sect_info [lrange $sect_list 2 end]
	}
    }
    
    if {[lsearch $bad_shape_types [$o getval current_stype]] != -1} {
	mess "$object_type_title([$o getval obtype]) of shape type '[$o getval current_stype]' not exported by this function\n" red
    } else {
	if {$sect_info != "check_object"} {
	    # what is the shape associated with this object?
	    # will be only one since not a check_object....
	    # HERE!***
	    set shapes [db_shapes $o]
	    set c_section [$o getval current_section]
	    set c_shape [$o getval ${c_section}_shape]
	    
	    set csv_text_data(s_type) [$o getval current_stype]
	    # what are the properties of this shape?
	    # want name, and the 
	    set ob_props [ list name [$o getval name]]
	    foreach p [$o list_values_with_units] {

		if {[llength $p ] == 3 } {
		    lappend ob_props [lindex $p 0] [lindex $p 1]
		    lappend ob_props [lindex $p 0]_units [lindex $p 2]
		} else {
		    lappend ob_props [lindex $p 0] [lindex $p 1]
		}		
	    }	
	    
	    # now want to output to the data file
	    set key [list $type $csv_text_data(s_type) [list Object Parameters]]
	    if {[info exists export_ob_sh($key)]} {		    
		# for polygonal shapes the data doesn't have the same ordering
		# since top and bottom points alternate in the list
		set old_list $export_ob_sh($key)
		set export_ob_sh($key) [ lappend old_list $ob_props]
	    } else {		    
		set export_ob_sh($key) [ list $ob_props]
	    }	
	} else {
	    set genus_text ""
	    if {$genus!="default"} { set genus_text $genus }
	    mess "Objects of type '$genus_text $type' have complex geometry and are not exported by this function\n" red 		    
	}
    }
    
}

proc csv_export_simple_shape_geometry { o } {
    global csv_text_data export_ob_sh object_edit_info
    #    set bad_shape_types [list polygon none container pipe ]    
    set bad_shape_types [list none container pipe ]    
    set type [$o getval obtype]
    set genus [$o getval current_genus ]
    # what is the shape that goes with it?
    # want to use the genus to get the correct sect_info list
    foreach sect_list $object_edit_info($type) {
	if {[lindex $sect_list 0] == [$o getval current_genus ]} {
	    set sect_info [lrange $sect_list 2 end]
	}
    }
    
    if {[lsearch $bad_shape_types [$o getval current_stype]] != -1} {
	mess "$object_type_title([$o getval obtype]) of shape type '[$o getval current_stype]' not exported by this function\n" red
    } else {
	if {$sect_info != "check_object"} {
	    # what is the shape associated with this object?
	    # will be only one since not a check_object....
	    # HERE!***
	    set shapes [db_shapes $o]
	    set c_section [$o getval current_section]
	    set c_shape [$o getval ${c_section}_shape]
	    
	    set csv_text_data(s_type) [$o getval current_stype]
	    # what are the properties of this shape?
	    # want name, and the 
	    set shape_props [ list name [$o getval name]]
	    foreach p [$c_shape list_values canset] {
		if {[llength [lindex $p 1 ]] == 1 || [string first "vert" [lindex $p 0]] == 0 || [string first "tvert" [lindex $p 0]] == 0} {
		    lappend shape_props [lindex $p 0] [lindex $p 1]
		    if { $csv_text_data(s_type) == "polygon" && \
			     [string first "vert" [lindex $p 0]] == 0 && \
			     [$c_shape si_value changes] == 0 } {
			lappend shape_props t[lindex ${p} 0] {}
		    }
		    
		}
	    }	
	    
	    # now want to output to the data file
	    set key [list $type $csv_text_data(s_type)]
	    if {[info exists export_ob_sh($key)]} {		    
		# for polygonal shapes the data doesn't have the same ordering
		# since top and bottom points alternate in the list
		set old_list $export_ob_sh($key)
		set export_ob_sh($key) [ lappend old_list $shape_props]
	    } else {		    
		set export_ob_sh($key) [ list $shape_props]
	    }	
	} else {
	    set genus_text ""
	    if {$genus!="default"} { set genus_text $genus }
	    mess "Objects of type '$genus_text $type' have complex geometry and are not exported by this function\n" red 		    
	}
    }
    
}



proc csv_export_object_geometry { file } {
    global object_edit_info csv_text_data object_type_title export_ob_sh
    global csv_output_params shape_title
     
    set fo [open $file w]
    # now write to the file with the separator of the user's choice
    
    # for each item in the list, a new row
    if [array exists export_ob_sh] {
	foreach {key value} [array get export_ob_sh] {
	    # the first part of key is the obtype, the second is the shape type
	    # if the corresponding $object_type_title($ob_type)
	    # or t
	    if {([llength $key] == 3) && ($csv_text_data(geom_only) == 0)} {
		# this is the object data
		puts $fo [list \# $object_type_title([lindex $key 0]) $shape_title([lindex $key 1]) Object ]
		
		# value contains param name/value pairs
		# here I output the param names to the file
		set split_char ""
		# generally for shapes of the same type they have the same 
		# num of data name/val pairs to output BUT for polygons, 
		# it depends on nverts - choose longest list to output
		# all have added tvert if changes == 0, so don't need 
		# to check for presence of these
		
		# FIX OBJECTS of the same type do not have this as true - eg sides may be on for some objs, but not for others of the same type
		set overall_name_list ""
		# want to find what are all the object parameter associated with things with this key    
		foreach v $value {
		    foreach {vn vv} $v {
			if {[lsearch $overall_name_list $vn] == -1} {
			    lappend overall_name_list $vn
			} 
		    }
		}
		# want to reduce the number of parameters to be output by 
		# resetting CSV_OUTPUT_PARAMS
		# for each key, write a list of column headers
		set obtype [lindex $key 0]
		csv_save_object_titles $fo $key $overall_name_list $obtype
		csv_save_objects $fo $key $overall_name_list
		#		choose_csv_output_params $key $overall_name_list $fo
		
	    } elseif  {([llength $key] == 2) } {
		puts $fo [list \# $object_type_title([lindex $key 0]) $shape_title([lindex $key 1])]
		puts $fo "\#"		
		
		# value contains param name/value pairs
		# here I output the param names to the file
		set split_char ""
		# generally for shapes of the same type they have the same 
		# num of data name/val pairs to output BUT for polygons, 
		# it depends on nverts - choose longest list to output
		# all have added tvert if changes == 0, so don't need 
		# to check for presence of these
		
		# FIX OBJECTS of the same type do not have this as true - eg sides may be on for some objs, but not for others of the same type
		set overall_name_list ""
		# want to find what are all the object parameter associated with things with this key    
		foreach v $value {
		    foreach {vn vv} $v {
			if {[lsearch $overall_name_list $vn] == -1} {
			    lappend overall_name_list $vn
			} 
		    }
		}

		foreach {t} $overall_name_list {
		    
		    puts -nonewline $fo "$split_char$t"
		    set split_char $csv_text_data(sep_char)
		    
		}
		
		# a new line here
		puts $fo ""

		
		foreach val $value {
		    set split_char ""
		    foreach nv $overall_name_list {
			set index_val [lsearch $val $nv]
			
			if {$index_val != -1} {
			    puts -nonewline $fo "$split_char[lindex $val [expr $index_val+1]]"
			    set split_char $csv_text_data(sep_char)
			} else {
			    puts -nonewline $fo "$split_char"
			}
			
		    }
		    # a new line here
		    puts $fo ""
		}
		
		# a new line here to split up the groups of object/shape types
		puts $fo ""		
	    }
	}
    }
    close $fo    
}


proc csv_save_object_titles {fo key namelist obtype} {
    global csv_output_params csv_text_data
    global export_ob_sh
    
    
    
    if [array exists csv_output_params] {
	set split_char ""
	set values $export_ob_sh($key)
	

	set all_vals ""
	# here allow the user to edit what csv_output params are output from the overall list of values associated with these objects
	# go through the array for this key, and see what active groups are
	#	for each of these, output all of the names in that list	
	puts -nonewline $fo "\#"
	foreach val $namelist {
	    switch $val {
		name {
		    puts -nonewline $fo "${split_char}Name"
		    set split_char $csv_text_data(sep_char)
		    lappend all_vals Name
		    
		}
		default {
		    foreach {keya value} [array get csv_output_params] {
			if {[string first "[lindex $key 0],[lindex $key 1]" $keya] != -1} {
			    if {([string first "active" $keya] != -1) && ($value == 1)} {
				set this_key [string range $keya 0 end-7]
				if  {[lsearch [set csv_output_params(${this_key})] $val] != -1} {
				    set val_title [title_from_object_params $val $obtype]
				    puts -nonewline $fo "$split_char${val_title}"
				    set split_char $csv_text_data(sep_char)
				    lappend all_vals ${val_title}
				}
			    }
			}
		    }
		}	    
	    }
	}
    }
    puts $fo ""
}

proc title_from_object_params { val obj_type } {
    global object_params
    
    set o_params $object_params($obj_type)
    set val_without_units [string trimright $val "_units" ]
    
    foreach data $o_params {
	set name [lindex $data 1]
	set title [lindex $data 0]
	if {$name == $val_without_units} {
	    if {$val_without_units == $val} {
		return $title
	    } else {
		return "$title units"
	    }
	}
    }
}

proc csv_save_objects {fo key namelist} {
    global csv_output_params csv_text_data
    global export_ob_sh
    
    if [array exists csv_output_params] {
	set split_char ""
	set values $export_ob_sh($key)
	

	set all_vals ""
	# here allow the user to edit what csv_output params are output from the overall list of values associated with these objects
	# go through the array for this key, and see what active groups are
	#	for each of these, output all of the names in that list	
	
	foreach val $namelist {
	    switch $val {
		name {
		    puts -nonewline $fo "$split_char$val"
		    set split_char $csv_text_data(sep_char)
		    lappend all_vals $val
		    
		}
		default {
		    foreach {keya value} [array get csv_output_params] {
			if {[string first "[lindex $key 0],[lindex $key 1]" $keya] != -1} {
			    if {([string first "active" $keya] != -1) && ($value == 1)} {
				set this_key [string range $keya 0 end-7]
				if  {[lsearch [set csv_output_params(${this_key})] $val] != -1} {
				 
				    puts -nonewline $fo "$split_char$val"
				    set split_char $csv_text_data(sep_char)
				    lappend all_vals $val
				}
			    }
			}
		    }
		}	    
	    }
	}
	
	# a new line here
	puts $fo ""

	foreach val $values {		
	    set split_char ""	
	    foreach nv $all_vals {
		set index_val [lsearch $val $nv]
		if { ($index_val != -1) } {
		    set cur_val [lindex $val [expr $index_val+1]]
		    # the next line gets rid of new lines in a field 
		    # and replaces them with spaces. This would be most 
		    # common with curve data
		    set new_val [string map  { \012 \040 } $cur_val ]
		    puts -nonewline $fo "${split_char}${new_val}"
		    set split_char $csv_text_data(sep_char)
		} else {
		    puts -nonewline $fo "$split_char"
		    set split_char $csv_text_data(sep_char)
		}			    
	    }
	    puts $fo ""		
	}
	
	# a new line here to split up the groups of object/shape types

    }
    puts $fo ""		
}


proc extra_parameter_data_from_prefix {prefix group} {
    
    if { ${group} == "source_trans_params"} {
	set  stuff [${group} $prefix "" 0]
    } else {
	set stuff [${group} $prefix]
    }
    set ret ""
    
    foreach val $stuff {
	lappend ret [lindex $val 1]
	if {[llength $val] == 5} {
	    lappend ret [lindex $val 1]_units
	}
    }
    return $ret
}


proc csv_group_params_set {indices ex_frame} {    
    foreach index $indices {
	${ex_frame}.chk$index select
    }
}

proc csv_group_params_unset {indices ex_frame} {
    foreach index $indices {
	${ex_frame}.chk$index deselect
    }    
}

proc choose_next_csv_output_params {id} {
    global csv_output_params object_type_title object_params
    global export_ob_sh shape_title
    
    if [array anymore export_ob_sh $id] {
	set key [array nextelement export_ob_sh $id]
	set value [set export_ob_sh($key)]
	while {[llength $key] != 3 && [array anymore export_ob_sh $id]} {
	    set key [array nextelement export_ob_sh $id]
	    set value [set export_ob_sh($key)]
	}
	
	if  {[llength $key] == 3} {
	    set form_name .csv_export_options[lindex $key 0]_[lindex $key 1]
	    form_kill $form_name
	    if {[array anymore export_ob_sh $id]} {
#		set buts [list "Select \"choose_next_csv_output_params $id;form_done $form_name\"" "Cancel \"if {[array exists export_ob_sh]} { array donesearch export_ob_sh $id };form_cancel $form_name\""]
		set buts [list "Select \"choose_next_csv_output_params $id;form_done $form_name\"" cancel]
	    } else {
		set buts [list "Select \"form_done $form_name\"" cancel]
#		set buts [list "Select \"form_done $form_name\"" "Cancel \"if {[array exists export_ob_sh]} { array donesearch export_ob_sh $id };form_cancel $form_name\""]
		
	    }
	    
	    
	    set d [form_init .csv_export_options[lindex $key 0]_[lindex $key 1] "$shape_title([lindex $key 1]) [set object_type_title([lindex $key 0])] export options" "" $buts ]
	    if {$d != ""} {
		set groups ""	
		set name_list ""
		# want to find what are all the object parameter associated with things with this key    
		
		foreach {n v} [array get csv_output_params] {
		    set vals [split $n ',']
		    if {([lindex $key 0] == [lindex $vals 0]) && ([lindex $key 1] == [lindex $vals 1])} {
			if {([lsearch $groups [lindex $vals 2] ] == -1) &&([llength $vals] == 3)} {
			    lappend groups [lindex $vals 2]
			}
		    }
		}
		
		
		set row_num 0
		form_frame $d.sub flat 2 {0 0 }
		foreach group $groups {
		    form_checkbutton ${d}.sub.checkb$row_num "$group                              " csv_output_params([lindex $key 0],[lindex $key 1],${group},active) "$row_num 1"
		    incr row_num
		}
		# opening_radiation_params
		# flow_dir_params
		form_finish bot
	    }
	} else {
	    if {[array exists export_ob_sh]} {
		array donesearch export_ob_sh $id
	    }
	}
    }
}


proc csv_output_select_all {val ex_frame num_vars} {
    set i 0
    while {$i < $num_vars} {
	if {$val==1} {
	    ${ex_frame}.chk$i select
	} else {
	    ${ex_frame}.chk$i deselect
	}
	incr i
    }
}

proc csv_file_export_object_geometry {} {    
    global object_edit_info csv_text_data
    
    set objs [tree_list_selected]
    if {$objs == ""} {
	user_error "No objects are selected"
	return
    }
    
    # before caling this, if no previous selected outputs,
    # set all to General
    set csv_text_data(geom_only) 0
    edit_csv_export_params
    
    set nn [file_selection -message "Save to what file?" \
		-title "Save object" \
		-is_save_dialog 1 -default [directory] \
		-new_ok 2 -extra_widgets \
		[list file_add_separating_character ]]
    if {$nn == ""} return
    csv_export_object_geometry $nn

}


proc csv_file_import_object_geometry {} {
    global file_import_name
    global csv_text_data
    
    if {$file_import_name == ""} {
	set file_import_name [directory]
    }
    
    # want to separate out the data type and sep char
    set file [file_selection -title "Select object geometry file" -default \
		  $file_import_name]
    
    
    if {$file == ""} return
    set file_import_name $file
    # now read in the text file to lists
    
    csv_load_object_groups $file
}

proc csv_get_ob_types {} {
    global object_edit_info object_type_title
    # want to include in the list all the object types that have some
    # instance that doesn't have the sectionlist "check-object" OR
    # only the BAD shape types :-)
    set o_options ""
    set bad_shape_types [list none container pipe ]
    
    foreach {ob_type sect_list} [array get object_edit_info] {
	# there could be one or more *type* of the object
	# with each type is associated either a list 
	# of shapes or "check-object" for complex objects
	if {$ob_type == "vent" || $ob_type == "domain"} continue
	foreach sect_type_list $sect_list {
	    set bad_type 1
	    # checks if at least one of the things in the list is a valid shape
	    # i.e. don't want things like pipE/container
	    
	    # only set bad_type to null if there's at least one valid section
	    foreach sh_type [lrange [lindex $sect_type_list 2] 2 end] {
		if {[lsearch $bad_shape_types [lindex $sh_type 0]]== -1} {
		    set bad_type 0
		}
	    }
	    
	    if {([lindex $sect_type_list 2] != "check_object") && ($bad_type==0)} {
		set this_option [ list $ob_type $object_type_title($ob_type)]
		if {[lsearch $o_options $this_option] == -1} {
		    lappend o_options [ list $ob_type $object_type_title($ob_type)]
		}
	    }
	}
	
    }
    return $o_options
}

proc csv_choose_object_type {} {
    global csv_text_data
    global object_edit_info object_type_title shape_title mat_title
    
    # this checks to see if in the commented out lines there is 
    # text indication of the object/shape type, and gives these as
    # defaults in the drop down menus
    
    # set here the list of shape types that are not acceptable
    
    set bad_shape_types [list none container pipe ]    
    
    if {$csv_text_data(revert) == 0} {
	# if no text matches, "block" and "hexa" are put as defaults
	set comment_line [lindex $csv_text_data(ob_group$csv_text_data(ob_index)) 0]	
	set title_line [lindex $csv_text_data(ob_group$csv_text_data(ob_index)) 1]
	set heading_line [lindex $csv_text_data(ob_group$csv_text_data(ob_index)) 2]	
	
	# is the input object data of the form "name" or "type"
	
	# is the 2nd entry in the list comment_line is found in the list of
	# object type titles?
	set ob_index [lsearch [array get object_type_title] [lindex $comment_line 1]]
	if {$ob_index % 2} { 
	    # if it is the object title, get the obtype instead
	    set poss_ob_type [lindex [array get object_type_title] [expr $ob_index - 1]]
	} else {
	    set poss_ob_type [lindex $comment_line 1]
	}

	set csv_text_data(data_type) shape
	set csv_text_data(default_sep_char) ,
	if {[string match \#* $comment_line] && 
	    [info exists object_edit_info($poss_ob_type)]} {
	    
	    # only goes here if the first line is a comment line, and 
	    # a possible object type has been identified
	    set csv_text_data(o_type) block
	    set csv_text_data(s_type) hexa
	    
	    if {[string first "Object" [lindex $comment_line 3]] == 0} {
		set csv_text_data(data_type) object
	    }
	    # what if these are not valid o_type/s_type data?
	    # then would want still to use the defaults...	
	    
	    # is the input object data of the form "name" or "type"
	    set poss_shape_type ""
	    foreach {sh_name sh_title} [array get shape_title] {
		if {([string first $sh_title [lindex $comment_line 2]] != -1) || ([string first $sh_name [lindex $comment_line 2]] != -1)} {
		    set poss_shape_type $sh_name
		} 
		
	    }
	    
	    foreach sect_type_list $object_edit_info($poss_ob_type) {
		# there could be one or more *type* of the object
		# with each type is associated either a list 
		# of shapes or "check-object" for complex objects
		if {[lindex $sect_type_list 2] != "check_object"} {
		    # check the list for the shape type
		    foreach sh_type [lrange [lindex $sect_type_list 2 ] 2 end] {
			set pat [lindex $sh_type 0]*
			
			if {([string match $pat $poss_shape_type]) && ([lsearch $bad_shape_types $sh_type] == -1) } {
			    set csv_text_data(o_type) $poss_ob_type
			    set csv_text_data(s_type) [lindex $sh_type 0]
			} 
		    }
		}
	    }
	    
	    set maxc 0
	    set csv_text_data(sep_char) ""
	    set csv_text_data(length_unit) [unit_get_default_choice length]
	    
	    foreach sep [csv_get_sep_char] {
		set sep [lindex $sep 0]
		set num [llength [split $heading_line $sep]]
		if {$num > $maxc} { 
		    set csv_text_data(sep_char) $sep; set maxc $num 
		}
	    }
	    set csv_text_data(hdr_num) 2
	    if {[string match \#* $title_line]} {
		set csv_text_data(hdr_num) 3
	    }
	    set csv_text_data(ftr_num) 0
	    
	} else {
	    # default shape data
	    
	    set csv_text_data(o_type) block
	    set csv_text_data(s_type) hexa	    
	    
	    set csv_text_data(hdr_num) 0
	    if {$csv_text_data(flotherm) == 1 } {
		set csv_text_data(hdr_num) 1
		set csv_text_data(o_type) plate
		set csv_text_data(s_type) quad
	    }
	    set csv_text_data(ftr_num) 0
	    set csv_text_data(sep_char) ,
	    set csv_text_data(length_unit) [unit_get_default_choice length]
	    
	}
        
        # reset to 0 every time the row import options gui is called
        set csv_text_data(set_power) 0
        set csv_text_data(power) 0.0
        set csv_text_data(unit_power) [unit_get_default_choice power]
        
	if {$csv_text_data(ob_index) != 0 } {
	    set csv_text_data(sep_char) $csv_text_data(default_sep_char)
	}
    }
    # want to be able to specify an object type and shape
    # so that we know what kind of data is being read in
    
    # how to make the setcmd
    
    # the first set of data is read into a text box and the user can 
    #choose what object type or profile it has while looking at the group of data
    set buts [ list {"Import" "csv_load_objects;form_done .object_options"} {"Skip" "form_done .object_options;skip_to_choose_next_data"} cancel ]
    
    set d [form_init .object_options "Row import options" "" $buts ]
    if { $d != "" } {
	form_frame $d.top flat 0 {0 0 }
	form_label $d.top.scharl "Separator" {0 0}
	futil_menuentry_labels $d.top.schar csv_text_data(sep_char) "csv_get_sep_char" {0 1} \
	    ""

	form_label $d.top.datl "Data type" {1 0}
	futil_menuentry_labels $d.top.datm csv_text_data(data_type) "csv_get_data_type" {1 1} \
	    ""
	set shape_act {{csv_text_data(data_type) shape}}
	form_label $d.top.lunits  "Shape length units" {2 0}  $shape_act
	futil_menuentry_labels $d.top.eunit csv_text_data(length_unit) "csv_get_length_options" {2 1} \
	    $shape_act

	form_label $d.top.numl "Number of header rows to ignore" {10 0}
	
	form_entry $d.top.char csv_text_data(hdr_num) string {10 1}	
	
	form_label $d.top.numll "Number of footer rows to ignore" {20 0}
	
	form_entry $d.top.charl csv_text_data(ftr_num) string {20 1}	

	form_label $d.top.onamel "Object type" {30 0}	
	futil_menuentry_labels $d.top.oname csv_text_data(o_type) "csv_get_ob_types" {30 1} \
	    "" 	
	
	form_label $d.top.snamel "Shape type" {40 0}
	futil_menuentry_labels $d.top.s_type csv_text_data(s_type) "csv_shapes_get_options" {40 1} \
	    "" 

#	form_checkbutton $d.top.set_power "Power" csv_text_data(set_power) {50 0} ""
#	set set_power {{csv_text_data(set_power) 1}}
#	form_entry $d.top.power csv_text_data(power) string {50 1} $set_power
#	futil_menuentry_labels $d.top.unit_power csv_text_data(unit_power) "csv_unit_get_options power" {50 2} $set_power

	form_text $d.text "" csv_text_data(ob_text) {1 0} "" ""
	$d.text.text configure -state disabled
	
	form_finish bot
    }  
    
    set obs_text {}
    set s {}
    foreach ob_line $csv_text_data(ob_group$csv_text_data(ob_index)) {
	append obs_text $s $ob_line
	set s \n
    }
    form_text_set .object_options.main.text $obs_text
}

proc csv_load_object_groups  { file } {
    global csv_text_data
    # how many groups of objects are there in the file - assume
    # a blank line separates the data
    set fi [open $file r]    
    set ob_no 0
    if [array exists csv_text_data] {
	foreach {i val} [array get csv_text_data ob_group* ] {
	    unset csv_text_data($i)
	}
    }
    set this_ob ""
    set csv_text_data(ob_index) 0
    set k 0
    
    set data [ split [read $fi ] \n ]
    set flo_header  [lindex $data 0]

    set flotherm [lindex [split $flo_header , ] 0]
    if { $flotherm == "Geometry Model" } {
	# is one of the Header's for flotherm "Collapsed"
	set fcol 0
	set collapsed_col ""
	foreach fh [split $flo_header ,] {
	    if {[string trimright [string trimleft $fh]] == "Collapsed"} {
		set collapsed_col $fcol
	    }
	    incr fcol
	}
	set csv_text_data(flotherm) 1
	set last_row ""
	foreach r $data {
	    if { $k == 0 } {
		lappend this_ob $r
		incr k
	    } elseif {$last_row == ""} {
		lappend this_ob $r
		set last_row $r
	    } elseif {([lindex [split $r , ] 1] != [lindex [split $last_row , ] 1]) || ( ($collapsed_col != "") && ([lindex [split $r , ] $collapsed_col] != [lindex [split $last_row , ] $collapsed_col])) } {
		# if an object has been defined by the previous row
		# if not, don't increment since line is blank...
		set csv_text_data(ob_group$ob_no) $this_ob
		set this_ob ""
		lappend this_ob $flo_header
		lappend this_ob $r
		set last_row $r
		incr ob_no
	    } else {
		lappend this_ob $r
		set last_row $r		
	    }
	}
    } else {
	set csv_text_data(flotherm) 0
	foreach r $data {
	    if {$r != ""} {
		lappend this_ob $r
		set split_stuff $r
	    } else {
		# this is a blank line so prepare for a new ob_group
		if {$this_ob != "" } {
		    # if an object has been defined by the previous row
		    # if not, don't increment since line is blank...
		    set csv_text_data(ob_group$ob_no) $this_ob
		    set this_ob ""
		    incr ob_no
		}
	    }	
	}	
    }
    
    if {[info exists csv_text_data(ob_group0)] } {
	set csv_text_data(revert) 0
	csv_choose_object_type    
    } else {
	user_error "No valid objects found in file $file"
    }    
}

proc csv_get_data_type {} {
    
    set options [list  {"object" "Object"} {"shape" "Shape"}]
    
    return $options    
}

proc csv_get_sep_char {} {
    
    set options [list  {"\t"  "tab" } { " " "space"} {"," "comma" } {";" "semi-colon"}]
    
    return $options    
}

proc csv_get_length_options {} {
    global unit_choices
    set options ""
    foreach v $unit_choices(length) {
	lappend options "$v $v"
    }
    return $options    
}

proc csv_unit_get_options {unit} {
    global unit_choices
    set units ""
    if [info exists unit_choices($unit)] {
        foreach v $unit_choices($unit) {
            lappend units "$v $v"
        }
    }
    return $units
}


proc edit_csv_export_params { {edit_choices 0} } {
    global object_edit_info csv_text_data object_type_title export_ob_sh
    global csv_output_params
    
    # 0 sets up the export_ob_sh array and compares it to previous 
    # if one exists.  Only sets up csv_output_params if something has 
    # changed in the selection. Default initial state is to have 
    # only "General" export

    # 1 is for where user elects to edit using the GUI

    
    set objs [tree_list_selected]
    # want to group the simple objects that have the same obtype and section type
    
    if {[array exists prev_export_ob_sh]} {	
	unset prev_export_ob_sh
    }
    
    if { $edit_choices != 1 } {
	if {[array exists export_ob_sh]} {	
	    
	    # want to copy existing array into prev_export_ob_sh and see if changes have been made
	    array set prev_export_ob_sh [array get export_ob_sh]
	    unset export_ob_sh
	}
        
	# this sets the export_ob_sh array up 
	foreach o $objs {
	    # only want to export data for Model object with shape
	    # and not of the check-object type, only the simple ones...
	    if {[$o getval obtype] != "part" && [$o getval obtype] != "material" && [$o getval obtype]!= "profile" && \
		    [$o getval obtype]!= "domain"   } {
		csv_export_simple_object_parameters $o
		csv_export_simple_shape_geometry $o	    
	    } else {
		mess "[set object_type_title([$o getval obtype])] are not exported by this function\n" red   
	    }	
	}
    }
    
    
    # only want to call this stuff if the export object array has changed since the last time it was called, OR user is choosing edit_object_parameters
    if {[array exists export_ob_sh] && ($csv_text_data(geom_only) == 0) } {
	
	# Here, if !edit_choices, and want to export object data,
	# want to edit csv_output_params only if: 
	# 1) the selection of objects to be output has changed
	# 2) csv_output_params doesn't exist (e.g. if previously had 
	# output geom_only)
	
	set selection_changed 0
	
	if  {[array exists prev_export_ob_sh]} {
	    set selection_changed [compare_export_arrays export_ob_sh prev_export_ob_sh]
	}
	
	if {$selection_changed || (![array exists csv_output_params])} {
	    if {$edit_choices == 0} {
		if [array exists csv_output_params] {
		    unset csv_output_params
		}
		# prior to the save box being brought up on the screen, 
		# the output parameters are grouped according 
		# to their data type
		default_csv_output_params
	    }
	}
	
	if {($edit_choices == 1) } {
	    choose_next_csv_output_params [array startsearch export_ob_sh]
	}
    }
}

proc compare_export_arrays { ary1 ary2 } {
    upvar $ary1 a1
    upvar $ary2 a2
    
    set ilist1 [array names a1]
    set ilist2 [array names a2]
    
    foreach idx $ilist1 {
	if {![info exists a2($idx)]} {
	    return 1
	}
	
	if {$a1($idx) != $a2($idx)} {
	    return 1
	}
    }
    
    return 0
}

proc file_add_separating_character {d} {
    global  csv_text_data
    
    form_label $d.namel "Separator" {0 0}
    futil_menuentry_labels $d.s_type csv_text_data(sep_char) "csv_get_sep_char" {0 1} \
	"" 

    set act1 {{csv_text_data(geom_only) "0"}}
    form_checkbutton $d.ck2 "Geometry only" csv_text_data(geom_only) {1 0} ""
    form_button $d.ck1 "Object output options" "edit_csv_export_params 1" {1 1} $act1
    
    
    return ""
}

proc csv_shapes_get_options {} {
    global  csv_text_data
    global object_edit_info
    # should return pairs of {value name}
    # should get the section from object_edit_info    

    set csv_text_data(collapsed) 0

    set ob_type ""
    if {$csv_text_data(flotherm) == 1} {
	# check if the data is COLLAPSED - 
	# in which case we don't want non-planar shapes available 
	set header_list [split [lindex $csv_text_data(ob_group$csv_text_data(ob_index)) 0] , ]
	set data_list [split [lindex $csv_text_data(ob_group$csv_text_data(ob_index)) 1] , ]

	# is collapsed in the header_line? 	
	set collapsed_index [ lsearch $header_list "Collapsed" ]
	set ob_type [lindex $data_list 1]	
	if {$collapsed_index != -1} {
	    # if so, is the corresponding value in the data_line YES?
	    if {[string trimleft [string trimright [lindex $data_list $collapsed_index]]] == "YES"} {
		set csv_text_data(collapsed) 0
	    }
	}	
    }
    
    set bad_shape_types [list none container pipe ]
    if { $csv_text_data(o_type) != "" } { 
	
	set num_genus [ llength $object_edit_info($csv_text_data(o_type)) ]
	if { $num_genus == 1 } {
	    # only one genus
	    # don't insert polygons!
	    foreach s [lrange [lindex [lindex $object_edit_info($csv_text_data(o_type)) 0 ] 2 ] 2 end ] {
		if { [lsearch $bad_shape_types [lindex $s 0]] == -1} {
		    lappend sect_list $s
		}
	    }
	} else {
	    # more than one genus - want all the section types
	    foreach s [lrange [lindex [lindex $object_edit_info($csv_text_data(o_type)) 0 ] 2 ] 2 end ] {
		if { [lsearch $bad_shape_types [lindex $s 0]] == -1} {
		    lappend sect_list $s
		}
	    }
	    
	    set i 1
	    while { $i < $num_genus } {
		# want to append the sections in this_sect_list that are not in sect_list
		set this_sect_list [lrange [lindex [lindex $object_edit_info($csv_text_data(o_type)) $i] 2 ] 2 end ]
		foreach sect $this_sect_list {
		    if { [ lsearch $sect_list $sect ] == -1 && \
			     [lsearch $bad_shape_types [lindex $sect 0]] == -1} {
			lappend sect_list $sect
		    }		    
		}
		incr i
	    }
	}
    } else {
	set sect_list ""
    }
    
    # is it a "check_object" type object? here assumes only simple object given
    
    if { $csv_text_data(collapsed) == 1 } {
	#	remove all the non flat shaped from the sec_list
	set flat_shapes [list quad circ polygon incline]
	set new_sect_list $sect_list
	set sect_list ""

	foreach sh $new_sect_list {
	    foreach fsh $flat_shapes {
		if {[string match *${fsh}* $sh]} {
		    lappend sect_list $sh   
		}		
	    }
	}
    }
    
    return $sect_list
}


proc get_shape_params_with_type { type } {
    set params ""
    switch $type {
	float {
	    set params [list xs ys zs xe ye ze xd yd zd xoff yoff zoff xc yc zc \
			    xc2 yc2 zc2 radius iradius radius2 iradius2 height \
			    start_angle end_angle xradvec yradvec zradvec xradvec2 \
			    yradvec2 zradvec2 oxradvec oyradvec ozradvec \
			    oxradvec2 oyradvec2 ozradvec2 thickness xd2 yd2 zd2 \
			    rotate_angle ]
	}
	int {
	    set params [list volume_flag diff_flag changes plane split_flag \
			    nverts sflag000 sflag001 sflag010 sflag011 \
			    sflag100 sflag101 sflag110 sflag111 \
			    plate_flag numcopies copyspace axis rotate_sign ]
	}
	str {
	    set params [ list notes name ignore ]
	}
    }
    return $params
}

proc csv_object_get_column_headers {} {
    global  csv_text_data
    
    #these options always available
    set titles [list {ignore Ignore} {name Name } {notes Notes} ]
    foreach v $csv_text_data(headers) {
	if {$v == "body_shape"} {
	    lappend titles ignore
	} else {
	    lappend titles [list $v $v]
	}
    }
    return $titles
    
}
proc csv_get_column_headers { col_vals {nverts ""} {changes ""}} {
    global  csv_text_data
    
    #these options always available
    set titles [list {ignore Ignore} {name Name } {notes Notes} ]
    set csv_float_params [get_shape_params_with_type float]
    set csv_int_params [get_shape_params_with_type int]
    set csv_str_params [get_shape_params_with_type string]
    if {$csv_text_data(s_type) == "polygon" && $nverts != ""} {
	for {set i 1} {$i <= $nverts} {incr i} {
	    lappend csv_str_params vert$i
	    if {$changes == 0} {
		lappend csv_str_params tvert$i
	    }
	}
    }
    # set of data things grouped by data type
    
    set extra_units ""
    
    
    # are any of the values in this column floats?
    # if so, no "flag"/integer params
    # are any strings?
    # if so, no "flag"/integer params, OR any float params
    set csv_data_type "integer"
    set poss_params [concat $csv_str_params $extra_units]
    foreach v $col_vals {
	# if an entry is a row is blank, want to allow
	# other columns to possibly have data there
	if {($v != "" )} {
	    set f1 [form_check_type {int} [string trim $v]]
	    set f2 [form_check_type {float} [string trim $v]]
	    if { $f1 == "integer" &&  $f2 == "real"} {
		# not real or an integer, so make only strings available 
		# in the menu for this column
		set csv_data_type "string"
	    } elseif { $f1 == "integer"} {
		# not an integer, is a float, so don't want the integer options in the list
		if {$csv_data_type != "string"} {
		    set csv_data_type "float"
		}
	    } 
	}
	if {$csv_data_type == "float"} { 
	    # don't want the integer dropdown items available since at least one value
	    # in this column is a float
	    set poss_params [concat $csv_str_params $csv_float_params  $extra_units]
	} elseif {$csv_data_type != "string"} { 
	    # there are no string items in the column, but those matching 
	    # integer pattern match could be "real" things (eg xs == 0)
	    set poss_params [concat $csv_str_params $csv_float_params $csv_int_params  $extra_units]
	}	
	
	# if csv_data type includes "real", don't want integer types
	# if it include string, don't want "real" OR "integer" types
	# the titles appended should be valid for the list of data given....
	switch $csv_text_data(s_type) {
	    fluid - surface - solid {
		
		foreach p $poss_params {
		    lappend titles [list $p $p]
		}    
	    }
	    default {
		set list_vals [[shape_${csv_text_data(s_type)}] list_values]
		for {set i 1} {$i <= $nverts} {incr i} {
		    lappend list_vals [list vert$i 0]
		    if {$changes == 0} {
			lappend list_vals [list tvert$i 0]
		    }
		}
		foreach p $list_vals {
		    if {[llength [lindex $p 1 ]] == 1} {
			if { [ lsearch $poss_params [lindex $p 0] ] != -1} {
			    lappend titles [list [lindex $p 0] [lindex $p 0]]
			}
		    }
		} 
		
	    }
	}
    }
    return $titles
}

proc transform_shape_length_scale { sh_data } {
    
    global csv_text_data unit_data
    
    set csv_length_params [list xs ys zs xe ye ze xd yd zd xoff yoff zoff xc yc zc \
			       xc2 yc2 zc2 radius iradius radius2 iradius2 height \
			       xradvec yradvec zradvec xradvec2 \
			       yradvec2 zradvec2 oxradvec oyradvec ozradvec \
			       oxradvec2 oyradvec2 ozradvec2 thickness xd2 yd2 zd2  ]
    
    # want to go through sh_data and rescale the length data 
    # from $csv_text_data(length_unit) to SI
    set new_sh_data ""
    set conv_vals $unit_data($csv_text_data(length_unit))
    set y0  [lindex $conv_vals 0] 
    set x0  [lindex $conv_vals 2] 
    set c  [lindex $conv_vals 1] 
    foreach {n v } $sh_data {
	if { [ lsearch $csv_length_params $n ] != -1 } {
	    # convert from $csv_text_data(length_unit) to SI
	    lappend new_sh_data $n [ expr ($v - $y0)/($c + 0.0) - $x0 ]	    
	} else {
	    lappend new_sh_data $n $v
	}
    }    
    
    return $new_sh_data
}

proc check_for_blank_int_or_float_data { i sh_data } {
    global csv_text_data
    
    foreach {n v} $sh_data {
	if {( $v == "") && [string first tvert $n]!= 0 && [string first vert $n]!= 0} {
	    set line_num [ expr $i + $csv_text_data(hdr_num) ]
	    mess "Ignored line $line_num because of blank $n value entry \n" red
	    return 1
	}		
    }
    
    return 0   
}

proc add_diff_flag_to_shape_data { sh_data } {
    
    set new_sh_data ""
    set use_given_diff_flag 0
    # if diff flag is set then don't append anything
    if {[lsearch $sh_data diff_flag] != -1} {
	set use_given_diff_flag 1
    }
    
    # if the end values are set, set diff_flag 0
    # if the diff values are set, set diff_flag 1
    set end_vals [list xe ye ze]
    set diff_vals [list xd yd zd]
    
    set have_diff 0
    set have_end 0
    foreach {n v} $sh_data {	
	if { ($v != "")} {
	    lappend new_sh_data $n $v   
	    if {([lsearch $diff_vals $n] != -1 )} {
		incr have_diff
	    } elseif {([lsearch $end_vals $n] != -1 )} {
		incr have_end
	    }		    
	}	
    }
    
    if {($use_given_diff_flag)} {
	# this ignores blank data, and enforces given diff_flag
	return $new_sh_data
    }
    
    if {$have_diff && !$have_end} {
	# for planar objects with only 2 of xd/yd/zd set, and plane unset
	# choose the plane from what diff value is not present in the list of params
	if {$have_diff == 2 && ([lsearch $sh_data "plane"] == -1)} {
	    foreach {diff plane} {xd 0 yz 1 zd 2} {
		if {([lsearch $sh_data $diff] == -1 )} {
		    lappend new_sh_data plane $plane
		}
	    }
	}
	
	# if all three diff values are set, choose plane flag (when unset)
	# from the thinnest direction - this is for Flotherm collapsed 
	# shapes in particular
	if {$have_diff == 3 } {

	    set min_thickness [lindex $sh_data [expr [lsearch $sh_data xd] + 1 ]]
	    set plane_dir 0
	    
	    foreach {diff plane} {xd 0 yz 1 zd 2} {
		if {([lindex $sh_data [expr [lsearch $sh_data $diff] + 1 ]] < $min_thickness )} {
		    set plane_dir $plane
		}
	    }
	    if {([lsearch $sh_data "plane"] == -1)} {
		lappend new_sh_data plane $plane_dir
	    }

	    # want to send a couple of values up to be added to the 
	    # object data if it is a collapsed Flotherm block - 
	    # thickness and plate_type
	    
	}
	# this ignores blank data, and enforces diff_flag based 
	# on non-blank xd/yd/zd values	
	return [lappend new_sh_data diff_flag 1]
    }
    
    if {!$have_diff && $have_end} {
	# this ignores blank data, and enforces diff_flag based 
	# on non-blank xe/ye/ze values (not strictly necessary to add
	# the diff_flag to the list, sice 0 is default diff_flag)
	return [lappend new_sh_data diff_flag 0]
    }
    
    # in the case that the list of data includes both end and diff values, 
    # and the diff_flag is NOT defined by the user, the 
    # end values are used (i.e. default diff flag is 0). 
    
    # The user ought to set a diff flag in the csv file if they want
    # to use diff vals in such a case - how can we assume otherwise, especially
    # if the data is inconsistent. If consistent the assumption is fine
    return $new_sh_data
}

proc make_all_new_objects {titles} {
    global csv_text_data
    # if no title is of type "Name", make all new objects
    # set the parameters defined from the titles
    set k 0
    for {set i 0} {$i < $csv_text_data(nrows)} {incr i} {
	
	set r $csv_text_data(row$i)
	
	set sh_data {}
	set ob_data {}
	set j 0		
	foreach val $r {
	    set title [ lindex $titles $j ]
	    # make a list of pairs of title value
	    switch $title {
		ignore {}
		notes {
		    lappend ob_data notes $val
		}
		default {
		    if { $csv_text_data(data_type) == "object" } {
			lappend ob_data $title $val
		    } else {
			lappend sh_data $title $val
		    }
		}
	    }		    
	    
	    incr j
	}
	
	if { $csv_text_data(data_type) == "shape" } {
	    # now if it is rectangle/prism, is it diff type/SE type?
	    # want to see if there is a diff_flag set
	    # if not derive diff_flag from the sh_data header names
	    if {($csv_text_data(s_type) == "quad" || $csv_text_data(s_type) == "hexa" )} {
		set sh_data [ add_diff_flag_to_shape_data $sh_data ] 
	    }
	}
	if { ![check_for_blank_int_or_float_data $i $sh_data ] } {
	    # here want to check the csv_text_data(length_unit) value
	    # transform the LENGTH unit values from this to the default
	    # and then reset these to have the unit value we want
	    # BUT here could have rounding errors!!!
	    set sh_data [transform_shape_length_scale $sh_data]
	    set new_obj [ax_object_create $csv_text_data(o_type) $csv_text_data(o_type) $csv_text_data(s_type) $ob_data $sh_data ]
	    if { $csv_text_data(data_type) == "shape" } {
		set obj_sh [db_get_shape $new_obj]
		
		foreach {name units} [$obj_sh get_all_units 0] {
		    $obj_sh setunits $name $csv_text_data(length_unit)
		}		    
	    }
	    incr k
	}
    }        
}


proc update_existing_ob_with_csv_data {old_ob {sh_data ""} {ob_data ""} {notes_val ""}} {
    global csv_text_data
    
    switch  $csv_text_data(o_type) {
	default {	    
	    model_set_current $old_ob
	    set obj_sh [db_get_shape $old_ob]
	    edit_set_category stype $csv_text_data(s_type)
	    set obj_sh [db_get_shape $old_ob]
	    foreach {param val} $sh_data {
		$obj_sh setval -noconstr $param $val
	    }
	    set n_ob_data ""
	   
	    set new_ob_data ""
	    set new_ob_unit_data ""
	    foreach {param val} $ob_data {
		if {[string last _units $param] != -1}  {
		    lappend new_ob_unit_data $param $val
		} else {
		    lappend new_ob_data $param $val
		}		
	    }
	    
	    set n_ob_data ""
	    set n_ob_unit_data ""
	    foreach {p v} $new_ob_data {
		if {[info exists v] } {
		    set with_units 0
		    if [info exists new_ob_unit_data] {
			foreach {pu vu} [split $new_ob_unit_data] {
			    if {[string first $p $pu] != -1} {
				lappend n_ob_unit_data $p $v $vu
				set with_units 1
			    } 
			}
		    }
		    if !$with_units {
			lappend n_ob_data $p $v
		    }			    
		}
	    }
	    
	    foreach {n v} $n_ob_data {
		$old_ob setval $n $v
	    }
	    foreach {n v u} $n_ob_unit_data {
		$old_ob setunits $n $u
		$old_ob setval_inunits $n $v
	    }

	    foreach {name units} [$obj_sh get_all_units 0] {
		$obj_sh setunits $name $csv_text_data(length_unit)
	    }
	}
    }

    # an existing object - only modify the notes
    # if notes_val not ""
    if {$notes_val != "" } {
	$old_ob setval notes $notes_val
    }

    if $csv_text_data(set_power) {
        $old_ob setval power $csv_text_data(power)
        $old_ob setunits power_units $csv_text_data(unit_power)
        $old_ob setval_inunits power_units $csv_text_data(unit_power)
    }
    
    if {[$old_ob getval body_shape] != ""} {
	edit_update_object $old_ob    
    }

}

proc create_new_ob_from_csv_data {{sh_data ""} {ob_data ""} notes_val ob_name} {
    global csv_text_data
    #	set new_obj [ ax_object_create $csv_text_data(o_type) $ob_name $csv_text_data(s_type) [list notes $notes_val] $sh_data]
    lappend ob_data notes $notes_val
    
    set n_ob_data ""
    set n_ob_unit_data ""
    set new_ob_data ""
    set new_ob_data_units ""
    set new_ob_unit_data ""
    foreach {param val} $ob_data {		
	if {[string last _units $param] != -1}  {
	    lappend new_ob_unit_data $param $val
	} else {
	    lappend new_ob_data $param $val   
	}		
    }
    
    foreach {p v} $new_ob_data {
	if {[info exists v] && ($new_ob_unit_data != "")} {
	    set with_units 0
	    foreach {pu vu} [split $new_ob_unit_data] {
		if {[string first $p $pu] != -1} {
		    lappend n_ob_unit_data $p $v $vu
		    set with_units 1
		} 
	    }
	    if !$with_units {
		lappend n_ob_data $p $v
	    }			    
	}
    }
    set new_obj [ ax_object_create $csv_text_data(o_type) $ob_name $csv_text_data(s_type) $n_ob_data $sh_data]
   
    set obj_sh [db_get_shape $new_obj]
    foreach {name units} [$obj_sh get_all_units 0] {
	$obj_sh setunits $name $csv_text_data(length_unit)
    }
	
    foreach {n v} $n_ob_data {
	$new_obj setval $n $v
    }
    foreach {n v u} $n_ob_unit_data {
	$new_obj setunits $n $u
	$new_obj setval_inunits $n $v
    }
    
    if $csv_text_data(set_power) {
        $new_obj setval power $csv_text_data(power)
        $new_obj setunits power_units $csv_text_data(unit_power)
        $new_obj setval_inunits power_units $csv_text_data(unit_power)
    }
       
    #    if {[info command app_clean_up_[set csv_text_data(o_type)]] != ""} {
    #	app_clean_up_[set csv_text_data(o_type)] $new_obj
    #    }
    #    reconfigure_object $new_obj
    
    if {[$new_obj getval body_shape] != ""} {
	edit_update_object $new_obj    
    }    

}

proc update_existing_and_create_new_objects {titles} {
    global csv_text_data
    
    for {set i 0} {$i < $csv_text_data(nrows)} {incr i} {
	set r $csv_text_data(row$i)
	
	# if an object exists with this name, 
	# update definition using the new parameter defs
	set sh_data {}
	set ob_data {}
	set notes_val ""
	set j 0		
	
	foreach val $r {		    
	    set title [ lindex $titles $j ]
	    # make a list of pairs of title value
	    switch $title {
		ignore {}
		name {
		    set ob_name $val
		}
		notes {
		    set notes_val $val
		}
		default {
		    if { $csv_text_data(data_type) == "object"} {
			if {$val != ""} {
			    lappend ob_data $title $val
			} 
		    } else {
			lappend sh_data $title $val
		    }
		}
	    }		    
	    
	    incr j
	}
	
	if {  $csv_text_data(data_type) == "shape"} {
	    # check here if the diff flag must be set
	    # now if it is rectangle/prism, is it diff type/SE type?
	    # want to see if there is a diff_flag set
	    # if not derive diff_flag from the sh_data header names
	    if {($csv_text_data(s_type) == "quad" || $csv_text_data(s_type) == "hexa" )} {
		set sh_data [ add_diff_flag_to_shape_data $sh_data ] 
	    }
	}
	
	# now either make a new object with that name or update an old one
	if { ![check_for_blank_int_or_float_data $i $sh_data ] } {

	    set old_ob [db_get_name $ob_name]
	    if {$old_ob != ""} {
		if {  $csv_text_data(data_type) == "shape"} {
		    set sh_data [transform_shape_length_scale $sh_data]
		}
		
		update_existing_ob_with_csv_data $old_ob $sh_data $ob_data $notes_val
	    } else {
		# where no such object exists, 
		# make one with this name
		if {  $csv_text_data(data_type) == "shape"} {
		    set sh_data [transform_shape_length_scale $sh_data]
		}
		create_new_ob_from_csv_data $sh_data $ob_data $notes_val $ob_name
	    }
	}
    }
    edit_reset_params
}

proc csv_make_objects {} {
    global csv_text_data
    # this should be called on done
    set titles {}
    set index_name ""
    # titles should be unique - check for this
    set i 0
    set fix_data ""
    set ignore_cols 0
    # check that same field not entered twice
    while { $i < $csv_text_data(maxc) && $fix_data==""} {
	set p $csv_text_data(header$i)
	
	if {$p == "ignore" } {
	    incr ignore_cols 1
	} 
	if {$p == "name" } {
	    set index_name $i
	}
	set found_in_list [ lsearch -exact  $titles $p ]
	if {$p == "?"} {
	    user_error "Fix value in column $i"
	    set fix_data 1
	} elseif {$found_in_list != -1 && $p != "ignore"} {
	    user_error "Each object should have only one field of each type"
	    set fix_data 1
	}
	
	lappend titles $p
	incr i	
    }    

    if {$fix_data== ""} {
	if {$ignore_cols != $csv_text_data(maxc)} {
	    if { $index_name == "" } {
		make_all_new_objects $titles
		# if no title is of type "Name", make all new objects
		# set the parameters defined from the titles
	    } else {
		# if one of the titles is Name
		# make each object separately depending on 
		# whether an existing object with that name exists
		update_existing_and_create_new_objects $titles
	    }
	}
	incr csv_text_data(ob_index)
	
	if {[info exists csv_text_data(ob_group$csv_text_data(ob_index))]} {
	    if {$csv_text_data(ob_group$csv_text_data(ob_index)) != ""} {
		set csv_text_data(revert) 0
		csv_choose_object_type
	    }
	}
	form_done .csv_options
    }
}

proc skip_to_choose_next_data {} {
    global csv_text_data
    
    incr csv_text_data(ob_index)
    
    if {[info exists csv_text_data(ob_group$csv_text_data(ob_index))]} {
	if {$csv_text_data(ob_group$csv_text_data(ob_index)) != ""} {
	    set csv_text_data(revert) 0
	    csv_choose_object_type
	}
    }
    
}

proc csv_load_objects  { } {
    global  csv_text_data
    
    # use the hdr_num to choose what rows to look for the headers and suggested data types in    
    set heading_line ""
    # if no text matches, "block" and "hexa" are put as defaults
    if {[expr $csv_text_data(hdr_num) - 1] >= 0} {
	set heading_line [lindex $csv_text_data(ob_group$csv_text_data(ob_index)) [expr  $csv_text_data(hdr_num) - 1]]
    }
    
    # set the headers      
    set csv_text_data(headers) [split $heading_line $csv_text_data(sep_char)]
    set maxc 0
    set csv_text_data(nrows) 0
    if {$csv_text_data(ob_index)==0} { set csv_text_data(default_sep_char) $csv_text_data(sep_char) }
    
    foreach l [lrange $csv_text_data(ob_group$csv_text_data(ob_index)) $csv_text_data(hdr_num) end-$csv_text_data(ftr_num)] {
	# what if the line has some quoted regions between separators?
	# find all the stuff that is between pairs of inverted commas
	# replace with "" and split the list using the separating character
	# then restore the stuff between the separators in order....
	if [array exists quoted] {
	    foreach {i val} [array get quoted ] {
		unset quoted($i)
	    }
	}
	
	# get rid of pairs of double inverted commas ""
	set sub_stuff "inv_comma_replace"
	set rep [ regsub -all {\"\"} $l $sub_stuff l ]
	# get rid of cells enclosed by inverted commas
	set inc 0
	set found [ regexp {\"[^\"]+\"} $l quoted($inc)]
	# Make l become the original line with stuff in pairs 
	# of inverted commas  replaced with ""	
	while {$found == 1} {
	    regsub {\"[^\"]+\"} $l "\"\"" l
	    incr inc
	    set found [ regexp {\"[^\"]+\"} $l quoted($inc)]
	}
	# split what's left using the separating character
	set this_l [split $l $csv_text_data(sep_char)]
	if {[info exists csv_text_data(row$csv_text_data(nrows))]} {
	    unset csv_text_data(row$csv_text_data(nrows))
	}
	set quotes_found $inc
	set inc 0
	set new_l ""
	foreach colv $this_l {
	    # use this to restore list indexes that are simply "" to
	    # be whatever they were previously
	    # ONLY one quoted text string in 
	    # each list index (so "if", not "while")	    
	    if {$quotes_found>0} {
		set sub_stuff [string trim $quoted($inc) \" ]
		set rep [ regsub {\"+?\"} $colv $sub_stuff colv ]
		incr inc $rep
		incr quotes_found [expr -1 * $rep]
	    }
	    lappend new_l $colv
	}
	# now replace  "inv_comma_replace" with a single inverted comma \"
	# and put data into the cells
	set sub_stuff "\""
	set c 0
	foreach colv $new_l {
	    set rep [ regsub {inv_comma_replace} $colv $sub_stuff colv ]
	    lappend csv_col($c) $colv	    
	    lappend csv_text_data(row$csv_text_data(nrows)) $colv
	    incr c
	}
	if {$c > $maxc} { 
	    set maxc $c 
	    set csv_text_data(maxc) $maxc
	}
	
	incr csv_text_data(nrows) 
	
    }
    
    set opts ""
    set defs ""
    set vals ""
    
    # How many vertices are present in the polygon object - here
    # check what column nverts is in. This val needed to see how many
    # dropdown "vert$i" options will be available in the column headers
    
    set nverts_index ""
    set changes_index ""
    set nverts -1
    set changes 1
    for {set i 0} {$i < $maxc} {incr i} {
	set v [lindex $csv_text_data(headers) $i]
	if {$v == "nverts"} {
	    set nverts_index $i
	}
	if {$v == "changes"} {
	    set changes_index $i
	}
    }
    if {$nverts_index != ""} {
	foreach v [split $csv_col($nverts_index)] {
	    if {$v > $nverts} {
		set nverts $v
	    }
	}
    }
    if {$changes_index != ""} {
	foreach v [split $csv_col($changes_index)] {
	    if {$v == 0} {
		set changes 0
	    }
	}
    }
    
    set fixed_option_list ""
    # first check if it is a shape or object 
    # if a shape, we sort the headers according to data type
    
    # if object, each column has the same list of options
    # actually possibly shoule make it just a fixed list, since there 
    # are heaps of potential header options, and it is not likely that 
    # the user will change them
    
    if {$csv_text_data(data_type) == "shape"} {
	for {set i 0} {$i < $maxc} {incr i} {
	    if {$csv_text_data(flotherm) == 1} {
		set def [flotherm_data_equivalent [string trimleft [string trimright [lindex $csv_text_data(headers) $i]]]]	
	    } else {
		set def [lindex $csv_text_data(headers) $i]		
	    }
	    if {$def == ""} { set def ignore }	
	    if {$def == "body_shape"} { set def ignore }	
	    lappend opts [csv_get_column_headers $csv_col($i) $nverts $changes]
	    lappend defs $def
	    lappend vals $csv_col($i)
	}
    } else {
	set opts [csv_object_get_column_headers]
	for {set i 0} {$i < $maxc} {incr i} {
	    set def [lindex $csv_text_data(headers) $i]
	    if {$def == ""} { set def ignore }	
	    if {$def == "body_shape"} { set def ignore }
	    if $csv_text_data(set_power) {
	        if {$def == "power"} {
	            set csv_col($i) $csv_text_data(power)
	        }
	        if {$def == "power_units"} {
	            set csv_col($i) $csv_text_data(unit_power)
	       }
	    }
	    lappend defs $def
	    lappend vals $csv_col($i)
	}
	
	set fixed_option_list 1
    }
    
    # want to open a window 
    # user can select a prototype object from the tree
    # label and entry box for number of header rows
    set buts [ list {"Import" "csv_make_objects"} \
		   {"<<< Back" "global csv_text_data; set csv_text_data(revert) 1;csv_choose_object_type;form_done .csv_options"} \
		   cancel ] 
    # what to do on "Done"?
    
    form_kill .csv_options
    set d [form_init .csv_options "Column import options" "" $buts ]
    if {$d == ""} return
    
    csv_sync_listboxes $d.bot $opts $defs $vals {0 0} $fixed_option_list
    form_finish bot
    
    update
    csv_sync_listboxes_update $d.bot
}

proc csv_sync_listboxes {w option_lists default_headers value_lists tabdata {fixed_option_list ""}} {
    global subtitle_font input_font input_background form_label_text
    global csv_text_data form_bw
    
    frame $w -relief flat -borderwidth 0
    
    set col [llength $option_lists]
    if {$fixed_option_list != ""} {
	set col [llength $default_headers]
    }
    set sfw $w.c.win
    scrollbar $w.vscroll -command "csv_sync_listboxes_yview $sfw.list $col" \
	-relief sunken -borderwidth $form_bw -orient v
    scrollbar $w.hscroll -command "$w.c xview" \
	-relief sunken -borderwidth $form_bw -orient h
    canvas $w.c -xscrollcommand "$w.hscroll set" -width 800 -height 600
    frame $sfw
    set fitem [$w.c create window 0 0 -window $sfw -anchor nw]
    
    grid $w.c -row 0 -column 0 -sticky news
    grid $w.hscroll -row 1 -column 0 -sticky ew
    grid $w.vscroll -row 0 -column 1 -sticky ns
    grid columnconfigure $w 0 -weight 1000
    grid rowconfigure $w 0 -weight 1000
    
    set height 30
    set col 0
    # number of list boxes depends on column width data
    # titles contains the name of a function, returning a list 
    # of menu items
    # which can be different from the number of columns
    # this will make the same menu for each column
    if {$fixed_option_list != ""} {
	foreach def $default_headers vals $value_lists {
	    
	    set width 0
	    foreach v $vals {
		if {[string length $v] > $width} { set width [string length $v] }
	    }
	    if {$width < 10} { set width 10 }
	    set csv_text_data(header$col) $def
	    futil_menuentry_labels $sfw.title$col csv_text_data(header$col) \
		"list $option_lists" "0 $col" "" "" [expr $width - 2]	    
	    # what to do if the menulist is too long for the screen??
	    
	    listbox $sfw.list$col -relief sunken -borderwidth 1 -yscroll \
		"$w.vscroll set" -background $input_background -exportselection 0 \
		-foreground black -width $width -height $height
	    grid $sfw.list$col -row 1 -column $col
	    eval $sfw.list$col insert end $vals	    
	    incr col
	}		
    } else {
	foreach opts $option_lists def $default_headers vals $value_lists {
	    set width 0
	    foreach v $vals {
		if {[string length $v] > $width} { set width [string length $v] }
	    }
	    if {$width < 10} { set width 10 }
	    set csv_text_data(header$col) $def
	    futil_menuentry_labels $sfw.title$col csv_text_data(header$col) \
		"list $opts" "0 $col" "" "" [expr $width - 2]
	    listbox $sfw.list$col -relief sunken -borderwidth 1 -yscroll \
		"$w.vscroll set" -background $input_background -exportselection 0 \
		-foreground black -width $width -height $height
	    grid $sfw.list$col -row 1 -column $col
	    eval $sfw.list$col insert end $vals
	    incr col
	}
    }
    form_dotab $w "$tabdata -fill both" news
}

proc csv_sync_listboxes_update {w} {
    set width [winfo reqwidth $w.c.win]
    set height [winfo reqheight $w.c.win]
    
    $w.c configure -height $height -scrollregion "0 0 $width $height"
}

proc csv_sync_listboxes_yview {pref ncols args} {
    for {set i 0} {$i < $ncols} {incr i} {
	eval $pref$i yview $args
    }
}

proc get_csv_sides_data {key} {
    
    switch [lindex $key 1] {
	polygon {
	    set sides [list bot top side]
	}
	hexa {
	    set sides [list minx maxx miny maxy minz maxz]
	} 
	quad {
	    set sides [list low high]
	}
	cyl {
	    set sides [list bot top sides inside]
	}
	circ {
	    set sides [list low high sides]
	}
	ellipse {
	    set sides [list minx maxx miny maxy minz maxz]
	}
	incline {
	    set sides [list low high sides]
	}
	oval {
	    set sides [list low high sides inner0 inner1 inner2 inner3]
	} 
	sphere {
	    set sides [list sides]
	    for {set i 0} {$i < 12} {incr i} {
		lappend sides inner$i   
	    }
	}	
    }
    return $sides
}

