#                      W I Z A R D . I T K
# BRL-CAD
#
# Copyright (c) 2004-2021 United States Government as represented by
# the U.S. Army Research Laboratory.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public License
# version 2.1 as published by the Free Software Foundation.
#
# This library is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this file; see the file named COPYING for more
# information.
#
###
#
# This is the 'revised' Wizard class. It uses a notebook like the
# original version, but it ditches the Prev/Next/Cancel buttons.
# Rather it incorporates a menubar and help frame. Switching between
# steps is accomplished using a menu with checkboxes. The check-
# boxes indicate whether a particular step has been accomplished.
#
# Individual pages can take advantage of the fact that the menubar
# is dynamic.
#

package require Itcl
package require Itk
package require Iwidgets

namespace eval RtWizard {

    package provide Wizard 1.0

    #
    # wizard - Provides a lowercased access method for the
    #          Wizard class.
    #
    proc wizard {pathName args} {
	uplevel Wizard $pathName $args
    }

    #
    # Add options
    #
    option add *Wizard.borderWidth 2 widgetDefault
    option add *Wizard.labelPos wn widgetDefault
    option add *Wizard.listHeight 150 widgetDefault
    option add *Wizard.hscrollMode dynamic widgetDefault
    option add *Wizard.vscrollMode dynamic widgetDefault
    option add *Wizard.textBackground seashell

    #
    # Define the usual options
    #
    itk::usual Wizard {
	keep -activebackground \
	    -activerelief \
	    -background \
	    -borderwidth -cursor \
	    -elementborderwidth \
	    -foreground \
	    -highlightcolor \
	    -highlightthickness \
	    -insertbackground \
	    -insertborderwidth \
	    -insertofftime \
	    -insertontime \
	    -insertwidth \
	    -jump \
	    -labelfont \
	    -selectbackground \
	    -selectborderwidth \
	    -textbackground \
	    -textfont \
	    -troughcolor
    }

    #
    # Wizard
    #
    # The wizard class provides the framework in which the RtWizard
    # operates. The wizard holds a series of pages (e.g. Color-
    # ObjSelector). Individual picture types (e.g. TypeF) use a subset
    # of the available pages to gather the information the need to
    # create an image.
    #
    # Use the singleton pattern to allow the pages to invoke wizard methods
    #
    set ::wizardInstance ""

    if {![info exists wizard_width]} {
	set ::DEF_WIDTH 600
    } else {
	set ::DEF_WIDTH $wizard_width
    }

    if {![info exists wizard_height]} {
	set ::DEF_HEIGHT 600
    } else {
	set ::DEF_HEIGHT $wizard_height
    }


    ::itcl::class Wizard {
	inherit ::itk::Widget

	public {
	    #
	    # methods
	    #
	    constructor {args} {}
	    method add {type token args}
	    method delete {token}
	    method enable {token}
	    method disable {toke}

	    method select {token}
	    method selectPrev {}

	    method menubar {args}

	    method setType {token}
	    method addType {type token}
	    method getTypeInfo {token}

	    method getIntroPage {}

	    method openSteps {}
	    method closeSteps {}

	    method fullSize {}
	    method preview {}

	    method UpdateRC {}

	    method updateRenderMenu {}
	    method activateMenu {token}
	}

	private {
	    #
	    # methods
	    #
	    method setupDefaultMenus {}

	    #
	    # variables
	    #

	    variable currentPageToken
	    variable prevPageToken

	    variable pagePaths ""
	    variable helpVar

	    variable statusVariables ""

	    variable pages
	    variable enabledPages

	    variable imageType

	    variable stepCount 0
	    variable currentStepMenu ""


	}
    }

    #
    # The Wizard class follows the singleton design pattern. If
    # a Wizard has already been created, the constructor throws.
    #
    itcl::body Wizard::constructor {args} {

	global wizardInstance

	if { [string length $wizardInstance] > 0 } {
	    error "Can not instantiate a second wizard object."
	}

	#
	# Create the menubar
	#
	itk_component add menubar {
	    iwidgets::menubar $itk_interior.menubar \
		-helpvariable [ itcl::scope helpVar ]
	} {
	    usual
	}

	#
	# Configure the File, Image, and Help menus.
	#
	setupDefaultMenus

	#
	# Create the notebook
	#
	itk_component add notebook {
	    iwidgets::notebook $itk_interior.notebook \
		-width $::DEF_WIDTH \
		-height $::DEF_HEIGHT
	} {
	    usual
	}

	#
	# Create the help label at the bottom of the screen
	#
	itk_component add helpThing {
	    label $itk_interior.helpThing \
		-anchor w \
		-textvariable [itcl::scope helpVar] \
		-width 40 \
		-relief sunken
	} {
	    usual
	}

	#
	# Pack em!
	#
	pack $itk_component(menubar) -fill x
	pack $itk_component(notebook) -fill both -expand 1
	pack $itk_component(helpThing) -fill x

	set wizardInstance $this
    }

    #
    # setupDefaultMenus
    #
    # Adds the initial set of menus to the menubar. These menus should
    # not be changed by the content objects.
    #
    itcl::body Wizard::setupDefaultMenus { } {

	#
	# File menu
	#
	$itk_component(menubar) add menubutton file \
	    -text File \
	    -menu {
		options -tearoff false -selectcolor blue
	    }

	$itk_component(menubar) add command .file.goNew \
	    -label "New" \
	    -helpstr "Create a new images." \
	    -command {puts "New Selected"}

	$itk_component(menubar) menuconfigure .file.goNew \
	    -state disabled

	$itk_component(menubar) add command .file.goOpen \
	    -label "Open BRL-CAD Database" \
	    -helpstr "Open a BRL-CAD database." \
	    -command {puts "Open Selected"}

	$itk_component(menubar) menuconfigure .file.goOpen \
	    -state disabled

	$itk_component(menubar) add command .file.doRC \
	    -label "Write .rtwizardrc" \
	    -helpstr "Create or update a .rtwizardrc file" \
	    -command "[itcl::code $this UpdateRC]"

	$itk_component(menubar) add separator .file.sep

	$itk_component(menubar) add command .file.Quit \
	    -label "Quit" -underline 0 \
	    -helpstr "Quit the RtWizard." \
	    -command {exit}

	#
	# Image Menu
	#
	$itk_component(menubar) add menubutton image \
	    -text "Image" \
	    -state disabled \
	    -menu {
		options -tearoff false -selectcolor blue
	    }

	$itk_component(menubar) add command .image.SelectType \
	    -label "New Image..." \
	    -helpstr "View examples of different image types." \
	    -command "[itcl::code $this select exp]"

	$itk_component(menubar) add separator .image.sep1

	#
	# Steps menu
	#

	#
	# Render Menu
	#
	$itk_component(menubar) add menubutton render \
	    -text "Render" \
	    -state disabled \
	    -menu {
		options -tearoff false -selectcolor blue
	    }

	$itk_component(menubar) add command .render.preview \
	    -label "Preview" \
	    -helpstr "Generate a quarter-size preview image" \
	    -command "[itcl::code $this preview]"

	$itk_component(menubar) add command .render.render \
	    -label "Full-Size" \
	    -helpstr "Generate a full-size picture with the current." \
	    -command "[itcl::code $this fullSize]"

	#
	# Help menu
	#
	$itk_component(menubar) add menubutton help \
	    -text Help \
	    -state disabled \
	    -menu {
		options -tearoff true -selectcolor green
	    }

	$itk_component(menubar) add command .help.goHelp \
	    -label "Help..." \
	    -helpstr "Detailed help" \
	    -command "[::itcl::code $this select] help"

	$itk_component(menubar) add command .help.goAbout \
	    -label "About..." \
	    -helpstr "About the RtWizard" \
	    -command {puts "selected: About..."}
    }

    #
    # add
    #
    # Adds a content page to the Wizard. The page should be implemented
    # as an iwidget that inherits from labeledframe. This is primarily
    # for consistency.
    #
    # The content page must support these public methods.
    #
    # 1. onSelect - called when the page is exposed.
    # 2. onDeselect - callback for hide events
    # 3. getStepInfo - gets the name of the step provided, and the
    #                  status variable.
    #
    itcl::body Wizard::add { type token args } {

	#
	# Create the page. First, we need to get the parent from the
	# notebook
	#
	set path [ $itk_component(notebook) add -label $token ]

	#
	# Instantiate the page, pass the arguments.
	#
	set pageName [ eval $type ${path}.\#auto $args ]

	#
	# Add to the pages array
	#
	set pages($token) $pageName

	#
	# Pack it!
	#
	pack $pageName \
	    -fill both -expand 1
	pack propagate $pageName

	set currentPageToken $token

	return $pageName
    }

    #
    # select
    #
    # Calls the onDeselect method for the current page.
    # Changes the notebook to the requested page.
    # Calls the onShow method for the new page.
    #
    itcl::body Wizard::select { token } {

	# onHide
	eval $pages($currentPageToken) onDeselect
	set prevPageToken $currentPageToken

	# switch pages
	set index [ $itk_component(notebook) index $token ]
	$itk_component(notebook) select $index

	set currentPageToken $token

	# onShow
	eval $pages($currentPageToken) onSelect
    }

    #
    # selectPrev
    #
    # Selects the previous viewed page. All pop-ups should
    # call this from their onOK and onCancel methods
    #
    itcl::body Wizard::selectPrev { } {
	select $prevPageToken
    }

    #
    # menubar
    #
    # Routes menubar configuration commands to the menubar.
    #
    itcl::body Wizard::menubar {args} {
	eval $itk_component(menubar) $args
    }

    #
    # enable
    #
    # Enables a page, loads its step into the steps menu, and
    # adds the page to the list of enabled pages.
    #
    itcl::body Wizard::enable {token} {

	#
	# Get the pageName, this is the object path.
	#
	set pageName $pages($token)

	#
	# Get the Step name and status variable
	#
	set si [ $pageName getStepInfo ]
	set stepName [ lindex $si 0 ]
	set stepVar [ lindex $si 1 ]

	#
	# Add the step to the step menu. If the page does not correspond
	# to a required step, skip.
	#
	if { $stepName != "No Step" } {
	    $itk_component(menubar) insert .$currentStepMenu.sep2 \
		checkbutton $token \
		-label $stepName \
		-variable $stepVar \
		-command "[itcl::code $this select] $token" \
		-selectcolor green \
		-helpstr [concat "Go to the \" " $stepName "\" page"]

	    #
	    # Add status variable to the list
	    #
	    lappend statusVariables $stepVar

	} else {
	    #    puts "Page does not require user interaction"
	}

	lappend enabledPages $token

	return $pageName
    }

    #
    # disable
    #
    # Disables a page. The step is removed from the steps menu and
    # the token is removed from the list of enabled pages.
    #
    itcl::body Wizard::disable {token} {

	#
	# Change to the intro page. It won't go away
	#
	select intro

	if { $token == "intro" } {
	    return
	}

	set index [lsearch $enabledPages $token]

	if { $index >= 0 } {
	    #
	    # delete menu entry
	    #
	    $itk_component(menubar) delete .$currentStepMenu.$token

	    #
	    # delete from list of enabled pages.
	    #
	    set enabledPages [ lreplace $enabledPages $index $index ]
	}
    }

    #
    # openSteps - opens the step menu.
    #
    itcl::body Wizard::openSteps {} {
	#
	# Generate a unique name for the steps menu. They cannot
	# be reused even if they are deleted. It seems odd, but its
	# is true.
	#
	set stepMenu ""
	append stepMenu "step" $stepCount

	incr stepCount

	set currentStepMenu $stepMenu
	set statusVariables ""

	$itk_component(menubar) add menubutton $stepMenu \
	    -text Steps \
	    -menu {
		options -tearoff true -selectcolor blue
	    }

	$itk_component(menubar) add command .$stepMenu.goGreet \
	    -label "Greeting" \
	    -helpstr "Return to the greeting page." \
	    -command "[itcl::code $this select intro]"

	$itk_component(menubar) add separator .$stepMenu.sep1

	$itk_component(menubar) add separator .$stepMenu.sep2

	$itk_component(menubar) add command .$stepMenu.fb \
	    -label "Configure Framebuffer" \
	    -helpstr "Configure the framebuffer" \
	    -command "[itcl::code $this select fbp]"
    }

    #
    # closeSteps - closes the step menu. The step menu should be
    #              active only when there is an active picture
    #              process.
    #
    itcl::body Wizard::closeSteps { } {
	if { [string length $currentStepMenu] > 0 } {
	    $itk_component(menubar) delete $currentStepMenu
	    set currentStepMenu ""
	}
	set statusVariables ""
    }

    #
    # getIntroPage - returns the text widget in the intro page
    #                so that the text may be modified.
    #
    itcl::body Wizard::getIntroPage { } {

    }


    #
    # UpdateRC - update .rtwizardrc file with current info. 
    #
    itcl::body Wizard::UpdateRC { } {
	set existing_file 0
	if {[info exists ::env(RTWIZARD_RCFILE)]} {
	    if {[file exists $::env(RTWIZARD_RCFILE)]} {
	      set fp [open $::env(RTWIZARD_RCFILE) w]
	      set existing_file $::env(RTWIZARD_RCFILE)
	    }
        } else {
	    if {[info exists ::env(HOME)]} {
		set homefile [file join $::env(HOME) .rtwizardrc]
	        if {[file exists $homefile]} {
		  set fp [open $homefile w]
	          set existing_file $homefile
		}
	   } else {
	       if {[file exists ".rtwizardrc"]} {
	         set fp [open ".rtwizardrc" w]
	         set existing_file ".rtwizardrc"
	       }
	   }
        }
	if {$existing_file == 0} {
	    if {[info exists ::env(HOME)]} {
		set homefile [file join $::env(HOME) .rtwizardrc]
	        set fp [open $homefile w]
	        set existing_file $homefile
	    } else {
	        set fp [open ".rtwizardrc" w]
	        set existing_file ".rtwizardrc"
	    }
        }

	eval $pages($currentPageToken) onDeselect
	eval $pages($currentPageToken) onSelect
	set cwidth [winfo width .]
	set cheight [winfo height .]
	puts $fp "set ::wizard_width $cwidth"
	puts $fp "set ::wizard_height $cheight"
	puts $fp "set ::gpane [list $::gpane]"
	close $fp
	tk_messageBox -message "Updated RtWizard configuration file is located at $existing_file" -type ok
    }


    #
    # fullSize - front-end to the type specific method
    #
    itcl::body Wizard::fullSize { } {
	set obj [$::exp getCurrentTypeObj]
	namespace inscope ::RtWizard::ExamplePage $obj fullSize
    }

    #
    # preview - front-end to the type specific method
    #
    itcl::body Wizard::preview { } {
	set obj [$::exp getCurrentTypeObj]
	namespace inscope ::RtWizard::ExamplePage $obj preview
    }

    #
    # updateRenderMenu - activates or deactivates render menu
    #                    based on the status variables
    #
    itcl::body Wizard::updateRenderMenu {} {

	if {[llength $statusVariables] == 0} {
	    return
	}

	set state 1
	foreach var $statusVariables {

	    #
	    # Retrieve the value of the variable whose name is
	    # stored in $var.
	    #
	    upvar #0 $var val

	    if { $state == 1 && $val == 1} {
		set state 1
	    } else {
		set state 0
	    }
	}

	if {$state == 1} {
	    $itk_component(menubar) menuconfigure render \
		-state normal
	} else {
	    $itk_component(menubar) menuconfigure render \
		-state disabled
	}
    }

    itcl::body Wizard::activateMenu {token} {
	catch {$itk_component(menubar) menuconfigure $token \
		   -state normal}
    }

}; # end namespace


# Local Variables:
# mode: Tcl
# tab-width: 8
# c-basic-offset: 4
# tcl-indent-level: 4
# indent-tabs-mode: t
# End:
# ex: shiftwidth=4 tabstop=8
