# message.tcl --
#
#	Implements the Message History Window(s) and related commands.
#

namespace eval NSMessage {

variable Priv

# NSMessage::NSMessage --
#
#	Create a message window.
#
# Arguments:
#	oop					OOP ID. See above.
#
# Results:
#	What happened.

proc NSMessage oop {

	global Windows
	variable Priv

	# Option: Combine identical messages
	Info $oop combine [Value messages,combine]
	
	InitWindow $oop

	set win [Info $oop win]
	bind $win <KeyPress-f> "NSMessage::Find $oop 0"
	bind $win <KeyPress-g> "NSMessage::Find $oop 1"
	bind $win <Control-KeyPress-w> "NSMessage::Close $oop"

	set Priv(find,string) ""

	#
	# Global list of application windows
	#

	set Windows(messages,win) [Info $oop win]
	set Windows(messages,class) NSMessage
	set Windows(messages,oop) $oop
}

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

proc Info {oop info args} {

	global NSMessage

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

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

# NSMessage::InitWindow --
#
#	Create the window associated with the object.
#
# Arguments:
#	oop					OOP ID. See above.
#
# Results:
#	What happened.

proc InitWindow oop {

	global Windows

	set win .messages$oop
	toplevel $win -height 400 -width 400
	wm title $win "Message History"

	wm transient $win $Windows(main,win)

	# Start out withdrawn (hidden)
	wm withdraw $win

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

	# This window plays sound
	SoundWindow $win

	# Set instance variables
	Info $oop win $win

	InitMenus $oop

	set frame $win.frameList
	frame $frame \
		-relief sunken -borderwidth 1

	set font [Global font,fixed,normal]

	set canvistId [NSObject::New NSTexist $frame $font 60 10]
	set canvas [NSTexist::Info $canvistId text]
	$canvas configure -background [NSColorPreferences::Get listBG]
	$canvas configure -yscrollcommand "$frame.yscroll set"
	$canvas configure -xscrollcommand "$frame.xscroll set"

	scrollbar $frame.yscroll \
		-orient vertical -command "$canvas yview" \
		-highlightthickness 0

	scrollbar $frame.xscroll \
		-orient horizontal -command "$canvas xview" \
		-highlightthickness 0

	NSTexist::Info $canvistId selectionCmd \
		"NSMessage::SelectionChanged $oop"

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

	Info $oop canvistId $canvistId

	pack $win.frameList \
		-expand yes -fill both

	#
	# Geometry
	#

	grid rowconfig $win.frameList 0 -weight 1
	grid rowconfig $win.frameList 1 -weight 0
	grid columnconfig $win.frameList 0 -weight 1
	grid columnconfig $win.frameList 1 -weight 0

	grid $canvas \
		-row 0 -column 0 -rowspan 1 -columnspan 1 -sticky news
	grid $frame.yscroll \
		-row 0 -column 1 -rowspan 1 -columnspan 1 -sticky ns
	grid $frame.xscroll \
		-row 1 -column 0 -rowspan 1 -columnspan 1 -sticky ew

	#
	# Feed Term when keys pressed
	#

	bind $win <KeyPress> {
		angband keypress %A
	}

	# Synch the scrollbars when window is shown.
	bind $canvas <Map> "NSMessage::SynchScrollBars $oop"
}

# NSMessage::InitMenus --
#
#	Create the menus associated with the window.
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc InitMenus oop {

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

	#
	# Menu bar
	#

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

	#
	# Message Menu
	#

	NSObject::New NSMenu $mbar {-tearoff 0 -identifier MENU_MESSAGE}
	NSMenu::MenuInsertEntry $mbar -end MENUBAR {-type cascade \
		-menu MENU_MESSAGE -label "Message" -underline 0 \
		-identifier M_MESSAGE}

	set entries {}
	lappend entries "-type command -label \"Dump Messages\" \
		-command \"NSMessage::Dump $oop $win\" \
		-underline 0 -identifier E_DUMP"
	lappend entries "-type separator"
	lappend entries "-type command -label \"Find...\" \
		-command \"NSMessage::Find $oop 0\" \
		-accelerator f -underline 0 -identifier E_FIND"
	lappend entries "-type command -label \"Find Again\" \
		-command \"NSMessage::Find $oop 1\" \
		-accelerator g -underline 6 -identifier E_FIND_AGAIN"
	lappend entries "-type separator"
	lappend entries "-type checkbutton -label \"Combine Messages\" \
		-command \"NSMessage::CombineMessages $oop\" \
		-variable NSMessage($oop,combine) -onvalue 1 -offvalue 0 \
		-identifier E_COMBINE"
	lappend entries "-type command -label \"Max Messages...\" \
		-command \"NSMessage::MaxMessages $oop\" \
		-underline 0 -identifier E_MAX"
	lappend entries "-type separator"
	lappend entries "-type command -label \"Close\" \
		-command \"NSMessage::Close $oop\" -underline 0 \
		-accelerator $mod+W -identifier E_CLOSE"
	
	NSMenu::MenuInsertEntries $mbar -end MENU_MESSAGE $entries
}

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

proc SetupMenus {oop mbarId} {

	variable Priv

	lappend identList E_DUMP E_COMBINE E_MAX E_FIND E_FIND_AGAIN E_CLOSE
		
	NSMenu::MenuEnable $mbarId $identList
}

# NSMessage::Close --
#
#	Description. 
#
# Arguments:
#	oop					OOP ID. See above.
#
# Results:
#	What happened.

proc Close oop {

	angband keypress \033
}

# NSMessage::SelectionChanged --
#
#	When a message is selected, play the sound associated with that
#	message.
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc SelectionChanged {oop canvistId select deselect} {

	set canvistId [Info $oop canvistId]

	if {![llength $select]} return

	set row [lindex $select 0]
	set index [lindex [Info $oop age] $row]
	angband sound play [angband message sound $index]
}

# NSMessage::SetList --
#
#	Display a list of recent messages in the Message Window. 
#
# Arguments:
#	oop					OOP ID. See above.
#
# Results:
#	What happened.

proc SetList oop {

	set canvistId [Info $oop canvistId]
	set canvas [NSTexist::Info $canvistId text]

	set max [angband message count]
	if {$max > [Value messages,max]} {
		set max [Value messages,max]
	}
	set age {}

	# Option: Combine identical messages (ex "You hit it. (x3)")
	if {[Value messages,combine]} {
	
		set curMsg ""
		set curSound ""
		set count 0
		for {set i [expr $max - 1]} {$i >= 0} {incr i -1} {
			set nextMsg [angband message get $i]
			set nextSound [angband message sound $i]
			if {[string compare $curMsg $nextMsg]} {
				if {$curMsg != ""} {
					if {$count > 1} {
						append curMsg " (x$count)"
					}
					lappend textList $curMsg
					if {[string length $curSound]} {
						lappend colorList Green
					} else {
						lappend colorList White
					}
					lappend age $curAge
				}
				set curMsg $nextMsg
				set curSound $nextSound
				set curAge $i
				set count 1
			} else {
				incr count
			}
		}
		if {$count > 1} {
			append curMsg " (x$count)"
		}
		lappend textList $curMsg
		if {[string length $curSound]} {
			lappend colorList Green
		} else {
			lappend colorList White
		}
		lappend age $curAge

	# Don't combine identical messages
	} else {
		for {set i [expr $max - 1]} {$i >= 0} {incr i -1} {
			lappend textList [angband message get $i]
			set sound [angband message sound $i]
			if {[string length $sound]} {
				lappend colorList Green
			} else {
				lappend colorList White
			}
			lappend age $i
		}
	}
	Info $oop age $age
	set max [llength $textList]

	NSTexist::SetList $canvistId $textList $colorList

	set row [expr $max - 1]
	NSTexist::See $canvistId $row
	NSTexist::UpdateSelection $canvistId $row {}

	focus $canvas
}

# NSMessage::SynchScrollBars --
#
#	There is a bug (my bug or in Tk?) which prevents the scroll bars
#	from synchronizing when the window is not mapped. So I bind to
#	the <Map> event and synch the scroll bars here.
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc SynchScrollBars oop {

	set win [Info $oop win]
	set canvistId [Info $oop canvistId]
	set canvas [NSTexist::Info $canvistId text]

	eval $win.frameList.yscroll set [$canvas yview]
	eval $win.frameList.xscroll set [$canvas xview]
}

# NSMessage::Dump --
#
#	Dump a list of messages to a file. 
#
# Arguments:
#	oop					OOP ID. See above.
#
# Results:
#	What happened.

proc Dump {oop parent} {

	global Angband

	set win [Info $oop win]

	set fileName [tk_getSaveFile -initialfile [angband player base_name].msg \
		-initialdir [file join $Angband(dir) lib user] -parent $parent]
	if {![string compare $fileName ""]} return

	if {[catch {open $fileName w} fileId]} {
		set msg "The following error occurred while attempting to open "
		append msg "the message-dump file for writing:\n\n$fileId"
		tk_messageBox -title Oops -message $msg
		return
	}

	puts $fileId "# Message Dump for [angband player name]\n"

	set max [angband message count]
	for {set i [expr $max - 1]} {$i >= 0} {incr i -1} {
		puts $fileId [angband message get $i]
	}

	close $fileId
}

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

proc Find {oop again} {

	variable Priv

	set canvistId [Info $oop canvistId]
	set text [NSTexist::Info $canvistId text]

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

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

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

	set Priv(find,index) [$text search -backwards -nocase \
		-- $Priv(find,string) $Priv(find,index)]
	if {$Priv(find,index) == ""} return

	scan $Priv(find,index) "%d.%d" line char
	incr line -1
	NSTexist::UpdateSelection $canvistId $line all
	NSTexist::See $canvistId $line
}

# NSMessage::MaxMessages --
#
#	Lets the user enter the maximum number of messages to be displayed
#	in the Message History.
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc MaxMessages oop {

	# Ask the user for a number
	set string [NSUtils::StringBox "Number Of Displayed Messages" \
		[Value messages,max] "Max (1-2048)" "Cancel OK" [Info $oop win]]
	if {$string == ""} return

	if {![scan $string "%d" max]} return
	if {$max < 1 || $max > 2048} return
	Value messages,max $max

	# Redisplay
	SetList $oop
}

# NSMessage::CombineMessages --
#
#	Called when the Combine menu entry is toggled.
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc CombineMessages oop {

	Value messages,combine [Info $oop combine]
	
	# Redisplay
	SetList $oop
}

# namespace eval NSMessage
}
