# store.tcl --
#
#	The store window(s) and related commands.
#

namespace eval NSStore {

variable Priv

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

proc InitModule {} {

	global Windows

	NSObject::New NSStore
}

# NSStore::NSStore --
#
#	Description.
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc NSStore oop {

	global NSStore
	global Windows

	# The user should be able to change the font.
	set NSStore($oop,font) [Global font,fixed,normal]

	InitWindow $oop

	NSWindowManager::RegisterWindow store [Info $oop win] \
		"NSStore::GeometryCmd $oop" "" "NSStore::DisplayCmd $oop"

	#
	# Global list of application windows
	#

	set Windows(store,win) [Info $oop win]
	set Windows(store,class) NSStore
	set Windows(store,oop) $oop
}

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

proc Info {oop info args} {

	global NSStore

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

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

# NSStore::InitWindow --
#
#	Description.
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc InitWindow oop {

	global NSStore
	global NSToolbar
	global Windows

	set win .store$oop
	toplevel $win
	wm title $win Store

	wm transient $win $Windows(main,win)

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

	# This window plays sound
	SoundWindow $win

	# Start out withdrawn (hidden)
	wm withdraw $win

	set NSStore($oop,win) $win

	InitMenus $oop

	#
	# Toolbar
	#

	set toolId [NSObject::New NSToolbar 20 $win]
	NSToolbar::AddTool $toolId Image_ButtonOptions "DoCommandIfAllowed ="
	NSToolbar::AddTool $toolId Image_ButtonHelp "DoCommandIfAllowed ?"

	#
	# Divider + Store Info
	#

	set frame $win.info
	frame $frame \
		-borderwidth 0
	frame $frame.divider1 \
		-borderwidth 1 -height 2 -relief groove
	label $frame.howMany \
		-font {Courier 9} -text "Quantity:"
	entry $frame.quantity \
		-width 2 -state disabled -textvariable NSStore($oop,quantity)
	label $frame.howMuch \
		-font {Courier 9} -text "Total Cost:"
	label $frame.totalCost \
		-font {Courier 9} -text ""
	label $frame.playerGold \
		-font {Courier 9} -text "Gold Remaining:"
	label $frame.gold \
		-font {Courier 9} -text [angband player gold]

	# Update the display when the character's gold changes
	qebind $frame.gold <Py-gold> {Debug frame.gold ; %W configure -text %c}
	qeconfigure $frame.gold <Py-gold> -active no

	label $frame.price_character \
		-font {Courier 9} -text "" -width 25 -anchor w
	label $frame.price_owner \
		-font {Courier 9} -text "" -width 25 -anchor w

	trace variable NSStore($oop,quantity) w "::NSStore::QuantityChanged $oop"

	# This stops keys being fed to the Term
#	bindtags $frame.quantity "$frame.quantity Entry all"

	frame $win.divider2 \
		-borderwidth 1 -height 2 -relief groove

	#
	# List
	#

	set cw [font measure $NSStore($oop,font) "W"]
	set width [expr $cw * 81]
	set iconSize [expr [icon size] + 8]

	frame $win.frame \
		-borderwidth 1 -relief sunken
	set canvistId [NSObject::New NSCanvist $win.frame $iconSize $width 300 \
		"NSStore::NewItemCmd $oop" "NSStore::HighlightItemCmd $oop"]
	set canvas [NSCanvist::Info $canvistId canvas]
	$canvas configure -background [NSColorPreferences::Get listBG]
	$canvas configure -yscrollcommand "$win.frame.scroll set"
	scrollbar $win.frame.scroll \
		-borderwidth 0 -command "$canvas yview" -orient vert

	set NSStore($oop,list) $canvistId

	pack $win.frame.scroll -side right -fill y
	pack $canvas -side left -expand yes -fill both

	# When an item is selected, recall it
	NSCanvist::Info $canvistId selectionCmd \
		"NSStore::SelectionChanged $oop"

	# Double-click to purchase an item
	NSCanvist::Info $canvistId invokeCmd \
		"NSStore::Invoke $oop"

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

	# Typing Enter in the Quantity Entry initiates a purchase
	bind $frame.quantity <KeyPress-Return> "
		focus $canvas
		NSStore::Invoke $oop $canvistId -1 -1
	"

	#
	# Statusbar
	#

	frame $win.statusBar -relief flat -borderwidth 0
	label $win.statusBar.label -anchor w -relief sunken -padx 2
	label $win.statusBar.label2 -anchor w -relief sunken -padx 2 -width 12
	pack $win.statusBar.label -side left -expand yes -fill both
	pack $win.statusBar.label2 -side right -expand no

	#
	# Geometry
	#

	grid rowconfig $win 0 -weight 0 -minsize 0
	grid rowconfig $win 1 -weight 0 -minsize 0
	grid rowconfig $win 2 -weight 0 -minsize 0
	grid rowconfig $win 3 -weight 1 -minsize 0
	grid rowconfig $win 4 -weight 0 -minsize 0
	grid columnconfig $win 0 -weight 1 -minsize 0

	pack forget $NSToolbar($toolId,frame)
	grid $NSToolbar($toolId,frame) -in $win \
		-row 0 -column 0 -rowspan 1 -columnspan 1 -sticky ew
	grid $win.info -in $win \
		-row 1 -column 0 -rowspan 1 -columnspan 1 -sticky ew
	grid $win.divider2 -in $win \
		-row 2 -column 0 -rowspan 1 -columnspan 2 -sticky ew
	grid $win.frame -in $win \
		-row 3 -column 0 -rowspan 1 -columnspan 1 -sticky news
	grid $win.statusBar -in $win \
		-row 4 -column 0 -rowspan 1 -columnspan 2 -sticky ew

	#
	# Feed Term when keys pressed
	#

	Term_KeyPress_Bind $win
	Term_KeyPress_Bind $canvas

	# XXX Hack -- Don't feed Tab to the Term
	bind $canvas <KeyPress-Tab> {
		focus [tk_focusNext %W]
		break
	}
	
	#
	# Synch the scrollbars when window is shown.
	#

	bind $win.frame.scroll <Map> "NSStore::SynchScrollBars $oop"

	bind $win <FocusIn> "if !\[string compare %W $win] {focus $canvas}"
}

# NSStore::InitMenus --
#
#	Description.
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc InitMenus oop {

	global NSStore

	set win $NSStore($oop,win)

	#
	# Menu bar
	#

	set menuDef "-tearoff 0 -postcommand \"NSStore::SetupMenus $oop\" -identifier MENUBAR"
	set mbar [NSObject::New NSMenu $win $menuDef]
	set NSStore($oop,mbar) $mbar

	#
	# Store Menu
	#

	NSObject::New NSMenu $mbar {-tearoff 0 -identifier MENU_STORE}
	NSMenu::MenuInsertEntry $mbar -end MENUBAR {-type cascade -menu MENU_STORE -label "Store" -underline 0 -identifier M_STORE}

	set entries {}
	lappend entries "-type command -label \"Buy\" -command \"DoCommandIfAllowed p\" -identifier E_STORE_BUY"
	lappend entries "-type command -label \"Sell\" -command \"DoCommandIfAllowed s\" -identifier E_STORE_SELL"
	lappend entries "-type separator"
	lappend entries "-type command -label \"Leave\" -command \"DoCommandIfAllowed \033\" -identifier E_STORE_EXIT"
	
	NSMenu::MenuInsertEntries $mbar -end MENU_STORE $entries

}

# NSStore::SetupMenus --
#
#	Description
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc SetupMenus {oop mbarID} {

	lappend identList E_STORE_BUY E_STORE_SELL E_STORE_EXIT

	NSMenu::MenuEnable $mbarID $identList
}

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

proc DisplayCmd {oop message first} {

	set win [Info $oop win]
	
	switch -- $message {
		preDisplay {
			SetList $oop
			qeconfigure $win.info.gold <Py-gold> -active yes
		}
		postDisplay {
		}
		reDisplay {
			SetList $oop
		}
		postWithdraw {
			qeconfigure $win.info.gold <Py-gold> -active no
		}
	}
}

# NSStore::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
}

# NSStore::Close --
#
#	Description.
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc Close oop {

	angband keypress \033
}

# NSStore::ConfigureWindow --
#
#	Description.
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc ConfigureWindow oop {

	global NSStore

	set win $NSStore($oop,win)
	set frame $win.info

	eval pack forget [winfo children $frame]

	pack $frame.divider1 \
		-side top -expand yes -fill x
	pack $frame.howMany \
		-side left -expand no -padx 2
	pack $frame.quantity \
		-side left -expand no -padx 2

	# Not in the Home
	if {[string first Home [angband store storename]] == -1} {

		pack $frame.howMuch \
			-side left -expand no -padx 2
		pack $frame.totalCost \
			-side left -expand no -padx 2
	}

	pack $frame.gold \
		-side right -expand no -padx 2
	pack $frame.playerGold \
		-side right -expand no -padx 2
}

# NSStore::HaggleSetup --
#
#	Shows or hides display elements during the buy/sell process.
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc HaggleSetup {oop action} {

	global NSStore

	set win $NSStore($oop,win)
	set frame $win.info

	switch -- $action {

		haggle_open {
			pack forget $frame.howMany $frame.quantity $frame.howMuch \
				$frame.totalCost
			pack $frame.price_owner \
				-side left -padx 2
			pack $frame.price_character \
				-side left -padx 2
		}

		haggle_close {
			pack forget $frame.price_owner $frame.price_character
			pack $frame.howMany \
				-side left -padx 2
			pack $frame.quantity \
				-side left -padx 2
			pack $frame.howMuch \
				-side left -padx 2
			pack $frame.totalCost \
				-side left -padx 2
		}
	}
}

# NSStore::CalcLineLength --
#
#	Return the number of characters that will fit on a line. This considers
#	the object image (20 pixels).
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc CalcLineLength oop {

	global NSStore

	set canvistId $NSStore($oop,list)
	set canvas [NSCanvist::Info $canvistId canvas]

	set cw [font measure $NSStore($oop,font) "W"]
	set width [winfo width $canvas]
	if {[Value show_icons]} {
		set leftBorder [expr [icon size] + 8]
	} else {
		set leftBorder 4
	}
	return [expr int([expr ($width - $leftBorder - 4) / $cw])]
}

# NSStore::CalcDescMax --
#
#	Return the max number of characters allowed in an item description.
#	This considers the option "show_weights", and the price (not present 
#	in Home).
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc CalcDescMax {oop lineLength} {

	global NSStore

	set length $lineLength
	incr length -3

	# Weight ex. "  123.4"
	if {[lindex [angband setting show_weights] 0]} {
		incr length -7
	}

	# Price (except in Home) ex. "  123456 F"
	if {[string first Home [angband store storename]] == -1} {
		incr length -10
	}

	return $length
}

# NSStore::SetList --
#
#	Description.
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc SetList oop {

	global NSStore
	variable Priv

	# Get the window and canvas
	set win $NSStore($oop,win)
	set canvistId $NSStore($oop,list)
	set canvas [NSCanvist::Info $canvistId canvas]

	# Set the window up for this store
	if {![winfo ismapped $win]} {
		ConfigureWindow $oop
	}

	# Clear and disable the "quantity" entry
	set NSStore($oop,quantity) ""
	$win.info.quantity configure -state disabled

	# Get the store name
	set storename [angband store storename]

	# In the Home
	if {[string first Home $storename] != -1} {

		# Set the window title
		wm title $win "Your Home"

	# Not in the Home
	} else {

		# Set the window title
		wm title $win "The $storename owned by [angband store ownername]"
	}

	# Get the inventory list
	set items [angband store inventory]

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

	# Option: Show icons in lists
	if {[Value show_icons]} {
		set rowHgt [expr [icon size] + 8]
	} else {
		set rowHgt 20
	}	
	NSCanvist::Info $canvistId rowHgt $rowHgt
	$canvas configure -yscrollincrement $rowHgt

	# Option: Show weights
	set show_weights [lindex [angband setting show_weights] 0]

	# Get maximum length of item description
	set Priv(lineLen) [CalcLineLength $oop]
	set Priv(descLim) [CalcDescMax $oop $Priv(lineLen)]

	# Append each object
	foreach itemDesc $items {

		set desc [lindex $itemDesc 2]
		set desc [string range $desc 0 [expr $Priv(descLim) - 1]]

		set weight [lindex $itemDesc 3]
		if !$show_weights {set weight ""}

		set icon [lindex $itemDesc 6]
		if {![Value show_icons]} {set icon ""}

		# Hack -- Align on trailing " F" (for fixed price)
		set price [lindex $itemDesc 7]
		if {[llength $price] == 1} {set price "$price  "}

		NSCanvist::Insert $canvistId end [lindex $itemDesc 0] \
			[lindex $itemDesc 1] $desc $weight [lindex $itemDesc 4] \
			[lindex $itemDesc 5] $icon $price 
	}

	# Display number of objects
	$win.statusBar.label2 configure -text "[llength $items] item(s)"
}

# NSStore::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 {

	global NSStore

	set win $NSStore($oop,win)
	set canvistId $NSStore($oop,list)
	set canvas [NSCanvist::Info $canvistId canvas]

	eval $win.frame.scroll set [$canvas yview]
	$canvas yview moveto 0.0
}

# NSStore::Invoke --
#
#	When a store item is double-clicked, "angband keypress" to
#	initiate a purchase. This is also called when Return is typed
#	while the Quantity Entry has the focus.
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc Invoke {oop canvistId x y} {

	global NSStore

	set doCmd [set doItem 0]
	if {![string compare [angband inkey_flags] INKEY_CMD]} {
		set doCmd 1
	}
	if {![string compare [angband inkey_flags] INKEY_ITEM_STORE]} {
		set doItem 1
	}
	if {!$doCmd && !$doItem} return

if 1 {
	set selection [NSCanvist::Selection $canvistId]
	if {![llength $selection]} return
	set row [lindex $selection 0]
} else {

	set canvas [NSCanvist::Info $canvistId canvas]

	if {[$canvas find withtag current] == {}} return
	set row [NSCanvist::ItemRow $canvistId current]
}

	# Get the char
	set itemDesc [lindex [angband store inventory] $row]

	# Enter a quantity if waiting for a command
	if $doCmd {
		set keypress "0$NSStore($oop,quantity)p"
	}
	append keypress [lindex $itemDesc 0]

	# Feed the Term
	angband keypress $keypress
}

# NSStore::SelectionChanged --
#
#	Called when the list selection changes.
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc SelectionChanged {oop canvistId select deselect} {

	global NSStore
	global Windows

	set win $NSStore($oop,win)
	set entry $win.info.quantity

	# An item was selected
	if {[llength $select]} {

		$entry configure -state normal
		set NSStore($oop,quantity) "1"

		# Get the (first) row
		set row [lindex $select 0]

		# Get object info
		set attrib [lindex [angband store inventory] $row]

		# Get the icon
		set icon [lindex $attrib 6]

		# Color
		set color [default_tval_to_attr [lindex $attrib 4]]

		# Describe the item
		set desc [lindex $attrib 2]

		# Recall the item
		NSRecall::SetText $Windows(recall,oop) $icon $color $desc \
			[angband store memory $row]

	# An item was deselected (but not selected)
	} else {

		set NSStore($oop,quantity) ""
		$entry configure -state disabled
	}
}

# NSStore::Purchase --
#
#	***DONT CALL THIS***
#	Attempt to purchase the selected item(s) from the store.
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc Purchase oop {

	global NSStore

	set win $NSStore($oop,win)
	set canvistId $NSStore($oop,list)
	set quantity $NSStore($oop,quantity)

	set selection [NSCanvist::Selection $canvistId]
	set row [lindex $selection 0]

	angband store purchase $row $quantity
}

# NSStore::QuantityChanged --
#
#	Trace variable callback on NSStore($oop,quantity). When the number
#	of items to buy changes, synch the "total cost" field.
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc QuantityChanged {oop name1 name2 op} {

	global NSStore

	set win $NSStore($oop,win)
	set canvistId $NSStore($oop,list)
	set quantity $NSStore($oop,quantity)

	set selection [NSCanvist::Selection $canvistId]

	if {$selection == {} || $quantity == {}} {
		$win.info.totalCost configure -text {}
		return
	}

	set row [lindex $selection 0]

	# Add "index" arg to get only one item!
	set item [lindex [angband store inventory] $row]

	if {$quantity <= 0} {
		set quantity 1
	}
	set max [lindex $item 1]
	if {$quantity > $max} {
		set quantity $max
	}

	# Maybe "store price <index>"
	# Hack -- Handle "fixed" costs (ex "45 F")
	set price [lindex [lindex $item 7] 0]
	
	# No prices are displayed in the Home
	if {[string first Home [angband store storename]] == -1} {

		$win.info.totalCost configure -text [expr $quantity * $price]
	}

	set NSStore($oop,quantity) $quantity
}

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

proc NewItemCmd {oop canvistId y char number text weight tval sval icon price} {

	global NSStore
	variable Priv

	set c [NSCanvist::Info $canvistId canvas]
	set lineHeight [NSCanvist::Info $canvistId rowHgt]
	set font $NSStore($oop,font)

	set prefix "$char) "

	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])]

	if {[Value show_icons]} {
		set offset [expr [icon size] + 8]
	} else {
		set offset 4
	}

	set textLeft [expr $offset + $cw * [string length $prefix]]

	# Selection rectangle around text
	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
	if {$icon != ""} {
		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]]
	}

	# Prefix (char & label)
	lappend itemIdList [$c create text $offset [expr $y + $diff] \
		-text $prefix -anchor nw -font $font -fill White]

	# Description
	set fill [default_tval_to_attr $tval]
	lappend itemIdList [$c create text [expr $textLeft + 1] [expr $y + $diff] \
		-text $text -anchor nw -font $font -fill $fill -tags "enabled fill:$fill hilite"]

	set lineLen $Priv(lineLen)
	if {![string length $price]} {
		incr lineLen 10
	}

	# Weight ("show_weights" option)
	if {[string length $weight]} {
		set lineLength $Priv(lineLen)
		if {[string length $price]} {incr lineLength -10}
		set weightRight [expr $offset + $cw * $lineLength]
		lappend itemIdList [$c create text $weightRight \
			[expr $y + $diff] -text $weight -anchor ne -justify right \
			-font $font -fill White]
	}

	# Price (except in Home)
	if {[string length $price]} {
		set lineLength $Priv(lineLen)
		set priceIsRight [expr $offset + $cw * $lineLength]
		lappend itemIdList [$c create text $priceIsRight \
			[expr $y + $diff] -text $price -anchor ne -justify right \
			-font $font -fill White]
	}

	return $itemIdList
}

# NSStore::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 {}
	}
}

# namespace eval NSStore
}

# angband_store --
#
#	A bit of ugliness to handle the haggle prompts in a store.
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc angband_store {action {arg1 ""}} {

	global Windows

	set storeId $Windows(store,oop)
	set win $Windows(store,win)

	switch -- $action {

		haggle_open -
		haggle_close {
			NSStore::HaggleSetup $storeId $action
		}

		price_character -
		price_owner {
			$win.info.$action configure -text $arg1
		}
	}
}
