# $Revision$ on $Date$
# The above revision line needs to be the first line for all techtk files
# Copyright (c) 2006 Ansys Inc
#----------------------------------------------------------------------------
#
# PADT---------------------------------------------------- Phoenix Analysis &
#                                                         Design Technologies
#
#----------------------------------------------------------------------------
#
#       Package: AI-Environment GUI Management
#
#
#          File: wizard_environment.tcl
#
#
#     Developer: Matt Sutton
#
#
#   Description: The procedures in this file are responsible for creating and
#                maintaining the execution environment for the wizards.  This 
#                includes setting up the namespaces that wrap each wizard as well
#                as transparently executing client procedures within these
#                namespaces.  Furthermore, all variables referenced by the 
#                widgets within a wizard are wrapped within the wizards namespace
#                and are managed by the procedures within this file.
#   
#
#      05/03/02: File Creation (MGS)
#
#           
#
#-----------------------------------------------------------------------------
# For support: 1-480-813-4884 ext. 125 -or- matt.sutton@padtinc.com
#-----------------------------------------------------------------------------

namespace eval ::AIEnv::GUIManagement {
	
}

namespace eval ::AIEnv::GUIManagement::Wizard {
	variable WizardCommands
	variable WizardData
	variable DefaultTitle
	set DefaultTitle "AI*Environment"
	
	set WizardCommands [list "compute_size" "set" "get" "proc" "next" "prev" "finish" "create" "addpage" "getpageframe" "show" "enablefinish" "cancel" "querynext" "setnext"]
	set WizardData(Count) 0
}

proc  ::AIEnv::GUIManagement::Wizard::RouterProc {Wizard {Cmd "proc"} args} {
	if { [string equal [info commands ::AIEnv::GUIManagement::Wizard::_$Cmd] ""] } {
		if { [string equal [info commands $Cmd] ""] } {
			variable WizardCommands
			set OptList [join $WizardCommands ", "]
			set OptList [linsert $OptList "end-1" "or"]
			error "Bad option \"$Cmd\": must be $OptList"
		} else {
			set UserSuppliedFunctionName $Cmd
			set Cmd "proc"
			set args [linsert $args 0 $UserSuppliedFunctionName]
		}
	}
	return [eval [list ::AIEnv::GUIManagement::Wizard::_$Cmd $Wizard] $args]
}

proc ::AIEnv::GUIManagement::Wizard::_set {Name args} {
	# Evaluate things in the namespace of this wizard
	set Namespace [::AIEnv::GUIManagement::GetNamespace Wizard $Name]

	# Move the name passed into this function into the global variable
	# _WizardName so that we can reference it from within the namespace
	# command below
	global _WizardName
	global _WizardArgs
	set _WizardName $Name
	set _WizardArgs $args

	namespace eval $Namespace {
		global _WizardName
		global _WizardArgs
		set LocalVariables [::AIEnv::GUIManagement::GetLocalVariables $_WizardName]
		set VariableName [namespace tail [lindex $_WizardArgs 0]]
		set VariableNamespace [namespace qualifiers [lindex $_WizardArgs 0]]
		set CurrentNamespace [namespace current]

		if { $VariableNamespace != "" } {
			if { $CurrentNamespace != $VariableNamespace } {
				error "Proc: ::AIEnv::GUIManagement::Wizard::_set \nThe variable named: \$VariableName\" resides in namespace: \"$VariableNamespace\" which is different from the current namespace scope: \"$CurrentNamespace\""
			}
		}
		set Value [lindex $_WizardArgs 1]

		if { [lsearch -exact $LocalVariables $VariableName] != -1 } {
			variable $VariableName
			set $VariableName $Value
		} else {
			error "Proc: ::AIEnv::GUIManagement::Wizard::_set \nThe variable named: \"$VariableName\" was not found within the scope of the Wizard named: \"$_DEZName\""
		}
	}
}

proc ::AIEnv::GUIManagement::Wizard::_get {Name args} {
	# Evaluate things in the namespace of this dez
	set Namespace [::AIEnv::GUIManagement::GetNamespace Wizard $Name]

	# Move the name passed into this function into the global variable
	# _DEZName so that we can reference it from within the namespace
	# command below
	global _WizardName
	global _WizardArgs
	set _WizardName $Name
	set _WizardArgs $args

	namespace eval $Namespace {
		global _WizardName
		global _WizardArgs
		set LocalVariables [::AIEnv::GUIManagement::GetLocalVariables $_WizardName]

		set VariableName [namespace tail [lindex $_WizardArgs 0]]
		set VariableNamespace [namespace qualifiers [lindex $_WizardArgs 0]]
		set CurrentNamespace [namespace current]

		if { $VariableNamespace != "" } {
			if { $CurrentNamespace != $VariableNamespace } {
				error "Proc: ::AIEnv::GUIManagement::Wizard::_get \nThe variable named: \$VariableName\" resides in namespace: \"$VariableNamespace\" which is different from the current namespace scope: \"$CurrentNamespace\""
			}
		}
		if { [lsearch -exact $LocalVariables $VariableName] != -1 } {
			variable $VariableName
			return [subst $[subst $VariableName]]
		}
	}
}

proc ::AIEnv::GUIManagement::Wizard::_proc {Name args} {
	variable DidExecuteUserProc
	set Namespace [::AIEnv::GUIManagement::GetNamespace Wizard $Name]
	global _WizardName
	global _WizardArgs
	global _UserProc

	set _WizardName $Name
	set _WizardArgs $args

	# Look for the function to see if it exists before we move into the
	# namespace
	set FuncName [lindex $args 0]
	set NullFunc [string toupper $FuncName]
	if { $NullFunc == "NULL" || $NullFunc == "" } {
		return
	}
	# Don't inforce a case convention on the user proc
	regexp -nocase "$FuncName" [join [info commands]] UserProc

	if { [info exists UserProc] } {
		if { $UserProc != "" } {
			set _UserProc $UserProc
			set DidExecuteUserProc 1
			namespace eval $Namespace {
				global _WizardName
				global _WizardArgs
				global _UserProc

				set LocalVariables [::AIEnv::GUIManagement::GetLocalVariables $_WizardNameName]

				# Bring these variables into this scope
				foreach Var $LocalVariables {
					variable $Var
				}

				# Get the names of the arguements for this procedure
				set ActualFuncArgs [info args $_UserProc]
				# Pull the arguments for the function into the local scope
				set ArgListIndex 1
				set SuppliedArgCount [expr [llength $_WizardArgs] - 1]
				foreach ActualArg $ActualFuncArgs {
					# Bring the argument into local scope
					variable $ActualArg
					if { $ArgListIndex <= $SuppliedArgCount } {
						set $ActualArg [lindex $_WizardArgs $ArgListIndex]
					} elseif { [info default $UserProc $ActualArg Value] == 1} {
						set $ActualArg $Value
					} else {
						mess "Error: Could not match a value for arg: \"$ActualArg\" while executing: \"$_DEZName proc $FuncName ...\"\n" red
					}
					incr ArgListIndex
				}

				# Now get the guts of the procedure and execute in this namespace
				set ProcBody [info body $_UserProc]
				eval $ProcBody
			}
		}
	}
	unset _WizardName
	unset _WizardArgs
}

proc ::AIEnv::GUIManagement::Wizard::_show {Name args} {
	variable WizardData
	# Call the users proc if avaiable
	ExecuteUserProc $Name Open
	set Stack [::struct::stack]
	set WizardData($Name-PageStack) $Stack
	set FirstPage [lindex $WizardData($Name-Pages) 0]
	$Stack push $FirstPage
	$WizardData($Name-PageMgr) raise $FirstPage
	
	if { [winfo ismapped $WizardData($Name-FinishButton)] } {
		grid forget $WizardData($Name-FinishButton)
		grid $WizardData($Name-CancelButton) -row 0 -col 3 -padx 3
	}
	
	$WizardData($Name-PrevButton) configure -state disabled
	$WizardData($Name-NextButton) configure -state active
	
	wm deiconify $WizardData($Name-MainWin)
	
}


proc ::AIEnv::GUIManagement::Wizard::_next {Name args} {
	variable WizardData
	# Set the next logical page
	set Stack $WizardData($Name-PageStack)
	set CurrentPage [$Stack peek]
	set PageList $WizardData($Name-Pages)
	set NextPageIndex [expr [lsearch $PageList $CurrentPage] + 1]
	if { $NextPageIndex >= 1 } {
		set NextPage [lindex $PageList $NextPageIndex]
	}
	
	set WizardData($Name-NextPage) $NextPage
	ExecuteUserProc $CurrentPage Next
	# The user might change the next page, so take that into
	# account
	set NextPage $WizardData($Name-NextPage)
	
	set NextPageIndex [lsearch $PageList $NextPage]
	set LastPage [lindex $PageList end]
	if {$NextPageIndex != -1} {
		if { $NextPage == $LastPage } {
			grid forget $WizardData($Name-CancelButton)
			grid $WizardData($Name-FinishButton) -row 0 -column 3 -padx 12
			$WizardData($Name-NextButton) configure -state disabled
		} 
		
		$Stack push $NextPage
		$WizardData($Name-PageMgr) raise $NextPage
		$WizardData($Name-PrevButton) configure -state active
	}
}

proc ::AIEnv::GUIManagement::Wizard::_prev {Name args} {
	variable WizardData
	set Stack $WizardData($Name-PageStack)
	set PageList $WizardData($Name-Pages)
	set CurrentPage [$Stack peek]
	$Stack pop
	set PrevPage [$Stack peek]
	
	set WizardData($Name-PrevPage) $PrevPage
	ExecuteUserProc $CurrentPage Next
	set PrevPage $WizardData($Name-PrevPage)
	
	set FirstPage [lindex $PageList 0]
	set PrevPageIndex [lsearch $PageList $PrevPage]
	if { $PrevPageIndex != -1 } {
		if { $PrevPage == $FirstPage } {
			$WizardData($Name-PrevButton) configure -state disabled
		}
		
		if { [winfo ismapped $WizardData($Name-FinishButton)] } {
			grid forget $WizardData($Name-FinishButton)
			grid $WizardData($Name-CancelButton) -row 0 -column 3 -padx 12
		}
		
		$WizardData($Name-PageMgr) raise $PrevPage
		$WizardData($Name-NextButton) configure -state active
	}
}

proc ::AIEnv::GUIManagement::Wizard::_finish {Name args} {
	variable WizardData
	
	wm withdraw $WizardData($Name-MainWin)
}

proc ::AIEnv::GUIManagement::Wizard::_querynext {Name args} {
	
}

proc ::AIEnv::GUIManagement::Wizard::_setnext {Name args} {
	
}

proc ::AIEnv::GUIManagement::Wizard::_destroy {Name args} {
	
}

proc ::AIEnv::GUIManagement::Wizard::_cancel {Name args} {
	variable WizardData
		
	wm withdraw $WizardData($Name-MainWin)
}

proc ::AIEnv::GUIManagement::Wizard::_compute_size {Name args} {
	variable WizardData
	if { [array names WizardData $Name-PageMgr] != "" } {
		$WizardData($Name-PageMgr) compute_size
	} else {
		mess "Didn't compute size\n" red
	}
}

proc ::AIEnv::GUIManagement::Wizard::_create {Name args} {
	variable WizardData
	variable DefaultTitle
	variable ::AIEnv::GUIManagement::GUIDisplayData
	
	set ArgLength [llength $args]
	if { [expr fmod($ArgLength, 2)] != 0.0 } {
		
	}
	set Title ""
	set Image ""
	foreach {key value} $args {
		switch -- $key {
			-title {
				set Title $value			
			}
			-image {
				set Image $value			
			}
			default {
				error "Unknown option to wizard named: \"$Name\". Should be either -title or -image"
			}
		}
	}
	incr WizardData(Count)
	# Create the toplevel widget
	set Wizard [toplevel .wizard${WizardData(Count)}]
	wm withdraw $Wizard
	wm resizable $Wizard 0 0
	
	if {$Title != ""} {
		wm title $Wizard $Title
	} else {
		wm title $Wizard $DefaultTitle
	}
	
	if {$Image != ""} {
		set ImageFrame [frame $Wizard.imageFrame -background white -relief sunken -borderwidth 1]
		set Image [label $ImageFrame.im -image $Image -background white]
		#set VSep [Separator $Wizard.vSep1 -orient vertical]
		pack $Image -side left
		grid $ImageFrame -row 0 -column 0 -sticky nsew -pady 3 -padx 3
		#grid $VSep -row 0 -column 1 -sticky ns -pady 2 -padx 5
		set InsertColumn 2
	} else {
		set InsertColumn 0
	}
	
	set ColumnSpan [expr $InsertColumn + 1]
	for {set Col 0} {$Col < $ColumnSpan} {incr Col} {
		if { $Col == $InsertColumn } {
			grid columnconfigure $Wizard $Col -weight 1
		} else {
			grid columnconfigure $Wizard $Col -weight 0
		}
	}
	# Create the page manager
	set PageMgr [PagesManager $Wizard.pgMgr]
	# Create the horizontal separator
	set HSep [Separator $Wizard.hSep -orient horizontal]
	# Create the buttons
	set ButtonFrame [frame $Wizard.bf]
	set NextButton [button $ButtonFrame.next -text "Next >" -command "$Name next" \
	                       -width $::AIEnv::GUIManagement::GUIDisplayData("ButtonWidth") \
	                       -font $::AIEnv::GUIManagement::GUIDisplayData("ButtonFont") ]
	set PrevButton [button $ButtonFrame.prev -text "< Prev" -command "$Name prev" \
	                       -width $::AIEnv::GUIManagement::GUIDisplayData("ButtonWidth") \
	                       -font $::AIEnv::GUIManagement::GUIDisplayData("ButtonFont") ]
	                       
	set CancelButton [button $ButtonFrame.cancel -text "Cancel" -command "$Name cancel" \
	                         -width $::AIEnv::GUIManagement::GUIDisplayData("ButtonWidth") \
	                         -font $::AIEnv::GUIManagement::GUIDisplayData("ButtonFont") ]
	                         
	set FinishButton [button $ButtonFrame.fini -text "Finish" -command "$Name finish" \
	                         -width $::AIEnv::GUIManagement::GUIDisplayData("ButtonWidth") \
	                         -font $::AIEnv::GUIManagement::GUIDisplayData("ButtonFont") ]
	
	# Grid in the buttons
	grid columnconfigure $ButtonFrame 0 -weight 1
	grid columnconfigure $ButtonFrame 1 -weight 0
	grid columnconfigure $ButtonFrame 2 -weight 0
	grid columnconfigure $ButtonFrame 3 -weight 0
	
	grid $PrevButton -row 0 -column 1 -padx 1 -pady 3
	grid $NextButton -row 0 -column 2  -pady 3
	grid $CancelButton -row 0 -column 3 -padx 12 -pady 3
	
	grid $PageMgr -row 0 -column $InsertColumn -padx 2 -sticky nsew -pady 3
	grid $HSep -row 1 -column 0 -columnspan $ColumnSpan -padx 2 -sticky ew
	grid $ButtonFrame -row 2 -column 0 -columnspan $ColumnSpan -padx 2 -sticky ew
	
	set WizardData($Name-PrevButton) $PrevButton
	set WizardData($Name-NextButton) $NextButton
	set WizardData($Name-CancelButton) $CancelButton
	set WizardData($Name-FinishButton) $FinishButton
	set WizardData($Name-PageMgr) $PageMgr
	set WizardData($Name-Pages) ""
	set WizardData($Name-MainWin) $Wizard
}

proc ::AIEnv::GUIManagement::Wizard::_addpage {Name args} {
	variable WizardData
	# Possible arguements are 
	# 1. The name of the page (Must be there)
	# 2. A -title "String" pair (optional)
	if {[llength $args] < 1} {
		error "Error while adding a page to the wizard named \"$Name\". No page name specified."
	} elseif {[llength $args] > 3} {
		error "Too may arguements to addpage for wizard named \"$Name\"."
	}
	
	set PageTitle ""
	set PageName ""
	if {[llength $args] == 1} {
		set PageName [lindex $args 0]	
	} elseif {[llength $args] == 3 } {
		set PageName [lindex $args 0]
		set PageTitle [lindex $args 2]
	} else {
		error "Wrong number of arguments to addpage for wizard named \"$Name\"."
	}
	
	if { [array names WizardData $Name-PageMgr] != "" } {
		set NewPage [$WizardData($Name-PageMgr) add $PageName]
	} else {
		error "Cannot add a page to the wizard named \"$Name\". You must create a wizard before you can add pages to it."
	}
	
	if { $PageTitle != "" } {
		set TitleLab [label $NewPage.title -text $PageTitle]
		set MainFrame [frame $NewPage.mainFrame]
		set HSep [Separator $NewPage.hSep -orient horizontal]
		grid rowconfigure $NewPage 0 -weight 0
		grid rowconfigure $NewPage 1 -weight 0
		grid rowconfigure $NewPage 2 -weight 1
		grid $TitleLab -row 0 -column 0 -sticky w -padx 3  
		grid $HSep -row 1 -column 0 -sticky ew -pady 3
		grid $MainFrame -row 2 -column 0 -sticky nsew 
		grid columnconfigure $NewPage 0 -weight 1
		set WizardData($Name-$PageName-frame) $MainFrame
	} else {
		set WizardData($Name-$PageName-frame) $NewPage
	}
	
	lappend WizardData($Name-Pages) $PageName
}

proc ::AIEnv::GUIManagement::Wizard::_getpageframe {Name args} {
	variable WizardData
	if { [llength $args] == 1 } {
		set PageName [lindex $args 0]
		if { [lsearch $WizardData($Name-Pages) $PageName] == -1 } {
			error "Page named: \"$PageName\" not found in wizard named: \"$Name\""
		}
	} else {
		error "Too many arguments to getpageframe for Wizard named: \"$Name\""
	}
	if {[array names WizardData $Name-$PageName-frame] != ""} {
		return $WizardData($Name-$PageName-frame)
	} else {
		error "Could not find frame for page named: \"$PageName\" in wizard named: \"$Name\""
	}
}


proc ::AIEnv::GUIManagement::Wizard::ExecuteUserProc {Name CandidateFunctionSuffixes {DefaultProc ""}} {
	# Iterate over all of the possible candidate functions
	variable DidExecuteUserProc
	set DidExecuteUserProc 0
	foreach Candidate $CandidateFunctionSuffixes {
		# Build up the default proc Name
		set ProcName "on${Name}${Candidate}"
		set args $ProcName
		_proc $Name $args
		if { $DidExecuteUserProc == 1 } {
			break
		}
	}
	if { $DidExecuteUserProc == 0 } {
		if {$DefaultProc != ""} {
			$DefaultProc $Name
		}
	}
}


