# recall.tcl --
#
#	The Recall Window and related commands.
#

namespace eval NSRecall {

variable RecallPriv

# NSRecall::InitModule --
#
#	One-time-only-ever initialization.
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc InitModule {} {

	variable RecallPriv

	if {[winfo screenwidth .] > 800} {
		set RecallPriv(font) {{MS Sans Serif} 10}
	} else {
		set RecallPriv(font) {{MS Sans Serif} 8}
	}
	set RecallPriv(icon,valid) 0

	NSObject::New NSRecall
}

# NSRecall::NSRecall --
#
#	Object constructor called by NSObject::New().
#
# Arguments:
#	oop					OOP ID. See above.
#
# Results:
#	.

proc NSRecall oop {

	global Notice
	global NSRecall
	global Windows

	InitWindow $oop

	set win [Info $oop win]

	qebind $win <Choose-item> "
		if !%s {
			NSRecall::SetHook $oop {}
		}
	"

	qebind $win <Inkey-item> "
		NSRecall::Info $oop display,what \[angband inkey_other]
		NSRecall::SetHook $oop recall_hook_inventory
	"

	qebind $win <Choose-mindcraft> "
		if %s {
			NSRecall::SetHook $oop recall_hook_mindcraft
		} else {
			NSRecall::SetHook $oop {}
		}
	"

	qebind $win <Choose-power> "
		if %s {
			NSRecall::SetHook $oop recall_hook_power
		} else {
			NSRecall::SetHook $oop {}
		}
	"

	qebind $win <Choose-spell> "
		if %s {
			NSRecall::Info $oop display,what %o
			NSRecall::SetHook $oop recall_hook_spell
		} else {
			NSRecall::SetHook $oop {}
		}
	"

	qebind $win <Term-fresh> \
		"NSRecall::Fresh_Display $oop"	
	qeconfigure $win <Term-fresh> -active no
	if $::DEBUG {
		set ::debug_display 0
	}

	qebind $win <Setting-depth_in_feet> \
		"NSRecall::Redraw $oop"

	qebind $win <Track-race> \
		"NSRecall::Recall $oop r_info %w"

	Info $oop hook ""
	Info $oop busy 0
	Info $oop expanded 0
	Info $oop current ""

	# Kind of information currently displayed
	Info $oop display ""

	#
	# Global list of application windows
	#

	set Windows(recall,win) [Info $oop win]
	set Windows(recall,class) NSRecall
	set Windows(recall,oop) $oop
}

# NSRecall::Info --
#
#	Query and modify info.
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc Info {oop info args} {

	global NSRecall

	# Set info
	if {[llength $args]} {
		switch -- $info {
			default {
				set NSRecall($oop,$info) [lindex $args 0]
			}
		}

	# Get info
	} else {
		switch -- $info {
			default {
				return $NSRecall($oop,$info)
			}
		}
	}
}

# NSRecall::InitWindow --
#
#	Create a recall window.
#
# Arguments:
#	oop					OOP ID. See above.
#
# Results:
#	.

proc InitWindow oop {

	global NSRecall
	global NSMenu
	global Windows
	variable RecallPriv

	set win .recall$oop

	toplevel $win -height 400 -width 400
	wm title $win Recall
	wm transient $win $Windows(main,win)

	# Feed the Term when keys are pressed
	Term_KeyPress_Bind $win
	
	# Do stuff when window closes
	wm protocol $win WM_DELETE_WINDOW "NSRecall::Close $oop"

	# This window plays sound
	SoundWindow $win

	# Start out withdrawn (hidden)
	wm withdraw $win

	# Turn off geometry propagation for the window
	pack propagate $win no

	# Set instance variables
	set NSRecall($oop,win) $win

	set frame $win.recall
	frame $frame -relief sunken -borderwidth 1 -background Black

	# Canvas to display icon
	set iconSize [expr [icon size] + 8]
	set canvas $frame.icon
	canvas $canvas \
		-borderwidth 0 -width $iconSize -height $iconSize -background Black \
		-highlightthickness 0
	$canvas create widget \
		6 6 -type none -index 0 -tags icon

	# If the icon of the displayed monster/object changes, we must
	# update the canvas Widget item. This binding does it.
	qebind $canvas <Assign> \
		"NSRecall::IconChanged $oop %d %I %t %i"

	text $frame.text \
		-wrap word -width 82 -height 30 -font $RecallPriv(font)\
		-borderwidth 0 -setgrid no -highlightthickness 0 \
		-padx 4 -pady 2 -background Black -foreground White -cursor ""
	bindtags $frame.text [list $frame.text $win all]

	pack $frame \
		-expand yes -fill both

	grid rowconfig $frame 0 -weight 1 -minsize 0
	grid columnconfig $frame 0 -weight 0 -minsize 0
	grid columnconfig $frame 1 -weight 1 -minsize 0

	grid $frame.icon -in $frame \
		-row 0 -column 0 -rowspan 1 -columnspan 1 -sticky ns
	grid $frame.text -in $frame \
		-row 0 -column 1 -rowspan 1 -columnspan 1 -sticky news

	# Set instance variables
	Info $oop text $frame.text

	# Window expands and contracts as the mouse enters and leaves it
	bindtags $win [concat [bindtags $win] RecallBindTag]
	bind RecallBindTag <Enter> "NSRecall::Expand $oop"
	bind RecallBindTag <Leave> "NSRecall::Contract $oop"

	# Fiddle with the selection for list behaviour
	$frame.text tag configure HOT -foreground White \
		-background [NSColorPreferences::Get listHilite]

	$frame.text tag bind HOT <ButtonPress-1> \
		"NSRecall::Invoke $oop \[$frame.text index {@%x,%y linestart}]"
	$frame.text tag bind TEXT <Motion> \
		"NSRecall::Motion $oop \[$frame.text index {@%x,%y linestart}]"
	$frame.text tag bind HOT <Leave> \
		"NSRecall::Motion $oop {}"
}

# NSRecall::Close --
#
#	Description. 
#
# Arguments:
#	oop					OOP ID. See above.
#
# Results:
#	.

proc Close oop {
	bell
}

# NSRecall::Recall --
#
#	Dump artifact/monster memory.
#
# Arguments:
#	oop					OOP ID. See above.
#	info					a_info or r_info.
#	index					Index into a_info[] or r_info[] C arrays, or zero
#
# Results:
#	What happened.

proc Recall {oop info index} {

	variable RecallPriv

	if {[Info $oop hook] != ""} return

	if $index {

		# Aritfact/Monster Icon and Name (color?)
		angband $info info $index attrib
		set icon $attrib(icon)
		set name $attrib(name):
	
		# Artifact/Monster Memory
		set memory [angband $info memory $index]

	} else {

		set icon [list none 0]
		set name [set memory ""]
	}

	if {$info == "r_info"} {
		set RecallPriv(icon,to) monster
		set color Red
	} else {
		set RecallPriv(icon,to) object
		set color [default_tval_to_attr $attrib(tval)]
	}

	# Set text
	SetText $oop $icon $color $name $memory

	set RecallPriv(icon,toindex) $index
	set RecallPriv(icon,valid) 1
}

# NSRecall::SetText --
#
#	
#
# Arguments:
#	oop					OOP ID. See above.
#
# Results:
#	What happened.

proc SetText {oop icon color title text} {

	global NSRecall
	variable RecallPriv

	if {[Info $oop hook] != ""} return

	# Display the icon
	$NSRecall($oop,win).recall.icon itemconfigure icon \
		-type [lindex $icon 0] -index [lindex $icon 1]

	set textBox $NSRecall($oop,text)

	# Delete
	$textBox delete 1.0 end

	# Insert title if any
	if {[string compare $title ""]} {

		# Title (color?)
		$textBox insert end $title\n
		$textBox tag add TAG_STYLE 1.0 {end -1 chars}
		$textBox tag configure TAG_STYLE -foreground $color
	}

	# Insert text if any
	if {[string compare $text ""]} {

		# Text
		$textBox insert end $text
	}

	set RecallPriv(icon,valid) 0
}

# NSRecall::IconChanged --
#
#	The icon of the recalled monster/object is displayed in the
#	Recall Window. If that monster or object is assigned a different
#	icon, we want to update the display. This is called as a
#	qebind command on the "Assign" quasi-event.
#
# Arguments:
#	oop					OOP ID. See above.
#
# Results:
#	What happened.

proc IconChanged {oop to toindex type index} {

	variable RecallPriv

	if !$RecallPriv(icon,valid) return
	if {$to == $RecallPriv(icon,to) && $toindex == $RecallPriv(icon,toindex)} {
		[Info $oop win].recall.icon itemconfigure icon \
			-type $type -index $index
	}		
}

# NSRecall::Redraw --
#
#	Called as a qebind <Setting-depth_in_feet> script.
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc Redraw oop {

	variable RecallPriv

	# Hack -- Not a monster/artifact
	if !$RecallPriv(icon,valid) return

	# Hack -- Not a monster
	if {$RecallPriv(icon,to) != "monster"} return

	# Hack -- Redisplay
	Recall $oop r_info $RecallPriv(icon,toindex)
}

# NSRecall::SetHook --
#
#	Set the hook.
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc SetHook {oop hook} {

	if {$hook != ""} {
		Info $oop hook NSRecall::$hook
		CallHook $oop open
		qeconfigure [Info $oop win] <Term-fresh> -active yes
		if $::DEBUG {
			set ::debug_display 1
		}
	} elseif {[Info $oop hook] != ""} {
		Info $oop hook ""
		Restore $oop
		qeconfigure [Info $oop win] <Term-fresh> -active no
		if $::DEBUG {
			set ::debug_display 0
		}
	}
}

# NSRecall::CallHook --
#
#	Call the hook.
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc CallHook {oop message args} {

	uplevel #0 eval [Info $oop hook] $oop $message [list $args]
}

# NSRecall::Fresh_Display --
#
#	Calls the hook to set the list, if required. Called as a command
#	on the "Term-fresh" quasi-event.
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc Fresh_Display oop {

	ASSERT {$::debug_display == 1} \
		"Fresh_Display called with debug_display=0!"

	CallHook $oop fresh

	# If the cursor is inside the Recall Window, we will attempt to
	# expand it.
	set pointerx [winfo pointerx .]
	set pointery [winfo pointery .]
	set toplevel [winfo containing $pointerx $pointery]
	if {($toplevel != "") && ([winfo toplevel $toplevel] == [Info $oop win])} {
		Expand $oop
	}
}

# NSRecall::SetList --
#
#	Clears the recall text, sets the icon to "none 0" and calls the
#	hook to set the text.
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc SetList oop {

	set win [Info $oop win]
	set textBox [Info $oop text]

	# Clear the text
	$textBox delete 1.0 end

	# Clear the icon
	$win.recall.icon itemconfigure icon -type none -index 0

	# Call the hook to set the list
	CallHook $oop set_list

	# Something is displayed
	Info $oop display something

	# No item is selected
	Info $oop current ""	
}

# NSRecall::Invoke --
#
#	. 
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc Invoke {oop index} {

	set textBox [Info $oop text]
	set row [expr [lindex [split $index .] 0] - 1]

	CallHook $oop invoke $row
}

proc Motion {oop index} {

	set textBox [Info $oop text]

	# If you invoke an item, hold down the mouse, and drag...
	if {[Info $oop hook] == ""} return

	# See if the item has changed
	if {$index == [Info $oop current]} return

	if {[Info $oop current] != ""} {
		UnhighlightItem $oop [Info $oop current]
	}

	if {$index != ""} {
		HighlightItem $oop $index
	}

	Info $oop current $index
}

proc HighlightItem {oop index} {

	set textBox [Info $oop text]
	set row [expr [lindex [split $index .] 0] - 1]

	# Highlight the item
	$textBox tag add HOT $index "$index lineend"
$textBox tag raise HOT

	# Call the hook to set the icon, perhaps
	CallHook $oop highlight $row
}

proc UnhighlightItem {oop index} {

	set win [Info $oop win]
	set textBox [Info $oop text]

	# Unhighlight the item
	$textBox tag remove HOT 1.0 end

	# Clear the icon
	$win.recall.icon itemconfigure icon -type none -index 0
}

proc Expand oop {

	variable RecallPriv

#	if {[Info $oop hook] == ""} return
	if {[Info $oop busy]} return
	if {[Info $oop expanded]} return

	set win [Info $oop win]
	set textBox [Info $oop text]

	set textHeight [winfo height $textBox]
	set lineHeight [font metrics $RecallPriv(font) -linespace]

	# Hack -- In order to find out how much space is taken up by the
	# text in the text widget, I create a canvas text item with the
	# proper attributes and calculate its size. The width is width-8
	# and height-4 because of the internal padding of the text
	# widget. I added 2 to each adjustment as a hack.
	set itemId [$win.recall.icon create text 1 1 -font $RecallPriv(font) \
		-width [expr [winfo width $textBox] - 8 - 2] -anchor nw \
		-text [$textBox get 1.0 end]]
	set bbox [$win.recall.icon bbox $itemId]
	set height [expr [lindex $bbox 3] - [lindex $bbox 1] + 4 + 2]

	# Hmmm... Is there a trailing newline, or what?
	incr height -$lineHeight

	# Delete the temp canvas item
	$win.recall.icon delete $itemId

	set winHeight [winfo height $win]
	set winWidth [winfo width $win]

	if {$height <= $winHeight} return

	# Save the current window geometry
	Info $oop geometry [wm geometry $win]

	Info $oop busy 1

	raise $win
	wm geometry $win ${winWidth}x$height+[winfo x $win]+[expr [winfo y $win] - ($height - $winHeight)]
	update

	Info $oop expanded 1
	Info $oop busy 0
}

proc Contract oop {

	if {[Info $oop busy]} return
	if {![Info $oop expanded]} return

	Info $oop busy 1

	set win [Info $oop win]
	wm geometry $win [Info $oop geometry]
	update

	Info $oop expanded 0
	Info $oop busy 0
}

proc Restore oop {

	if {[Info $oop display] == ""} return
	SetText $oop {none 0} {} {} {}
	Contract $oop
	Info $oop display ""
}

proc recall_hook_inventory {oop message args} {

	switch -- $message {

		open {
		}

		fresh {
			SetList $oop
		}

		close {
		}

		set_list {
		
			set textBox [Info $oop text]
		
			# Get the list of matching item indexes
			set invOrEquip [Info $oop display,what]
			set itemList [angband $invOrEquip find -tester yes]
				
			# Process each item
			foreach index $itemList {
		
				# Get item info
				angband $invOrEquip info $index attrib

				if {$invOrEquip == "floor"} {
					set attrib(char) [string index "abcdefghijklmnopqrstuvw" [lsearch -exact $itemList $index]]
				}
		
				# Get the color
				set color [default_tval_to_attr $attrib(tval)]
		
				# Append the character and description
				$textBox insert end "$attrib(char) $attrib(name)" \
					[list ITEM_$index TEXT] "\n"
				$textBox tag configure ITEM_$index -foreground $color
			}
		
			# Delete trailing newline
			$textBox delete "end - 1 chars"
		
			# Keep a list of inventory indexes
			Info $oop match $itemList
		}

		get_color {

			set row [lindex $args 0]
			set index [lindex [Info $oop match] $row]
			angband [Info $oop display,what] info $index attrib
			return [default_tval_to_attr $attrib(tval)]
		}

		invoke {

			set row [lindex $args 0]
			set index [lindex [Info $oop match] $row]
			if {[Info $oop display,what] == "floor"} {
				set attrib(char) [string index "abcdefghijklmnopqrstuvw" $row]
			} else {
				angband [Info $oop display,what] info $index attrib
			}
			angband keypress $attrib(char)
		}

		highlight {

			set row [lindex $args 0]
			set index [lindex [Info $oop match] $row]
			angband [Info $oop display,what] info $index attrib
			scan $attrib(icon) "%s %d" type index
			[Info $oop win].recall.icon itemconfigure icon \
				-type $type -index $index
		}
	}
}

proc recall_hook_mindcraft {oop message args} {

	switch -- $message {

		open {
		}

		fresh {
			SetList $oop
		}

		close {
		}

		set_list {
		
			set textBox [Info $oop text]
		
			# Get the list of mindcraft powers
			set powerList [angband mindcraft get]

			# Keep a list of invoke chars
			set match {}

			# Process each power
			foreach power $powerList {

				angband mindcraft info $power attrib
				if !$attrib(okay) continue

				# Append the character and description
				$textBox insert end "$attrib(char)\) $attrib(name)" \
					[list POWER_$attrib(char) TEXT] "\n"
				$textBox tag configure POWER_$attrib(char) -foreground White

				# Keep a list of spell chars
				lappend match $attrib(char)
			}
		
			# Delete trailing newline
			$textBox delete "end - 1 chars"
		
			# Keep a list of inventory indexes
			Info $oop match $match
		}

		get_color {

			return White
		}

		invoke {

			set row [lindex $args 0]
			set char [lindex [Info $oop match] $row]
			angband keypress $char
		}

		highlight {
		}
	}
}


proc recall_hook_power {oop message args} {

	switch -- $message {

		open {
		}

		fresh {
			SetList $oop
		}

		close {
		}

		set_list {
		
			set textBox [Info $oop text]
		
			# Get the list powers
			set powerList [angband power get]

			# Keep a list of invoke chars
			set match {}

			set alpha "abcdefghijklmnopqrstuvwxyz"
			set i 0

			# Process each power
			foreach power $powerList {

				angband power info $power attrib
				set attrib(char) [string index $alpha $i]

				# Append the character and description
				$textBox insert end "$attrib(char)\) $attrib(name)" \
					[list POWER_$attrib(char) TEXT] "\n"
				$textBox tag configure POWER_$attrib(char) -foreground White

				# Keep a list of spell chars
				lappend match $attrib(char)

				incr i
			}
		
			# Delete trailing newline
			$textBox delete "end - 1 chars"
		
			# Keep a list of inventory indexes
			Info $oop match $match
		}

		get_color {

			return White
		}

		invoke {

			set row [lindex $args 0]
			set char [lindex [Info $oop match] $row]
			angband keypress $char
		}

		highlight {
		}
	}
}

proc recall_hook_spell {oop message args} {

	switch -- $message {

		open {
		}

		fresh {
			SetList $oop
		}

		close {
		}

		set_list {
		
			set textBox [Info $oop text]
		
			# Get the list of matching item indexes
			set bookNum [Info $oop display,what]
			set spellList [angband spell $bookNum]

			# Keep a list of spell chars
			set match {}

			# Process each spell
			foreach spell $spellList {

				# TO DO: Rewrite spell command to return array
				set attrib(known) [lindex $spell 6]
				set attrib(char) [lindex $spell 0]
				set attrib(name) [lindex $spell 1]

				# Can we cast this spell?
				if !$attrib(known) continue
		
				# Append the character and description
				$textBox insert end "$attrib(char) $attrib(name)" \
					[list SPELL_$attrib(char) TEXT] "\n"
				$textBox tag configure ITEM_$attrib(char) -foreground White

				# Keep a list of spell chars
				lappend match $attrib(char)
			}
		
			# Delete trailing newline
			$textBox delete "end - 1 chars"
		
			# Keep a list of inventory indexes
			Info $oop match $match
		}

		get_color {

			return White
		}

		invoke {

			set row [lindex $args 0]
			set char [lindex [Info $oop match] $row]
			angband keypress $char
		}

		highlight {
		}
	}
}

proc recall_hook_xxx {oop message args} {

	switch -- $message {

		set_list {
		}

		get_color {
		}
	}
}

# namespace eval NSRecall
}
