#!/usr/bin/tclsh
# OpenVerse Server Program
# Modified by Andy Goth <unununium@openverse.org>
# Now it runs a GothChess room!
# 
#
# Module Name		- GothChess event handlers
# Current Maintainter 	- Andy Goth <unununium@openverse.org>
# Sourced By		- gothchess.tcl
#
# 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.

global chess MVS

# on_join who
#
# Do GothChess setup for a new player
proc on_join {who} {
    global MVS chess

    # Disable the start/stop button
    set chess(startstop_active) 0

    # Is there a missing player?
    if {($chess(players.1.is_missing) == 1) || ($chess(players.2.is_missing) == 1)} {
	if {($chess(players.1.is_missing) == 1) && ([string compare $chess(players.1.name) $MVS($who.name)] == 0)} {
	    # Player 1's back!!
	    set name 1
	} elseif {($chess(players.2.is_missing) == 1) && ([string compare $chess(players.2.name) $MVS($who.name)] == 0)} {
	    # Player 2's back!!
	    set name 2
	} else {
	    # There's a missing player, but this guy isn't him--disconnect!
	    log_it "(on_join): Unknown player attempting to join game in progress; disconnecting"
	    send_to_user $who "ROOMFULL"
	    disconnect_user who 1
	    unset who
	    return
	}
	log_it "(on_join): Player $name reconnecting"

	# Yeah, this is the guy.  Let him rejoin.
	set chess(players.$name.sock) $who
	set chess(players.$name.is_missing) 0

	# Give him the game screen
	initialize_screen $who
	show_all_pieces $who
	show_chess_timer $who "all"

	# Inform all users of this guy
	show_button "all" "stop"
	show_names "all"
	show_message "all" "green" "Player $name rejoined"

	# Put the players on top again
	send_to_all_users "ABOVE $chess(players.1.name)"
	send_to_all_users "ABOVE $chess(players.2.name)"

	# If game stalled, resume it
	if {($chess(turn) == $name) && ($chess(initial_time) > 0)} {
	    start_chess_timer
	}
    } else {
	# Which player will the new guy join as?
	if {$chess(players.1.sock) == 0} {
	    log_it "(on_join): Player 1 joining"
	    set name 1
	} elseif {$chess(players.2.sock) == 0} {
	    log_it "(on_join): Player 2 joining"
	    set name 2
	} else {
	    log_it "(on_join): Unknown player joining while there are already two (shouldn't happen)"
	    return
	}

	# Put user information in player list
	set chess(players.$name.sock) $who
	set chess(players.$name.name) $MVS($who.name)
	reset_chess_timers
	set chess(players.$name.is_missing) 0

	# Send the user all the wonderful objects that the game uses
	initialize_screen $who

	# Show all the new chess timers
	show_chess_timer "all" "all"

	# Inform all users of this newcomer
	show_names "all"
	show_message "all" "green" "Player $name joined"

	if {$MVS(users) == 2} {
	    # If there are now two players, enable the Start Game button
	    show_button "all" "start"
	} else {
	    # Either one or zero users; disable Start Game
	    show_button "all" "start_disabled"
	}
    }

    # Move this guy to the right spot.
    if {$name == 1} {
	push_to $who 560 80
    } else {
	push_to $who 560 400
    }

    # Ensure that the correct locations of everybody are known
    # Redundant, yes, but I've had problems with this
    foreach person $MVS(socks) {
	send_to_user $who "MOVE $MVS($person.name) $MVS($person.x) $MVS($person.y) 50"
    }

    unset who name
}

# on_part who
#
# Clean up after a disconnecting player
# Perhaps make it possible for him to rejoin
proc on_part {who} {
    global MVS chess

    # Which player was this guy?
    set name [name_of $who]

    if {$name == 0} {
	log_it "(on_part): Unknown player disconnecting"
	unset who name
	return
    }

    if {$chess(turn) == 0} {
	# There's no game running, so forget him
	log_it "(on_part): Player $name cleanly disconnecting"
	set chess(players.$name.sock) 0
	set chess(players.$name.name) ""
	set chess(players.$name.time) 0
	set chess(players.$name.is_missing) 0

	# Now there aren't two players, so starting a new game is impossible
	set chess(startstop_active) 0
	show_button "all" "start_disabled"
	show_message "all" "red" "Player $name disconnected"
    } else {
	# He disconnected in a game
	# Grant him the chance to reconnect
	set chess(players.$name.sock) 0
	set chess(players.$name.is_missing) 1

	# Activate the Stop button (Draws can be called without consent now)
	set chess(startstop_active) 1
	show_button "all" "stop_active"

	# See if the other player is also missing
	if {$chess(players.[expr 3 - $name].is_missing) == 1} {
	    # Both players are gone
	    # Grant them five minutes to reconnect, and after that call the game a draw
	    after 300 "check_for_players"
	    if {$chess(chess_timer_running) == 1} {
		stop_chess_timer
	    }
	    log_it "(on_part): Player $name disconnected; now both players are missing"
	} else {
	    # One guy's there, but the other's gone, so stall the game and let him reconnect
	    if {$chess(turn) == $name} {
		# He skipped out on his turn, so pause his clock
		log_it "(on_part): Player $name disconnecting during his turn"
		stop_chess_timer
	    } else {
		# Let the other player go and then stop the game
		log_it "(on_part): Player $name disconnecting during other's turn"
	    }
	    show_message "all" "red" "Player $name lost link"

	    # Show his frozen clock
	    show_chess_timer "all" $name
	}
    }
    
    # Inform the remaining users of the departure
    show_names "all"

    unset who name
}

# on_nick who
#
# Allow nickname changes
proc on_nick {who} {
    global chess MVS

    # See who changed their name
    set name [name_of $who]
    if {$name == 0} {
	log_it "(on_nick): Unknown player changing nick to $MVS($who.name) (shouldn't happen)"
	unset who
	return
    }

    # Change the player's nickname in the players database
    log_it "(on_nick): Player $name changing nick from $chess(players.$name.name) to $MVS($who.name)"
    set chess(players.$name.name) $MVS($who.name)

    # Inform everyone of the name change
    show_names "all"

    unset who name
}

# on_startstop who
#
# TELL callback for when user presses Start/Stop
proc on_startstop {who} {
    global chess MVS

    # See who clicked startstop
    set name [name_of $who]
    if {$name == 0} {
	log_it "(on_startstop): Unknown player clicked startstop (shouldn't happen)"
	unset who
	return
    }

    if {$chess(turn) == 0} {
	if {($MVS(users) < 2)} {
	    # Game is not running
	    # Warn player
	    log_it "(on_startstop): Player $name clicked disabled Start Game button"
	    show_message $who "red" "Not enough players"
	} elseif {$chess(startstop_active) == 0} {
	    # Game is not running, and player requested to start
	    # Activate start game button
	    log_it "(on_startstop): Player $name requested a game start"
	    show_button "all" "start_active"
	    show_message "all" "magenta" "Player $name requested start"
	    set chess(startstop_active) $who
	} elseif {[string compare $chess(startstop_active) $who] == 0} {
	    # Game is not running, and player retracted a request to start
	    # Deactivate start game button
	    log_it "(on_startstop): Player $name retracted a game start request"
	    show_button "all" "start"
	    show_message "all" "magenta" "Player $name cancelled start"
	    set chess(startstop_active) 0
	} else {
	    # Game is not running, and player accepted game start request
	    # Start the game
	    log_it "(on_startstop): Player $name accepted game start request"
	    show_button "all" "stop"
	    show_message "all" "yellow" "It's on!"
	    set chess(startstop_active) 0

	    # Let a proc handle it
	    game_start
	}
    } else {
	if {$chess(startstop_active) == 0} {
	    # Game is running, and player requested to stop
	    # Activate stop game button
	    log_it "(on_startstop): Player $name requested a draw"
	    show_button "all" "stop_active"
	    show_message "all" "magenta" "Player $name requested draw"
	    set chess(startstop_active) $who
	} elseif {[string compare $chess(startstop_active) $who] == 0} {
	    # Game is running, and player retracted a request to stop
	    # Deactivate stop game button
	    log_it "(on_startstop): Player $name retracted a draw request"
	    show_button "all" "stop"
	    show_message "all" "magenta" "Player $name cancelled draw"
	    set chess(startstop_active) 0
	} else {
	    # Game is running, and player accepted request to draw
	    # Stop the game
	    log_it "(on_startstop): Player $name accepted draw request"
	    show_button "all" "start"
	    show_message "all" "orange" "The game is a draw"
	    set chess(startstop_active) 0

	    # Let a proc handle it
	    game_end "draw"
	}
    }
}

# on_exit who
#
# TELL callback for when user presses Exit
proc on_exit {who} {
    set name [name_of $who]

    if {$name == 0} {
	log_it "(on_exit): Unknown player pressed Exit Room (shouldn't happen)"
    } else {
	log_it "(on_exit): Player $name pressed Exit Room"
    }
    disconnect_user $who 1

    unset who name
}

# on_rook who
#
# TELL callback for when user presses the rook button
proc on_rook {who} {
    global chess MVS

    set name [name_of $who]

    if {$name == 0} {
	log_it "(on_rook): Unknown player pressed rook button (shouldn't happen)"
    } else {
	if {$chess(promotion) > -1} {
	    # Turn pawn to rook
	    log_it "(on_rook): Player $name promoted his pawn to a rook"
	    show_message "all" "white" "[lindex {A B C D E F G H} $chess(pieces.$chess(turn).$chess(promotion).y)][expr $chess(pieces.$chess(turn).$chess(promotion).x) + 1] promoted to rook"
	    set chess(pieces.$chess(turn).$chess(promotion).type) "rook"
	    if {$chess(turn) == 1} {
		set chess(pieces.$chess(turn).$chess(promotion).img) "white_rook.gif"
	    } else {
		set chess(pieces.$chess(turn).$chess(promotion).img) "black_rook.gif"
	    }

	    end_turn
	} else {
	    log_it "(on_rook): Player $name pressed rook button while pawn promotion unavailable"
	    show_message $who "red" "Pawn promotion unavailable"
	}
    }

    unset who name
}

# on_knight who
#
# TELL callback for when user presses the knight button
proc on_knight {who} {
    global chess MVS

    set name [name_of $who]

    if {$name == 0} {
	log_it "(on_knight): Unknown player pressed knight button (shouldn't happen)"
    } else {
	if {$chess(promotion) > -1} {
	    # TODO: Pawn --> knight
	} else {
	    log_it "(on_rook): Player $name pressed knight button while pawn promotion unavailable"
	    show_message $who "red" "Pawn promotion unavailable"
	}
    }

    unset who name
}

# on_bishop who
#
# TELL callback for when user presses the bishop button
proc on_bishop {who} {
    global chess MVS

    set name [name_of $who]

    if {$name == 0} {
	log_it "(on_bishop): Unknown player pressed bishop button (shouldn't happen)"
    } else {
	if {$chess(promotion) > -1} {
	    # TODO: Pawn --> bishop
	} else {
	    log_it "(on_rook): Player $name pressed bishop button while pawn promotion unavailable"
	    show_message $who "red" "Pawn promotion unavailable"
	}
    }

    unset who name
}

# on_queen who
#
# TELL callback for when user presses the queen button
proc on_queen {who} {
    global chess MVS

    set name [name_of $who]

    if {$name == 0} {
	log_it "(on_queen): Unknown player pressed queen button (shouldn't happen)"
    } else {
	if {$chess(promotion) > -1} {
	    # TODO: Pawn --> queen
	} else {
	    log_it "(on_queen): Player $name pressed queen button while pawn promotion unavailable"
	    show_message $who "red" "Pawn promotion unavailable"
	}
    }

    unset who name
}

# on_chat msg
#
# Hook to allow talking to the server
proc on_chat {who msg} {
    global chess

    if {[llength [split $msg]] < 1} {
	unset who msg
	return
    }
    if {[string compare [lindex $msg 0] "TIME"] == 0} {
	if {$chess(turn) > 0} {
	    # The chess timer can't be set while it's running
	    log_it "(on_chat): Player [name_of $who] attempted to set timer while game was running"
	    unset who msg
	    return
	}

	# Extract the hours, minutes, and seconds
	if {[llength [split $msg]] >= 2} {
	    set hours [lindex $msg 1]
	} else {
	    set hours 0
	}
	if {[llength [split $msg]] >= 3} {
	    set minutes [lindex $msg 2]
	} else {
	    set minutes 0
	}
	if {[llength [split $msg]] >= 4} {
	    set seconds [lindex $msg 3]
	} else {
	    set seconds 0
	}

	if {([is_num $hours] == 0) && ([is_num $minutes] == 0) && ([is_num $seconds] == 0)} {
	    if {($hours >= 0) && ($hours <= 24) && ($minutes >= 0) && ($minutes < 60) && ($seconds >= 0) && ($seconds < 60)} {
		# Valid setting; use it
		set chess(initial_time) [expr $hours * 3600 + $minutes * 60 + $seconds]
		log_it "(on_chat): Player [name_of $who] set timer to $hours : $minutes : $seconds ($chess(initial_time) seconds)"
		reset_chess_timers
		show_chess_timer "all" "all"

		if {$chess(initial_time) > 0} {
		    set msg "Timer set to $hours"
		    append msg ":"
		    if {$minutes < 10} {
			append msg "0"
		    }
		    append msg "$minutes"
		    append msg ":"
		    if {$seconds < 10} {
			append msg "0"
		    }
		    append msg $seconds
		} else {
		    set msg "Chess timer disabled"
		}
		show_message "all" "white" $msg

		unset hours minutes seconds who msg
		return
	    }
	}

	# Bad setting
	log_it "(on_chat): Player [name_of $who] invalidly set timer to $hours : $minutes : $seconds"
	show_message $who "red" "Invalid chess timer setting"

	unset hours minutes seconds
    }

    unset who msg
}

# on_square who x y
#
# Processes $who's click on square $x, $y
proc on_square {who x y} {
    global MVS chess

    set name [name_of $who]

    # See if it's this guy's turn or not
    if {$name == 0} {
	log_it "(on_square): Unknown player clicked the board ($x, $y) (shouldn't happen)"
	unset who x y name
	return
    } elseif {$chess(turn) == 0} {
	log_it "(on_square): Player $name clicked board ($x, $y) while game is not running"
	show_message $who "red" "Start a game first"
	unset who x y name
	return
    } elseif {$name != $chess(turn)} {
	log_it "(on_square): Player $name clicked board ($x, $y) while not his turn"
	show_message $who "red" "It's not your turn"
	unset who x y name
	return
    }

    if {$chess(promotion) > -1} {
	# The player clicked the board while pawn promotion is pending
	log_it "(on_square): Player $name clicked board ($x, $y) while pawn promotion pending"
	show_message $who "red" "Pawn promotion pending"
	unset who x y name
	return
    }

    if {$chess(piece_index) == -1} {
	# The player just now selected a piece

	# Check to see if this square contains a piece
	if {$chess(board.$x.$y.owner) == 0} {
	    log_it "(on_square): Player $name clicked empty square on board ($x, $y)"
	    show_message $who "red" "No piece there"
	    unset who x y name
	    return
	} elseif {$chess(board.$x.$y.owner) != $name} {
	    log_it "(on_square): Player $name clicked piece he does not own ($x, $y)"
	    show_message $who "red" "You don't own that piece"
	    unset who x y name
	    return
	}

	# Determine which piece he selected
	set chess(piece_index) $chess(board.$x.$y.index)
	log_it "(on_square): Player $name selected $chess(pieces.$name.$chess(piece_index).type) at ($x, $y)"
	show_message $who "white" "You selected a $chess(pieces.$name.$chess(piece_index).type)"

	# Show the selection circle
	set loc "$x"
	append loc "_$y"
	if {[expr ($x & 1) ^ ($y & 1)] == 0} {
	    set img "light_select.gif"
	} else {
	    set img "dark_select.gif"
	}
	send_to_user $who "IMAGE square_$loc [expr $x * 60 + 30] [expr $y * 60 + 30] $img [file size "$MVS(images)/$img"] 0"
	unset img loc
    } else {
	# The player selected a piece and now a destination

	# Check to see if he is cancelling his previous selection
	if {($chess(pieces.$name.$chess(piece_index).x) == $x) && ($chess(pieces.$name.$chess(piece_index).y) == $y)} {
	    log_it "(on_square): Player $name cancelled his selection of a $chess(pieces.$name.$chess(piece_index).type) at ($x, $y)"
	    show_message $who "white" "You deselected a $chess(pieces.$name.$chess(piece_index).type)"

	    # Remove the selection circle
	    set loc "$x"
	    append loc "_$y"
	    if {[expr ($x & 1) ^ ($y & 1)] == 0} {
		set img "light_default.gif"
	    } else {
		set img "dark_default.gif"
	    }
	    send_to_user $who "IMAGE square_$loc [expr $x * 60 + 30] [expr $y * 60 + 30] $img [file size "$MVS(images)/$img"] 0"
	    unset img loc

	    # Clear the selection
	    set chess(piece_index) -1

	    unset who x y name
	    return
	}

	# Check to see if he selected another one of his pieces (invalid)
	if {$chess(board.$x.$y.owner) == $name} {
	    log_it "(on_square): Player $name tried to move one of his pieces onto his $chess(pieces.$name.$chess(piece_index).type) at ($x, $y)"
	    show_message $who "red" "You occupy square"

	    unset who x y name
	    return
	}

	# Now that all the basic legality tests are through, do the move
	do_move $name $chess(piece_index) $x $y
    }

    unset who x y name
}

