#=~~=~~=~~=~~=~~=~~=~~=~~=~~=~~=~~=~~=~~=~~=~~=~~=~~=~~=~~=~~=~~=~~=~~=~~=~~=~~=~~=~~=~~=~~=~~=~~=~~=~=~
#                                      numgeo - GiD interface
#                       Copyright (C) 2023-today Jan Machacek, Rahul Dogra
#=~~=~~=~~=~~=~~=~~=~~=~~=~~=~~=~~=~~=~~=~~=~~=~~=~~=~~=~~=~~=~~=~~=~~=~~=~~=~~=~~=~~=~~=~~=~~=~~=~~=~=~
#
# History
# - 26.11.2023, J. Machacek - Initial version
# - 07.03.2025, R. Dogra - Added beam elements
# - 11.04.2025, R. Dogra - Added window top level
#=~~=~~=~~=~~=~~=~~=~~=~~=~~=~~=~~=~~=~~=~~=~~=~~=~~=~~=~~=~~=~~=~~=~~=~~=~~=~~=~~=~~=~~=~~=~~=~~=~~=~=~

proc GiD_Event_InitProblemtype { dir } {

    numgeo::SetDir $dir ;#store to use it later
    #numgeo::RegisterGiDEvents
    numgeo::LoadScripts
    numgeo::ModifyMenus
	
    GidUtils::OpenWindow CUSTOMLIB
    
	# disclaimer at start-up
	set self_close 0
    GidUtils::Splash [file join $dir images numgeo-gid-2024-06-05.png] .splash $self_close
	
	# Window scrolling
	set ::CUSTOM_LIB_WINDOW_VALUES_SCROLLED 0
	set ::CUSTOM_LIB_WINDOW_TOPLEVEL 1
	
	# Modify menu
	GiDMenu::Create "numgeo" "PRE" 6 =
	set pos -1
	GiDMenu::InsertOption "numgeo" [list "Generate numgeo files"] [incr pos] PRE "numgeo::CreateInputFiles"
	GiDMenu::InsertOption "numgeo" [list "---"] [incr pos] PRE "" "" "" replace =
	GiDMenu::InsertOption "numgeo" [list "Reference Manual..."] [incr pos] PRE "numgeo::OpenReferenceManual" "" "" replace =
	GiDMenu::InsertOption "numgeo" [list "Tutorial Manual..."] [incr pos] PRE "numgeo::OpenTutorial" "" "" replace =
	GiDMenu::InsertOption "numgeo" [list "Theory Manual..."] [incr pos] PRE "numgeo::OpenTheoryManual" "" "" replace =

    # Change the default procedure GiD_Event_SelectGIDBatFile to our own
    GiD_RegisterEvent GiD_Event_SelectGIDBatFile numgeo::Select_GID_Bat_File PROBLEMTYPE numgeo

	# remove CALCULATE menu
	# GiDMenu::Delete "Calculate" PRE
	
	# remove options in DATA menu
	GidChangeDataLabel "Interval" ""
	GidChangeDataLabel "Local axes" ""
	GidChangeDataLabel "Materials" ""
	GidChangeDataLabel "Conditions" ""
	GidChangeDataLabel "Problem data" ""
	
	# remove options in MESH menu
	GiDMenu::RemoveOption "Mesh" [list "Create boundary mesh"] PRE _
	GiDMenu::RemoveOption "Mesh" [list "View Mesh boundary"] PRE _
	
	# apply menu update
    GiDMenu::UpdateMenus

    # open window tree
    GidUtils::OpenWindow CUSTOMLIB
}

proc GiD_Event_EndProblemtype {} {
    #GiD_UnRegisterEvents PROBLEMTYPE numgeo
    #GiD_UnRegisterPluginAddedMenuProc numgeo::ModifyMenus
}

# Bbox
proc ::gid_groups_conds::_register_popup_help_add_contents {w text} {
    if {[catch {
        if {[winfo class $w] eq "Text"} {
            set bbox [$w bbox end]
        } else {
            set bbox ""
        }
    } err]} {
        puts "Warning: popup help bbox failed on widget $w – $err"
    }
}

#
# numgeo namespace 
#

namespace eval numgeo {
    variable problemtype_dir
}

proc numgeo::SetDir { dir } {
    variable problemtype_dir
    set problemtype_dir $dir
}

proc numgeo::GetDir { } {
    variable problemtype_dir
    return $problemtype_dir
}

proc numgeo::open_url {url} {
    # Function to open a URL with the default web browser
	
    # Check if the platform is Windows
    set platform [expr {[info exists ::env(COMSPEC)] ? "windows" : [info exists ::env(OSTYPE)] ? $::env(OSTYPE) : "unix"}]

    if {$platform eq "windows"} {
        exec {*}[auto_execok start] "" $url
    } elseif {[string match "darwin*" [exec uname]]} {
        # Check if the platform is macOS
        exec open $url
    } else {
        # Assume the platform is a Unix-like system
        exec xdg-open $url
    }
}

proc numgeo::OpenTutorial { } {
    set url "https://j-machacek.github.io/numgeo/tutorial/index.html"
    numgeo::open_url $url
}

proc numgeo::OpenReferenceManual { } {
    set url "https://j-machacek.github.io/numgeo/reference/index.html"
    numgeo::open_url $url
}

proc numgeo::OpenTheoryManual { } {
    set url "https://j-machacek.github.io/numgeo/theory/index.html"
    numgeo::open_url $url
}

#proc numgeo::RegisterGiDEvents {} {
#    # Write - Calculation
#    GiD_RegisterEvent GiD_Event_AfterWriteCalculationFile numgeo::AfterWriteCalculationFile PROBLEMTYPE numgeo
#
#    # register the proc to be automatically called when re-creating all menus (
#	# e.g. when doing files new or changing the current language)
#    GiD_RegisterPluginAddedMenuProc numgeo::ModifyMenus
#}


proc numgeo::CreateInputFiles { } {
   set project_path [GiD_Info Project ModelName]
   set ModelName [file tail $project_path]
   set FileName $project_path
   append FileName ".gid" "/" $ModelName
   
   #GiD_Process Mescape Utilities Calculate
   numgeo::WriteMesh $FileName
   numgeo::WriteCalculationFile $FileName $ModelName
   
   WarnWin [= "... Meshfile and Calculation File created."]
}


proc numgeo::LoadScripts { } {
    variable problemtype_dir
    # Common scripts
    set script_files [list select-element-type.tcl writing.tcl write-mesh.tcl write-batch-file.tcl]
    foreach FileName $script_files {
        uplevel #0 [list source [file join $problemtype_dir scripts $FileName]]
    }
}

#proc numgeo::AfterWriteCalculationFile { FileName errorflag } {
#
#    set project_path [GiD_Info Project ModelName]
#    set ModelName [file tail $project_path]
#	
#    if { ![info exists gid_groups_conds::doc] } {
#        WarnWin [= "Error: data not OK"]
#        return
#    }
#    set err [catch { numgeo::WriteCalculationFile $FileName $ModelName } ret]
#    if { $err } {
#        WarnWin [= "Error when preparing data for analysis (%s)" $::errorInfo]
#        set ret -cancel-
#    }
#    return $ret
#}

proc numgeo::ModifyMenus { } {
    if { [GidUtils::IsTkDisabled] } {
        return
    }
    foreach menu_name {Conditions Interval "Interval Data" "Local axes"} {
        GidChangeDataLabel $menu_name ""
    }
    if { [lsearch $::gidUserDataOptions [_ "Show data tree"]] != -1  } {
        # menu "Data tree" is automatically added but can be toggled of
        GidAddUserDataOptions [= "Show data tree"] [list GidUtils::ToggleWindow CUSTOMLIB] end
    }
	
    #set x_path {/*/container[@n="Properties"]/container[@n="_materials_"]}
    #GidAddUserDataOptions [= "Import/export materials"] [list gid_groups_conds::import_export_materials .gid $x_path] end
	
    GiDMenu::UpdateMenus
}

#proc numgeo::ModifyMenus { } {
#    if { [GidUtils::IsTkDisabled] } {
#        return
#    }
#    foreach menu_name {Conditions Interval "Interval Data" "Local axes"} {
#        GidChangeDataLabel $menu_name ""
#    }
#    if { [lsearch $::gidUserDataOptions [_ "Show data tree"]] != -1  } {
#        GidAddUserDataOptions [= "Show data tree"] [list GidUtils::ToggleWindow CUSTOMLIB] end
#    }
#
#    GiDMenu::InsertOption "Calculate" [list "Launch Sine Wave Generator"] end PRE LaunchSineWaveApp "" "" replace =
#
#    GiDMenu::UpdateMenus
#}
#source [file join [file dirname [info script]] scripts launch_sine_wave.tcl]

#
# auxiliary procs invoked from the tree (see .spd xml description)
#

proc numgeo::GetMaterialsList { domNode } {
    set x_path {//container[@n="_mat_"]/container[@n="_materials_"]}
    set dom_materials [$domNode selectNodes $x_path]
    if { $dom_materials == "" } {
        error [= "xpath '%s' not found in the spd file" $x_path]
    }
    set result [list]
    foreach dom_material [$dom_materials childNodes] {
        set name [$dom_material @name]
        lappend result $name
    }
    return [join $result ,]
}

proc numgeo::EditDatabaseListDirect {domNode dict boundary_conds } {
    set has_container ""
    set database materials
    set title [= "User defined"]
    set list_name [$domNode @n]
    set x_path {//container[@n="_materials_"]}
    set dom_materials [$domNode selectNodes $x_path]
    if { $dom_materials == "" } {
        error [= "xpath '%s' not found in the spd file" $x_path]
    }
    set primary_level material
    if { [dict exists $dict $list_name] } {
        set xps $x_path
        append xps [format_xpath {/blockdata[@n=%s and @name=%s]} $primary_level [dict get $dict $list_name]]
    } else {
        set xps ""
    }
    set domNodes [gid_groups_conds::edit_tree_parts_window -accepted_n $primary_level -select_only_one 1 $boundary_conds $title $x_path $xps]
    set dict ""
    if { [llength $domNodes] } {
        set domNode [lindex $domNodes 0]
        if { [$domNode @n] == $primary_level } {
            dict set dict $list_name [$domNode @name]
        }
    }
    return [list $dict ""]
}


# 
# procs to amplitudes
#

proc numgeo::GetAmplitudesList { domNode } {
    set x_path {//container[@n="_amplitudes_"]}
    set dom_amplitudes [$domNode selectNodes $x_path]
    if { $dom_amplitudes == "" } {
        error [= "xpath '%s' not found in the spd file" $x_path]
    }
    set result [list]
    foreach dom_amplitude [$dom_amplitudes childNodes] {
        set name [$dom_amplitude @name]
        lappend result $name
    }
    return [join $result ,]
}

proc numgeo::EditDatabaseListDirectAmplitude {domNode dict boundary_conds } {
    set has_container ""
    set database amplitudes
    set title [= "User defined"]
    set list_name [$domNode @n]
    set x_path {//container[@n="_amplitudes_"]}
    set dom_amplitudes [$domNode selectNodes $x_path]
    if { $dom_amplitudes == "" } {
        error [= "xpath '%s' not found in the spd file" $x_path]
    }
    set primary_level material
    if { [dict exists $dict $list_name] } {
        set xps $x_path
        append xps [format_xpath {/blockdata[@n=%s and @name=%s]} $primary_level [dict get $dict $list_name]]
    } else {
        set xps ""
    }
    set domNodes [gid_groups_conds::edit_tree_parts_window -accepted_n $primary_level -select_only_one 1 $boundary_conds $title $x_path $xps]
    set dict ""
    if { [llength $domNodes] } {
        set domNode [lindex $domNodes 0]
        if { [$domNode @n] == $primary_level } {
            dict set dict $list_name [$domNode @name]
        }
    }
    return [list $dict ""]
}

#
# procs to truss
#

proc numgeo::GetTrussList { domNode } {
    set x_path {//container[@n="_mat_"]/container[@n="_truss_prop_"]}
    set dom_trusses [$domNode selectNodes $x_path]
    if { $dom_trusses == "" } {
        error [= "xpath '%s' not found in the spd file" $x_path]
    }
    set result [list]
    foreach dom_truss [$dom_trusses childNodes] {
        set name [$dom_truss @name]
        lappend result $name
    }
    return [join $result ,]
}



# 
# procs to toggle between visible and invisble settings in real time
#

# Toggle visibility for multiphase simulations with more than one phase
proc visible_ge_2_phases {domNode} {
	set path {string(../../value[@n="_nphases_"]/@v)}
	# I do not yet understand why I need to move 2 directories up, but ok... it works
	set nphases [$domNode selectNodes $path]
	if {$nphases == "2"} {return normal}
	if {$nphases == "3"} {return normal}
    return hidden
}

proc visible_ge_2_phases2 {domNode} {
	set path {string(../value[@n="_nphases_"]/@v)}
	# I do not yet understand why I need to move 2 directories up, but ok... it works
	set nphases [$domNode selectNodes $path]
	if {$nphases == "2"} {return normal}
	if {$nphases == "3"} {return normal}
    return hidden
}

# Toggle visibility for multiphase simulations with 3 phases
proc visible_eq_3_phases {domNode} {
	set path {string(../../value[@n="_nphases_"]/@v)}
	set nphases [$domNode selectNodes $path]
	if {$nphases == "3"} {return normal}
    return hidden
}

# ... same as above but one level less nested
proc visible_eq_3_phases_2 {domNode} {
	set path {string(../value[@n="_nphases_"]/@v)}
	set nphases [$domNode selectNodes $path]
	if {$nphases == "3"} {return normal}
    return hidden
}

proc visible_model {flag0 flag1 domNode} {
	set path {string(../value[@n=$flag0]/@v)}
	set model_name [$domNode selectNodes $path]
	if {$model_name == $flag1} {return normal}
    return hidden
}

proc visible_0up {flag0 flag1 domNode} {
	set path {string(value[@n=$flag0]/@v)}
	set model_name [$domNode selectNodes $path]
	if {$model_name == $flag1} {return normal}
    return hidden
}

proc visible_1up {flag0 flag1 domNode} {
	set path {string(../value[@n=$flag0]/@v)}
	set model_name [$domNode selectNodes $path]
	if {$model_name == $flag1} {return normal}
    return hidden
}

proc visible_2up {flag0 flag1 domNode} {
	set path {string(../../value[@n=$flag0]/@v)}
	set model_name [$domNode selectNodes $path]
	if {$model_name == $flag1} {return normal}
    return hidden
}

#
# Drawing symbols
#

proc numgeo::draw_point_load { } {
    GiD_OpenGL draw -color $::gid_draw_opengl::rgb(orange)
    gid_draw_opengl::draw_point_load
}

proc numgeo::draw_surface_pressure { values_list } {
    foreach item $values_list {
        lassign $item key value unit
        set data($key) $value
        #set unit($key) $unit
    }
    set modulus $data(_value_)    
    if { $modulus > 0 } {
        set load_local_direction [list 0 -1 0] ;#considered positive pointing oposite to local normal
    } elseif { $modulus < 0 } {
        set load_local_direction [list 0 1 0] ;#considered positive pointing oposite to local normal
    } else {
        #the value could be set by other function field, intead as modulus, consider like positive modulus
        set load_local_direction [list 0 -1 0]
    }
    set dictionary [dict create load_type local load_vector $load_local_direction]    
    return $dictionary
}


proc numgeo::draw_loads_global { values_list } {
    foreach item $values_list {
        lassign $item key value unit
        set data($key) $value
    }

    set vx $data(_value_x_)
	set vy $data(_value_y_)
	set vz $data(_value_z_)
	
	set force [list $vx $vy $vz]

    set dictionary [dict create load_type global load_vector $force]    
    return $dictionary
}

proc my_loads_function_time { domNode } {
    set loads [list [list scalar]]   
    lappend loads [list interpolator_func x]
    return [join $loads ,]
}

proc my_loads_function_periodic { domNode } {
    set loads [list [list scalar]]   
    lappend loads [list interpolator_func Ai]
    return [join $loads ,]
}

#namespace eval numgeo {}

#proc numgeo::CallbackRunSine { } {
#    set exe_path "[numgeo::GetDir]/bin/sine_wave_generator.exe"
#
#    if {[file exists $exe_path]} {
#        exec $exe_path &
#    } else {
#        tk_messageBox -message "Executable not found: $exe_path"
#    }
#
#    # ✅ RETURN an empty dictionary so GiD doesn't throw an error
#    return [dict create]
#}

# valuesList will be like: {{_vaue_ 3 {}} {_constant_ yes {}}}
#proc numgeo::draw_loads_global { valuesList } {
#    package require gid_draw_opengl
#	package require math::linearalgebra
#	
#    foreach item $valuesList {
#        lassign $item key value unit
#        set data($key) $value
#        #set unit($key) $unit
#    }
#    set vx $data(_value_x_)
#	set vy $data(_value_y_)
#	set vz $data(_value_z_)
#
#    #set transform_matrix [concat $vx 0 $vy 0 $vz 0 0 0 0 1]    
#	
#	set force [list $vx $vy $vz]
#    set matrix_x_axis [math::linearalgebra::unitLengthVector $force]
#    lassign [MathUtils::CalculateLocalAxisFromXAxis $matrix_x_axis] matrix_y_axis matrix_z_axis
#    set scale [expr {1.0e1*$scale_draw_loads*[math::linearalgebra::norm_two $force]}]    
#    set transform_matrix [concat [math::linearalgebra::scale_vect $scale $matrix_x_axis] 0 [math::linearalgebra::scale_vect $scale $matrix_y_axis] 0 [math::linearalgebra::scale_vect $scale $matrix_z_axis] 0 0 0 0 1]   
#	
#    set list_id [gid_draw_opengl::create_opengl_list_drawing numgeo::draw_point_load $transform_matrix]
#    return $list_id
#}
