# choose-monster.tcl --
#
#	The Choose-Monster Window and related commands.
#

namespace eval NSChooseMonster {

variable Priv

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

proc InitModule {} {

	variable Priv
	
	set Priv(find,string) ""

	NSObject::New NSChooseMonster
}

# NSChooseMonster::NSChooseMonster --
#
#	Object constructor called by NSObject::New().
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc NSChooseMonster oop {

	global Windows

	InitWindow $oop

	Info $oop group,current -1
	Info $oop member,current -1

	set win [Info $oop win]

	# The user can type ESCAPE to cancel
	# The user can type ENTER to use the selected monster (if any)
	# The user can type the d_char of any monster race (if valid)
	bind $win <KeyPress-Escape> \
		"NSChooseMonster::Close $oop"
	bind $win <KeyPress-Return> \
		"NSChooseMonster::Accept $oop"
	bind $win <KeyPress> \
		"NSChooseMonster::TrySymbol $oop %A"

	# Searching
	bind $win <Control-KeyPress-f> \
		"NSChooseMonster::Find $oop 0"
	bind $win <Control-KeyPress-g> \
		"NSChooseMonster::Find $oop 1"

	NSWindowManager::RegisterWindow choosemonster $win \
		"NSChooseMonster::GeometryCmd $oop" "" \
		"NSChooseMonster::DisplayCmd $oop"		

	#
	# Global list of application windows
	#

	set Windows(choosemonster,win) [Info $oop win]
	set Windows(choosemonster,class) NSChooseMonster
	set Windows(choosemonster,oop) $oop
}

# NSChooseMonster::~NSChooseMonster --
#
#	Object destructor called by NSObject::Delete().
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc ~NSChooseMonster oop {

	NSValueManager::RemoveClient listBG [Info $oop group,clientId]
	NSValueManager::RemoveClient listBG [Info $oop member,clientId]
}

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

proc Info {oop info args} {

	global NSChooseMonster

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

	# Get info
	} else {
		switch -- $info {
			groupCanvas {
				set canvistId [Info $oop group,canvistId]
				return [NSCanvist::Info $canvistId canvas]
			}
			memberCanvas {
				set canvistId [Info $oop member,canvistId]
				return [NSCanvist::Info $canvistId canvas]
			}
			default {
				return $NSChooseMonster($oop,$info)
			}
		}
	}
}

# NSChooseMonster::InitWindow --
#
#	Create the window.
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc InitWindow oop {

	global Windows

	set win .choosemonster$oop
	toplevel $win
	wm title $win "Choose Monster"

	wm transient $win $Windows(main,win)

	# Do stuff when window closes
	wm protocol $win WM_DELETE_WINDOW "NSChooseMonster::Close $oop"

	Info $oop win $win

	InitMenus $oop

	#
	# Group List
	#

	set font [Global font,fixed,normal]
	set cw [font measure $font "W"]
	set width [expr $cw * 26]
	set iconSize [expr [icon size] + 8]

	frame $win.frameGroup \
		-borderwidth 1 -relief sunken
	set canvistId [NSObject::New NSCanvist $win.frameGroup $iconSize $width 300 \
		"NSChooseMonster::NewItemCmd $oop" "NSChooseMonster::HighlightItemCmd $oop"]
	set canvas [NSCanvist::Info $canvistId canvas]
	$canvas configure -height [expr 40 * 5]
	$canvas configure -background [NSColorPreferences::Get listBG]
	$canvas configure -yscrollcommand "$win.frameGroup.scroll set"
	scrollbar $win.frameGroup.scroll \
		-borderwidth 0 -command "$canvas yview" -orient vert
	bind $win.frameGroup.scroll <Map> "eval %W set \[$canvas yview]"

	# Do something when a group item is selected
	NSCanvist::Info $canvistId selectionCmd \
		"NSChooseMonster::SelectionChanged_Group $oop"

	# This call updates the list background color whenever the
	# global list background color changes
	Info $oop group,clientId \
		[NSValueManager::AddClient listBG "ListBackgroundChanged $canvas"]

	pack $win.frameGroup.scroll -side right -fill y
	pack $canvas -side left -expand yes -fill both -anchor nw

	Info $oop group,canvistId $canvistId

	#
	# Member List
	#

	set width [expr $cw * 40]

	frame $win.frameMember \
		-borderwidth 1 -relief sunken
	set canvistId [NSObject::New NSCanvist $win.frameMember $iconSize $width 300 \
		"NSChooseMonster::NewItemCmd $oop" "NSChooseMonster::HighlightItemCmd $oop"]
	set canvas [NSCanvist::Info $canvistId canvas]
	$canvas configure -height [expr 40 * 5]
	$canvas configure -background [NSColorPreferences::Get listBG]
	$canvas configure -yscrollcommand "$win.frameMember.scroll set"
	scrollbar $win.frameMember.scroll \
		-borderwidth 0 -command "$canvas yview" -orient vert
	bind $win.frameMember.scroll <Map> "eval %W set \[$canvas yview]"

	# This call updates the list background color whenever the
	# global list background color changes
	Info $oop member,clientId \
		[NSValueManager::AddClient listBG "ListBackgroundChanged $canvas"]

	# Do something when a member item is invoked.
	NSCanvist::Info $canvistId invokeCmd \
		"NSChooseMonster::Invoke_Member $oop"

	# Do something when a member item is selected.
	NSCanvist::Info $canvistId selectionCmd \
		"NSChooseMonster::SelectionChanged_Member $oop"

	Info $oop member,canvistId $canvistId

	pack $win.frameMember.scroll -side right -fill y
	pack $canvas -side left -expand yes -fill both -anchor nw

	#
	# Geometry
	#

	grid rowconfig $win 0 -weight 1
	grid columnconfig $win 0 -weight 0
	grid columnconfig $win 1 -weight 1
 
	grid $win.frameGroup -in $win \
		-row 0 -column 0 -rowspan 1 -columnspan 1 -sticky ns
	grid $win.frameMember -in $win \
		-row 0 -column 1 -rowspan 1 -columnspan 1 -sticky news
}

# NSChooseMonster::InitMenus --
#
#	Create the menus.
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc InitMenus oop {

	global NSMenu

	set win [Info $oop win]
	set mod "Ctrl"

	#
	# Menu bar
	#

	set menuDef "-tearoff 0 -postcommand \"NSChooseMonster::SetupMenus $oop\" \
		-identifier MENUBAR"
	set mbar [NSObject::New NSMenu $win $menuDef]
	Info $oop mbar $mbar

	#
	# Choose
	#

	NSObject::New NSMenu $mbar {-tearoff 0 -identifier MENU_CHOOSE}
	NSMenu::MenuInsertEntry $mbar -end MENUBAR {-type cascade \
		-menu MENU_CHOOSE -label "Choose" -underline 0 \
		-identifier M_CHOOSE}

	set entries {}
	lappend entries "-type command -label \"Find...\" \
		-command \"NSChooseMonster::Find $oop 0\" \
		-accelerator $mod+F -underline 0 -identifier E_FIND"
	lappend entries "-type command -label \"Find Again\" \
		-command \"NSChooseMonster::Find $oop 1\" \
		-accelerator $mod+G -underline 6 -identifier E_FIND_AGAIN"
	lappend entries "-type separator"
	lappend entries "-type command -label \"Close\" \
		-command \"NSChooseMonster::Close $oop\" -underline 0 \
		-accelerator $mod+W -identifier E_CLOSE"
	
	NSMenu::MenuInsertEntries $mbar -end MENU_CHOOSE $entries
}

# NSChooseMonster::SetupMenus --
#
#	Prepare to post the menus.
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc SetupMenus {oop mbarID} {

	lappend identList E_FIND E_FIND_AGAIN E_CLOSE
		
	NSMenu::MenuEnable $mbarID $identList
}

# NSChooseMonster::DisplayCmd --
#
#	Called by NSWindowManager::Display().
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc DisplayCmd {oop message first} {

	set canvas [Info $oop groupCanvas]

	switch -- $message {
		preDisplay {
			SetHook $oop choose_hook_monster
		}
		postDisplay {
			focus $canvas
		}
		postWithdraw {
			Unmap $oop
		}
	}
}

# NSChooseMonster::GeometryCmd --
#
#	Called by NSWindowManager::Setup(). Returns the desired (default)
#	geometry for the window.
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc GeometryCmd oop {

	global Windows

	set win [Info $oop win]
	set winMain $Windows(main,win)
	set winMicro $Windows(micromap,win)
	set x [winfo x $winMain]
	set y [winfo y $winMain]
	set width [NSToplevel::ContentWidth $win [expr [NSToplevel::EdgeRight $winMicro] - $x]]
	set height [NSToplevel::ContentHeight $win [expr [NSToplevel::EdgeBottom $winMain] - $y]]
	return ${width}x$height+$x+$y
}

# NSChooseMonster::Close --
#
#	Do something when closing the window.
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc Close oop {

	angband keypress \033
}

# NSChooseMonster::Unmap --
#
#	Do something when unmapping the window.
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc Unmap oop {

	set canvistId [Info $oop group,canvistId]
	NSCanvist::Delete $canvistId 0 end

	set canvistId [Info $oop member,canvistId]
	NSCanvist::Delete $canvistId 0 end
}

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

proc SetHook {oop hook} {

	Info $oop hook $hook
	SetList_Group $oop
}

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

proc CallHook {oop message args} {

	uplevel #0 NSChooseMonster::[Info $oop hook] $oop $message $args
}

# NSChooseMonster::SelectionChanged_Group --
#
#	When a "group" list item is selected, display members in the
#	"member" list.
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc SelectionChanged_Group {oop canvistId select deselect} {

	# If nothing was selected, clear the member list
	if {![llength $select]} {
		set canvistId [Info $oop member,canvistId]
		NSCanvist::Delete $canvistId 0 end
		Info $oop group,current -1
		return
	}

	# Get the (first) row
	set row [lindex $select 0]
	Info $oop group,current $row

	# Display members in that group
	SetList_Member $oop $row
}

# NSChooseMonster::SelectionChanged_Member --
#
#	Do something when a "member" list item is selected.
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc SelectionChanged_Member {oop canvistId select deselect} {

	# Do nothing if no new row was selected
	if {![llength $select]} {
		Info $oop member,current -1
		return
	}

	# Get the (first) row
	set row [lindex $select 0]
	Info $oop member,current $row

	# Call the hook to do stuff
	CallHook $oop select_member $row
}

# NSChooseMonster::Invoke_Member --
#
#	Do something when a "member" list item is invoked.
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc Invoke_Member {oop canvistId x y} {

	set row [NSCanvist::PointToRow $canvistId $x $y]
	if {$row == -1} return

	set r_idx [lindex [Info $oop member,match] $row]
	angband r_info info $r_idx attrib
	angband keypress $attrib(d_char)
}

# NSChooseMonster::Accept --
#
#	If the user types ENTER, and a monster is selected, then
#	"angband keypress" the monster's d_char.
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc Accept oop {

	set row [Info $oop member,current]
	if {$row == -1} return
	
	set r_idx [lindex [Info $oop member,match] $row]
	angband r_info info $r_idx attrib
	angband keypress $attrib(d_char)
}

# NSChooseMonster::TrySymbol --
#
#	If the given symbol is a valid monster d_char, then
#	"angband keypress" it.
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc TrySymbol {oop symbol} {

	if {[string length $symbol] != 1} return
	set match [angband r_info find -d_char $symbol -limit 1 -unique no]
	if {![llength $match]} return
	angband keypress $symbol
}

# NSChooseMonster::SetList_Group --
#
#	Set the group list.
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc SetList_Group oop {

	set win [Info $oop win]
	set canvistId [Info $oop group,canvistId]

	# Clear the list
	NSCanvist::Delete $canvistId 0 end

	# Call hook to set the group list
	CallHook $oop set_list_group

	Info $oop group,current -1

	# Hack -- Clear the "member" list
	NSCanvist::Delete [Info $oop member,canvistId] 0 end
}

# NSChooseMonster::SetList_Member --
#
#	Set the member list.
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc SetList_Member {oop group} {

	set canvistId [Info $oop member,canvistId]

	# Clear the list
	NSCanvist::Delete $canvistId 0 end

	Info $oop member,current -1

	# Call hook to set the member list
	CallHook $oop set_list_member $group
}

# NSChooseMonster::NewItemCmd --
#
#	Called by NSCanvist::InsertItem() to create a list row.
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc NewItemCmd {oop canvistId y icon text} {

	set c [NSCanvist::Info $canvistId canvas]
	set lineHeight [NSCanvist::Info $canvistId rowHgt]
	set font [Global font,fixed,normal]

	set cw [font measure $font "W"]
	set fw [font measure $font $text]
	set fh [font metrics $font -linespace]
	set diff [expr int([expr ($lineHeight - $fh) / 2])]

	# Selection rectangle around everything
	lappend itemIdList [$c create rectangle 2 [expr $y + 2] \
		[expr [winfo width $c] - 3] [expr $y + $lineHeight - 2] \
		-fill {} -outline {} -tags enabled -width 2.0]

	# Image
	set iw [icon size]
	set ih [icon size]
	set wid [expr [icon size] + 8]
	set xdiff [expr int([expr ($wid - $iw) / 2])]
	set ydiff [expr int([expr ($lineHeight - $ih) / 2])]
	lappend itemIdList [$c create widget $xdiff [expr $y + $ydiff] \
		-type [lindex $icon 0] -index [lindex $icon 1] -tags enabled]

	# Text
	set fill White
	lappend itemIdList [$c create text [expr $wid + 1] [expr $y + $diff] \
		-text $text -anchor nw -font $font -fill $fill -tags enabled]

	return $itemIdList
}

# NSChooseMonster::HighlightItemCmd --
#
#	Called by NSCanvist::Select() to highlight a row.
#
# Arguments:
#	oop					OOP ID. See above.
#	canvistId					OOP ID of NSCanvist object.
#	state					1 or 0 highlight state.
#	args					List of canvas item ids
#
# Results:
#	What happened.

proc HighlightItemCmd {oop canvistId state args} {

	set canvas [NSCanvist::Info $canvistId canvas]
	set itemIdList $args

	set idRect [lindex $itemIdList 0]

	if {[NSUtils::HasFocus $canvas]} {
		set fill [NSColorPreferences::Get listHilite]
	} else {
		set fill [NSColorPreferences::Get listInactive]
	}

	if $state {
		$canvas itemconfigure $idRect -outline $fill

	} else {
		$canvas itemconfigure $idRect -outline {}
	}
}

# NSChooseMonster::Find --
#
#	Simple search routine to look for a member by name.
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc Find {oop again} {

	variable Priv

	# Repeat the last search
	if {$again && [string length $Priv(find,string)]} {
		set string $Priv(find,string)
		incr Priv(find,member)

	# Enter a string to find, start from the beginning
	} else {

		# Ask the user for a name
		set string [NSUtils::StringBox "Find" $Priv(find,string) \
			Name "Cancel Find" [Info $oop win]]
		if {$string == ""} return

		# Clean up after the dialog
		update

		# Reset search parameters
		set Priv(find,string) $string
		set Priv(find,group) 0
		set Priv(find,member) 0
	}

	set group $Priv(find,group)
	set member $Priv(find,member)
	set max [NSCanvist::Info [Info $oop group,canvistId] count]

	# Compare lowercase
	set string [string tolower $string]

	for {set i $group} {$i < $max} {incr i} {

		set match [lrange [CallHook $oop member_list $i] $member end]

		foreach index $match {
	
			# Get the member name
			set name2 [CallHook $oop member_name $index]

			# Compare lowercase
			set name2 [string tolower $name2]
	
			# Found a match
			if {[string first $string $name2] != -1} {
	
				set canvistId [Info $oop group,canvistId]
	
				# The new group is not displayed
				if {![NSCanvist::IsRowSelected $canvistId $i]} {

					# Select the new group. As a side effect, the
					# SetList_Member() is called to display the monsters in
					# the group.
					NSCanvist::RemoveSelection $canvistId
					NSCanvist::UpdateSelection $canvistId $i {}
					NSCanvist::See $canvistId $i

				# The new group is already selected
				} else {

					# The found monster is in the currently-displayed
					# group, so remove the current selection
					NSCanvist::RemoveSelection [Info $oop member,canvistId]
				}
	
				set canvistId [Info $oop member,canvistId]
	
				# Select the matching member
				NSCanvist::UpdateSelection $canvistId $member {}
				NSCanvist::See $canvistId $member
	
				set Priv(find,group) $i
				set Priv(find,member) $member

				# Done
				return
			}
			incr member
		}
		set member 0
	}

	set Priv(find,group) 0
	set Priv(find,member) 0

	# If we aren't searching from the start, then wrap around if
	# a match isn't found
	if {$group || $member} {
		Find $oop 1
		return
	}
}

proc choose_hook_monster {oop message args} {

	global Windows

	switch -- $message {

		set_list_group {

			set canvistId [Info $oop group,canvistId]

			foreach {title findSpec} [Global groups,r_info] {

				# Skip unique monster group
				if {[string first "-unique yes" $findSpec] != -1} continue

				# Find the last monster in the group
				set match [eval angband r_info find -limit 1 \
					-backwards -unique no $findSpec]

				# Get the icon
				angband r_info info [lindex $match 0] attrib
				set icon $attrib(icon)
		
				# Add this group to the list
				NSCanvist::Insert $canvistId end $icon $title
			}
		}

		set_list_member {

			set canvistId [Info $oop member,canvistId]

			set group [lindex $args 0]
			set findSpec [lindex [Global groups,r_info] [expr $group * 2 + 1]]

			# Get a list of monsters in the group
			set match [eval angband r_info find -unique no $findSpec]

			# Add each match to the list
			foreach index $match {
		
				# Get the icon and name
				angband r_info info $index attrib
		
				# Append match to the list
				NSCanvist::Insert $canvistId end $attrib(icon) \
					"$attrib(name) ($attrib(d_char))"
			}

			# Keep a list of matching indexes
			Info $oop member,match $match
		}

		select_member {
			set row [lindex $args 0]
			set r_idx [lindex [Info $oop member,match] $row]
			NSRecall::Recall $Windows(recall,oop) r_info $r_idx
		}

		member_name {
			angband r_info info [lindex $args 0] attrib
			return $attrib(name)
		}

		member_list {
			set group [lindex $args 0]
			set findSpec [lindex [Global groups,r_info] [expr $group * 2 + 1]]
			return [eval angband r_info find -unique no $findSpec]
		}
	}
}

# namespace eval NSChooseMonster
}
