# OpenVerse Passageways Module
# 
# this file initalizes the program and does any
# platform specific things/setup. It will then source 
# supporting modules.
#
# Module Name		- Passageways Module
# Current Maintainter 	- Cruise <cruise@openverse.org>
# Sourced By		- Init Main Window
#
# Modifications by KaosBeetl:
#    Room History, 01/16/2000, revised 02/07/2000, 02/09/2000, 02/12/2000
#
# Copyright (C) 1999 David Gale <cruise@openverse.org>
# For more information visit http://OpenVerse.org/
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
# 
# This program 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 General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
# USA.

#Performance enhancements using string compare
# Now using curley braces to increase performance


proc DoBookmarks {} {
	global MV

	destroy $MV(passageways_menu).m

	if {!$MV(use_windowmanager_colors)} {
		menu $MV(passageways_menu).m -bg $MV(colors.pw.bg) \
			-fg $MV(colors.pw.fg) -activeforeground $MV(colors.pw.afg) \
			-activebackground $MV(colors.pw.abg)
	} else {
		menu $MV(passageways_menu).m
	}

	$MV(passageways_menu).m add command -label [Trns add_current_room] \
		-command "AddBookmark"
	$MV(passageways_menu).m add command -label [Trns refresh_list] \
		-command "QueryServers 1"
	$MV(passageways_menu).m add command -label [Trns edit_list] \
		-command "PassagewayEditor"

 	set MV(pwq.queries) 0
 	set server 0
 
	# History and Passageways menu code merged; KaosBeetl 02/07/2000
	foreach filename [list $MV(history) $MV(bookmarks)] \
		menudef [list [Trns history] [Trns passageways]] \
		getsdepth [list 0 1] {
	    set depth 0
	    set marks 0
	    if {[file exists "$filename"]} {
		set depth 0
		set infile [open "$filename" r]
		while {[eof $infile] != 1} {
		    gets $infile input
		    set bm [split $input "|"]
		    set serv [split [lindex $bm 1] ":"]
		    if {[string first "|" $input] != -1} {
			if {!$depth} {
			    incr depth
			    if {$getsdepth} {
				set menulabel "$menudef $depth"
			    } else {
				set menulabel "$menudef"
			    }
			    $MV(passageways_menu).m add cascade -menu \
				    $MV(passageways_menu).m.m$menudef$depth -label "$menulabel"
			    if {!$MV(use_windowmanager_colors)} {					
				menu $MV(passageways_menu).m.m$menudef$depth -bg $MV(colors.pw.bg) \
					-fg $MV(colors.pw.fg) -activeforeground $MV(colors.pw.afg) \
					-activebackground $MV(colors.pw.abg)
			    } else {
				menu $MV(passageways_menu).m.m$menudef$depth
			    }
			} 
			set prepend ""
			if {[info exists MV(pwq.$server.users)]} {
				append prepend "($MV(pwq.$server.users)) ($MV(pwq.$server.users)) "
			}
			set name_label "$prepend [lindex $bm 0] [HistoryAge [lindex $bm 2]]"
			set MV(pwq.queries) "$server"
			set MV(pwq.$server.name) "$name_label"
			set MV(pwq.$server.short_name) "[lindex $bm 0] [HistoryAge [lindex $bm 2]]"
			set MV(pwq.$server.host) "[lindex $serv 0]"
			set MV(pwq.$server.port) "[lindex $serv 1]"
			set MV(pwq.$server.menu) "$MV(passageways_menu).m.m$menudef$depth"
			$MV(passageways_menu).m.m$menudef$depth add command -label "$name_label" \
				-command "ConnectToRoom [lindex $serv 0] [lindex $serv 1]"
			if {[info exists MV(pwq.$server.users)]} {
				$MV(pwq.$server.menu) entryconfigure $MV(pwq.$server.name) -label "$name_label" -foreground $MV(pwq.$server.color) -activeforeground $MV(pwq.$server.color)
			}
			if {$getsdepth} {
			    incr marks
			}
			incr server
			if {$marks > 19} {
			    # this will only happen if $getsdepth is set
			    incr depth
			    $MV(passageways_menu).m add cascade -menu \
				    $MV(passageways_menu).m.m$menudef$depth -label "$menudef $depth"
			    if !$MV(use_windowmanager_colors) {
				menu $MV(passageways_menu).m.m$menudef$depth -bg $MV(colors.pw.bg) \
					-fg $MV(colors.pw.fg) -activeforeground $MV(colors.pw.afg) \
					-activebackground $MV(colors.pw.abg)
			    } else {
				menu $MV(passageways_menu).m.m$menudef$depth
			    }
			    set marks 0
			}
		    }
		}
		close $infile
	    }
	}
}

proc AddBookmark {} {
	global MV

	set outfile [open "$MV(bookmarks)" a+]
	puts $outfile "$MV(roomname)|$MV(roomhost):$MV(roomport)|[clock seconds]"
	close $outfile
	DoBookmarks
}

#
# Will query all servers in the passageways list
# and retrieve a number of users on each server.
# It will also note the time it took to respond to the query.
#
# This information will be stored into an array for future use when
# updating the history information when changing rooms.
#
proc QueryServers {BookMark} {
	global MV

	if {[llength $MV(server_queries)] > 0} {return}
	set MV(nosort) 1
	if $BookMark {
		DoBookmarks
	}
	set time 500
	for {set c 0} {$c <= $MV(pwq.queries)} {incr c} {
		lappend MV(server_queries) $c
		after $time "PWQueryServer $c"
		incr time 500
		update idletasks
	}
}

proc PWQueryServer {idx} {
	global MV

	set MV(pwq.$idx.sock) -1
	set sck -1
	catch {set sck [socket -async $MV(pwq.$idx.host) $MV(pwq.$idx.port)]}
	catch {fconfigure $sck -blocking 0}
	set MV(pwq.$idx.sock) $sck
	set MV(pwq.$idx.time) [clock seconds]
	after 30000 "PWKillRequestSocket $idx"
	catch {puts $MV(pwq.$idx.sock) "USERS"}
	catch {flush $MV(pwq.$idx.sock)}
	update idletasks
	catch {fileevent $MV(pwq.$idx.sock) readable "PWGetUsers $idx"}
}

proc PWGetUsers {idx} {
	global MV

	set input "AUTH REQD"
	catch {gets $MV(pwq.$idx.sock) input}
	if {[eof $MV(pwq.$idx.sock)]} {
		set MV(pwq.$idx.time) 0
		set id [lsearch -exact $MV(server_queries) $idx]
		set MV(server_queries) [lreplace $MV(server_queries) $id $id]
		catch {close $MV(pwq.$idx.sock)}
		return
	}
	DebugIt "<-(P) [eof $MV(pwq.$idx.sock)] $MV(pwq.$idx.sock) $input" prot
	set parms [split $input]
	set users [lindex $parms 1]
	set tme [expr [clock seconds] - $MV(pwq.$idx.time)]
	switch -exact -- [lindex $parms 0] {
		"PING" { 
			return
		}
		"AUTH" {
			$MV(pwq.$idx.menu) entryconfigure $MV(pwq.$idx.name) -label "(??) ($tme S) $MV(pwq.$idx.short_name)" -foreground red -activeforeground red
			set MV(pwq.$idx.users) "??"
			set MV(pwq.$idx.time) $tme
			set MV(pwq.$idx.color) "red"
		}
		"USERS" {
			# Performance Enhancement
			if {[string compare $users ""] && $users > 0} {
				$MV(pwq.$idx.menu) entryconfigure $MV(pwq.$idx.name) -label "($users) ($tme S) $MV(pwq.$idx.short_name)" -foreground blue -activeforeground blue
				set MV(pwq.$idx.color) "blue"
			} else {
				$MV(pwq.$idx.menu) entryconfigure $MV(pwq.$idx.name) -label "($users) ($tme S) $MV(pwq.$idx.short_name)" -foreground "dark green" -activeforeground "dark green"
				set MV(pwq.$idx.color) "dark green"
			}
			set MV(pwq.$idx.users) "$users"
			set MV(pwq.$idx.time) $tme
		}
		default {return}
	}
	set MV(pwq.$idx.time) 0
	set id [lsearch -exact $MV(server_queries) $idx]
	set MV(server_queries) [lreplace $MV(server_queries) $id $id]
	catch {close $MV(pwq.$idx.sock)}
	update idletasks
}

proc PWKillRequestSocket {idx} {
	global MV

	if !$MV(pwq.$idx.time) {
		set id [lsearch -exact $MV(server_queries) $idx]
		set MV(server_queries) [lreplace $MV(server_queries) $id $id]
		return
	}
	catch {close $MV(pwq.$idx.sock)}
	$MV(pwq.$idx.menu) entryconfigure $MV(pwq.$idx.name) -label "(--) (TO) $MV(pwq.$idx.short_name)" -foreground red -activeforeground red
	set MV(pwq.$idx.users) "--"
	set MV(pwq.$idx.time) 0
	set MV(pwq.$idx.color) "red"
	set id [lsearch -exact $MV(server_queries) $idx]
	set MV(server_queries) [lreplace $MV(server_queries) $id $id]
}

# Returns a human-readable form of the difference between $time and 
# current time, in seconds, minutes, hours, days, or "long time"
# KaosBeetl 02/07/2000
proc HistoryAge {time} {
    if {![string compare $time ""] || ($time == 0)} {
	return "(unknown)"
    }

    set age [ expr [clock seconds] - $time ]

    if { $age < 60 } {
	if { $age > 0 } {
	    set age_val $age
	} else {
	    set age_val 0
	}
	set age_unit "sec"
    } elseif { $age < 3600 } {
	set age_val [ expr $age / 60 ]
	set age_unit "min"
    } elseif { $age < 86400 } {
	set age_val [ expr $age / 3600 ]
	set age_unit "hour"
    } elseif { $age > 31536000 } {
	# more than 365 days (86400 * 365)
	return "(long time)"
    } else {
	set age_val [ expr $age / 86400 ]
	set age_unit "day"
    } 

    if { $age_val == 0 } {
	return ""
    }

    if { $age_val == 1 } {
	return "($age_val $age_unit)"
    }

    # note the added s
    return "($age_val ${age_unit}s)"
}

# Updates history and passageways file, setting the last use time for the
# current server to the current time.  Sorts history, and optionally sorts
# passageways.
# KaosBeetl 02/07/2000
proc UpdatePassageways {} {
    global MV
    
    foreach filename [list $MV(bookmarks) $MV(history)] \
	    getssort [list $MV(sort_bookmarks) 1] \
	    max [list 9999 $MV(history_max)] {
	set found 0
	set cur_list [list]
	DebugIt "Processing $filename" other
	if [file exists "$filename"] {
	    set infile [open "$filename" r]
	    while {[eof $infile] != 1} {
		gets $infile input
		set bm [split $input "|"]
		
		if {[string first "|" $input] != -1} {
		    set cur_entry [list]
		    set name "[lindex $bm 0]"
		    set thisserv "[split [lindex $bm 1] ":"]"
		    set host "[lindex $thisserv 0]"
		    set port [lindex $thisserv 1]
		    set time [lindex $bm 2]
		    
		    if {![string compare $time ""]} {
			set time 0
		    }
		    
		    # see if this is the current server
		    if {![string compare $MV(roomhost) $host] && \
			    ![string compare $MV(roomport) $port]} {
			if {!$found} {
			    # update name and time
			    set name "$MV(roomname)"
			    set time [clock seconds]
			    set found 1
			} else {
			    set host ""
			}
		    }
		    
		    if {[string compare $host ""]} {
			set cur_entry [list $name $host $port $time]
			lappend cur_list $cur_entry
		    }
		}
	    }
	}
	
	if {![string compare $filename $MV(history)]} {
	    # for history pass, make sure current room is in
	    if {!$found} {
		set cur_entry [list $MV(roomname) $MV(roomhost) $MV(roomport) [clock seconds]]
		lappend cur_list $cur_entry
		set found 1
	    }
	}
	
	if {$getssort && !$MV(nosort)} {
	    # sort by visiting time
	    set cur_list [lsort -integer -decreasing -index 3 $cur_list]
	}
	
	# write them back out, if necessary
	if {$found} {
	    set outfile [open "$filename" w]
	    set ct 1
	    
	    foreach cur_entry $cur_list {
		if {$ct > $max} {
		    break
		} else {
		    incr ct
		}
		puts $outfile "[lindex $cur_entry 0]|[lindex $cur_entry 1]:[lindex $cur_entry 2]|[lindex $cur_entry 3]"
	    }
	    
	    close $outfile
	}
    }
    DoBookmarks
}


#
# PassEdWriteOut
#
# Write the list of passageways.
proc PassEdWriteOut {} {
	global PASSED MV
	set outfile [open "$MV(bookmarks)" w]
	foreach pass $PASSED(bms) {
		puts $outfile "$pass"
	}
	close $outfile
	DoBookmarks
}

#
# PassagewayEdDisplay
#
# This function displays a selected passageway in the editor.
#
global PASSED
proc PassagewayEdDisplay {} {
	global PASSED

	if {[catch {set idx [lindex [split [selection get -displayof .pas_ed.list.list]] 0]}]} {return}
	
	set input [lindex $PASSED(bms) $idx]
	set bm [split $input "|"]
	set hp [split [lindex $bm 1] ":"]
	set PASSED(name) [lindex $bm 0]
	set PASSED(host) [lindex $hp 0]
	set PASSED(port) [lindex $hp 1]
	set PASSED(idx) $idx
	update idletasks
}


#
# PassEdSaveNew
#
# Saves a new passageway
#
proc PassEdSaveNew {} {
	global PASSED

	set input "$PASSED(name_new)|$PASSED(host_new):$PASSED(port_new)|0"
	set idx [llength $PASSED(bms)]
	lappend PASSED(bms) "$input"
	lappend PASSED(list) "$idx $PASSED(name_new)"
	destroy .pas_ed_new
	catch {unset PASSED(name_new)}
	catch {unset PASSED(host_new)}
	catch {unset PASSED(port_new)}
	.pas_ed.list.list selection set end
	update idletasks
	PassEdWriteOut
	PassagewayEdDisplay
}

#
# PassEdSave
#
# Saves the currently displayed passageway.
#
# 
proc PassEdSave {} {
	global PASSED

	if {$PASSED(idx) == -1} {return}
	set input "$PASSED(name)|$PASSED(host):$PASSED(port)|0"
	set idx $PASSED(idx)
	set PASSED(bms) [lreplace $PASSED(bms) $idx $idx "$input"]
	set PASSED(list) [lreplace $PASSED(list) $idx $idx "$idx $PASSED(name)"]
	update idletasks
	PassEdWriteOut
}

#
# PassEdDelete
#
# Deletes the selected item.
#
proc PassEdDelete {} {
	global PASSED
	if {[catch {set idx [lindex [split [selection get -displayof .pas_ed.list.list]] 0]}]} {return}
	set PASSED(list) [lreplace $PASSED(list) $idx $idx]
	set PASSED(bms) [lreplace $PASSED(bms) $idx $idx]
	update idletasks
	PassEdWriteOut
	.pas_ed.list.list selection set 0
	PassagewayEdDisplay
}

#
# PassEdNewRoom
#
# A popup to add a new room.
#
proc PassEdNewRoom {} {
	global PASSED

	if {[winfo exists .pas_ed_new]} {
		destroy .pas_ed_new
	}

	toplevel .pas_ed_new
	wm title .pas_ed_new [Trns new_room]

	frame .pas_ed_new.name
	label .pas_ed_new.name.l -text [Trns name] -width 10
	entry .pas_ed_new.name.e -textvariable PASSED(name_new)
	pack .pas_ed_new.name -fill both
	pack .pas_ed_new.name.l -side left
	pack .pas_ed_new.name.e -side left -fill x -expand y

	frame .pas_ed_new.host
	label .pas_ed_new.host.l -text [Trns host] -width 10
	entry .pas_ed_new.host.e -textvariable PASSED(host_new)
	label .pas_ed_new.host.pl -text [Trns port] -width 10
	entry .pas_ed_new.host.pe -textvariable PASSED(port_new)
	pack .pas_ed_new.host -fill both
	pack .pas_ed_new.host.l -side left
	pack .pas_ed_new.host.e -side left -fill x -expand y
	pack .pas_ed_new.host.pl -side left
	pack .pas_ed_new.host.pe -side left -fill x -expand y

	frame .pas_ed_new.buttons -relief sunken -borderwidth 2
	button .pas_ed_new.buttons.quit -text [Trns close] -command "destroy .pas_ed_new"
	button .pas_ed_new.buttons.save -text [Trns save] -command PassEdSaveNew
	pack .pas_ed_new.buttons -fill both
	pack .pas_ed_new.buttons.quit .pas_ed_new.buttons.save -side left -fill x -expand y

}

#
# This is the passageway editor. Add, delete, etc.
#
proc PassagewayEditor {} {
	global MV PASSED

	if {[winfo exists .pas_ed]} {
		destroy .pas_ed
	}

	toplevel .pas_ed
	wm title .pas_ed [Trns passageway_editor]

	frame .pas_ed.name
	label .pas_ed.name.l -text [Trns name] -width 10
	entry .pas_ed.name.e -textvariable PASSED(name)
	pack .pas_ed.name -fill both
	pack .pas_ed.name.l -side left
	pack .pas_ed.name.e -side left -fill x -expand y

	frame .pas_ed.host
	label .pas_ed.host.l -text [Trns host] -width 10
	entry .pas_ed.host.e -textvariable PASSED(host)
	label .pas_ed.host.pl -text [Trns port] -width 10
	entry .pas_ed.host.pe -textvariable PASSED(port)
	pack .pas_ed.host -fill both
	pack .pas_ed.host.l -side left
	pack .pas_ed.host.e -side left -fill x -expand y
	pack .pas_ed.host.pl -side left
	pack .pas_ed.host.pe -side left -fill x -expand y

	frame .pas_ed.buttons -relief sunken -borderwidth 2
	button .pas_ed.buttons.quit -text [Trns close] -command "destroy .pas_ed"
	button .pas_ed.buttons.save -text [Trns save] -command "PassEdSave"
	button .pas_ed.buttons.del -text [Trns delete_selected] -command "PassEdDelete"
	button .pas_ed.buttons.new -text [Trns new_room] -command PassEdNewRoom
	pack .pas_ed.buttons -fill both
	pack .pas_ed.buttons.quit .pas_ed.buttons.save .pas_ed.buttons.del .pas_ed.buttons.new -side left \
		-fill x -expand y


	frame .pas_ed.list -relief sunken -borderwidth 2
	listbox .pas_ed.list.list -width 80 -height 20 -xscrollcommand ".pas_ed.list.scrollx set" \
		-yscrollcommand ".pas_ed.list.scrolly set" -listvar PASSED(list)
	scrollbar .pas_ed.list.scrolly -command ".pas_ed.list.list yview"
	scrollbar .pas_ed.list.scrollx -orient horizontal -command ".pas_ed.list.list xview"
	pack .pas_ed.list -fill both -expand y
	pack .pas_ed.list.scrollx -side top -fill x
	pack .pas_ed.list.list -side left -fill both -expand y
	pack .pas_ed.list.scrolly -side left -fill y
	update idletasks

	set PASSED(bms) {}
	set PASSED(list) {}
	set PASSED(idx) -1
	set idx -1
	if {[file exists "$MV(bookmarks)"]} {
		set infile [open "$MV(bookmarks)" r]
		while {[eof $infile] != 1} {
			gets $infile input
			if {[string first "|" $input] != -1} {
				incr idx
				set bm [split $input "|"]
		    		set hp [split [lindex $bm 1] ":"]
				set name [lindex $bm 0]
				set host [lindex $hp 0]
				set port [lindex $hp 1]
				lappend PASSED(list) "$idx $name"
				lappend PASSED(bms) $input
			}
		}
		close $infile
	}
	bind .pas_ed.list.list <ButtonRelease> "PassagewayEdDisplay"
}
