<?php

// myChess
// ```````

// ##########################################################
// Initialisation and constants
// ##########################################################


$page_create_time = microtime();
session_start();

require_once 'common.php';
require 'db.php';
require 'chess.php';

define('MODE_HOME',     0);
define('MODE_GAME',     1);
define('MODE_CREATE',   2);
define('MODE_MOVE',     3);
define('MODE_CHAT',     4);
define('MODE_OPTIONS',  5);
define('MODE_SETOPTS',  6);
define('MODE_AJAXHIST', 7);
define('MODE_DEFAULT', MODE_HOME);
$current_mode = MODE_DEFAULT;

$game = array(
	'id'        => 0,					// Game index
	'whoami'    => PLAYER_S,			// Current PLAYER_*
	'auth'      => '',					// Hash for current player
	'auth_spec' => '',					// Hash for spectator
	'player1'   => array(),				// Details for player 1
	'player2'   => array(),				// Details for player 2
	'board'     => DEFAULT_BOARD,		// Current board
	'turn'      => '',					// PLAYER_* to move
	'flipped'   => false,				// Board is inverted by $_GET param
	'theme'		=> DEFAULT_THEME		// Current piece theme
);


// ##########################################################
// Controller
// ##########################################################


$board = array();
if (! isset($_GET['p'])) redirect(HOME_ADDRESS);
switch( $_GET['p'] ) {

	// MODE_GAME
	case 'game': {
		$current_mode = MODE_GAME;
		
		// Validate request and load game
		if (! isset($_GET['id'])) dieWithError();
		$game['auth'] = $_GET['id'];
		validateGame();
		if (isset($_GET['flip']) && $_GET['flip']=='1') $game['flipped'] = true;
		
	} break;
	
	// MODE_HOME
	case 'home': {
		$current_mode = MODE_HOME;
	} break;
	
	// MODE_OPTIONS
	case 'options': {
		$current_mode = MODE_OPTIONS;
	
		// Validate request and load game
		if (! isset($_GET['id'])) dieWithError();
		$game['auth'] = $_GET['id'];
		validateGame();
		
		// Require black/white
		if ($game['whoami'] == PLAYER_S) dieWithError();
	} break;
	
	// MODE_AJAXHIST
	case 'history': {
		$current_mode = MODE_AJAXHIST;
		
		// Validate request and load game (with plain errors)
		if (! isset($_GET['id'])) die(E_INVREQ);
		$game['auth'] = $_GET['id'];
		list($game['id'], $game['whoami']) = DB\SearchHash($game['auth']);
		if (! $game['id']) die(E_INVREQ);
		DB\GetGame();
		
		// Return entire history
		viewHistory(1024);
		die();
		
	} break;
	
	// MODE_SETOPTS
	case 'setoptions': {
		$current_mode = MODE_SETOPTS;
				
		// Validate request and load game
		if (! checkPost(array('auth', 'email', 'timezone', 'theme'))) dieWithError();
		$game['auth'] = $_POST['auth'];
		validateGame();
		
		// Require black/white
		if ($game['whoami'] == PLAYER_S) {
			$_SESSION['info'] = E_INVREQ;
			redirect('?p=options&id='.$game['auth']);
		}
		
		// Ensure valid timezone
		
		// Store changes
		DB\SetPlayerPreferences(
			(($game['whoami'] == PLAYER_W)?$game['player1']['id'] : $game['player2']['id']),
			$_POST['email'],
			isset($_POST['doEmail']),
			$_POST['timezone'], $_POST['theme']
		);
		
		$_SESSION['info'] = S_DETAILSOK;
		redirect('?p=options&id='.$game['auth']);
	} break;	
	
	// MODE_CREATE
	case 'create': {
		$current_mode = MODE_CREATE;
		
		// Validate request
		if (! checkPost(array('name1', 'name2'))) dieWithError();
		if (! strlen($_POST['name1']) || ! strlen($_POST['name2'])) dieWithError(E_NONAME);
		
		// Create the game
		$hashes = DB\NewGame($_POST['name1'], $_POST['name2']);
		$response_str = explode('|', S_GAMECREATED);		
		$_SESSION['info'] =
			$response_str[0] .
			'<a href="?p=game&id='.$hashes[PLAYER_B].'">'.$response_str[1].'</a>'.
			$response_str[2]
		;		
		redirect('?p=game&id='.$hashes[PLAYER_W]);
	} break;
	
	// MODE_MOVE
	case 'move': {
		$current_mode = MODE_MOVE;
	
		// Validate request and load game
		if (! checkPost(array('auth', 'move'))) dieWithError();
		$game['auth'] = $_POST['auth'];
		validateGame();
		
		// Check play order
		if ($game['whoami'] != $game['turn']) {
			$_SESSION['info'] = E_NOTURN;
			redirect('?p=game&id='.$game['auth']);
		}
		
		// Test and apply move
		$move = explode(' ', $_POST['move']);
		
		if ( (count($move) == 2) &&
			chess\canMoveStr($game['board'], $move[0], $move[1], $game['turn'])
		) {
			$move_text = chess\formatMoveText($game['board'], $move[0], $move[1]);
			DB\Move(
				$game['id'],
				(($game['turn'] == PLAYER_W) ? PLAYER_B : PLAYER_W),
				$move_text,
				chess\applyMoveStr($game['board'], $move[0], $move[1])
			);
			$_SESSION['info'] = S_PLAYED.' '.$move_text;
			emailOpponent(S_PLAYED.' '.$move_text);
		} else {
			$_SESSION['info'] = E_INVMOVE;
		}
		
		// Send email notifications
		
		redirect('?p=game&id='.$game['auth']);
	} break;
	
	// MODE_CHAT
	case 'chat': {
		$current_mode = MODE_CHAT;
	
		// Validate request and load game
		if (!checkPost(array('auth', 'message')) || !strlen($_POST['message'])) {
			dieWithError(E_INVREQ);
		}
		$game['auth'] = $_POST['auth'];
		validateGame();
		
		if ($game['whoami'] == PLAYER_S) {
			// Block spectator chat
			$_SESSION['info'] = E_INVREQ;
		} else {		
			// Add to DB and notify opponent
			DB\Chat($game['id'], $game['whoami'], $_POST['message']);
			emailOpponent($_POST['message']);		
			$_SESSION['info'] = S_CHATOK;
		}
		
		// Return
		redirect('?p=game&id='.$game['auth']);
	} break;
	
	default:
		redirect(HOME_ADDRESS);
}


// ##########################################################
// Controller helper functions
// ##########################################################


function emailOpponent($content) {
	global $game;
	
	// Retrieve and validate opponent's parameters
	if ($game['whoami'] == PLAYER_W) {
		if (! $game['player2']['doEmail']) return;
		$email = $game['player2']['email'];
		$opponent = PLAYER_B;
	} else {
		if (! $game['player1']['doEmail']) return;
		$email = $game['player1']['email'];
		$opponent = PLAYER_W;
	}
	if (! filter_var($email, FILTER_VALIDATE_EMAIL)) return;
	
	// Search for opponent's hash
	$hash = DB\RetrieveHash($game['id'], $opponent);
	
	// Determine message content
	$content .= "\n\n
___________________________
View game: ".main_url()."?p=game&id={$hash}
Powered by ".APP_NAME."
	";
	
	// Send message
	mail(
		$email,
		APP_NAME.' update - '.htmlspecialchars($game['player1']['name'].' v '.$game['player2']['name']),
		$content,
		'From: '.APP_NAME.' <'.APP_EMAIL_FROM.'>'."\n".
		'Content-Type: text/plain'
	);
}

function validateGame() {
	global $game;
	list($game['id'], $game['whoami']) = DB\SearchHash($game['auth']);
	if (! $game['id']) dieWithError();
	DB\GetGame();
	
	switch ($game['whoami']) {
		case PLAYER_W: {
			$game['theme'] = $game['player1']['theme'];
		} break;
		case PLAYER_B: {
			$game['theme'] = $game['player2']['theme'];
		} break;
		default: {
			$game['theme'] = DEFAULT_THEME;
		} break;
	}
}

function checkPost($args) {
	foreach ($args as $arg) if (! isset($_POST[$arg])) return false;
	return true;
}


// ##########################################################
// HTML Helper views
// ##########################################################


function timezone_select_interior($selected = DEFAULT_TZONE) {
	$timezones = timezone_identifiers_list();
	$result = array();
	foreach ($timezones as $timezone) {
		$entry = explode('/', $timezone, 2);
		if (count($entry) == 1) {
			$result['Other'][] = $entry[0];
		} else {
			$result[$entry[0]][] = $entry[1];
		}
	}
	foreach ($result as $continent => $regions) {
		echo '<optgroup label="'.$continent.'">'."\n";
		foreach ($regions as $region) {
			echo '<option ';
			if ($continent.'/'.$region == $selected) echo 'selected ';
			echo 'value="'.$continent.'/'.$region.'">'.str_replace('_', ' ', $region).'</option>';
		}
		echo '</optgroup>'."\n";
	}
}

function formatPiece($type, $theme) {
	static $utf_types = array(
		'K' => 12, 'Q' => 13, 'R' => 14, 'B' => 15, 'N' => 16, 'P' => 17,
		'k' => 18, 'q' => 19, 'r' => 20, 'b' => 21, 'n' => 22, 'p' => 23
	);
	
	switch($theme) {
		
		// 0: Unicode theme
		case 0: {
			return (isset($utf_types[$type])) ? '&#98'.$utf_types[$type].';' : '&nbsp;';
		} break;
		
		// 1: Plain text theme
		case 1: {
			return strlen($type) ? $type : '&nbsp;';
		} break;
		
		// 2: Graphical theme A
		case 2: {
			return strlen($type) ? '<div class="thmA piece_'.$type.'"></div>' : '&nbsp;';
		} break;
		
		// 3: Graphical theme B
		case 3: {
			return strlen($type) ? '<div class="thmB piece_'.$type.'"></div>' : '&nbsp;';
		} break;
		
		// 4: Graphical theme C
		case 4: {
			return strlen($type) ? '<div class="thmC piece_'.$type.'"></div>' : '&nbsp;';
		} break;
	}
	
	return '&nbsp;'; // Error
}

function formatBoard($board) {
	global $game;
	
	$colnames = array('&nbsp;','a','b','c','d','e','f','g','h','&nbsp;');
	if ($game['flipped'] xor $game['whoami'] == PLAYER_B) {
		$colnames = array_reverse($colnames);
		$bounds = array(7, -1, -1, 1);
	} else {
		$bounds = array(0,  8, 1,  0);		
	}
	
	$colnames = '<tr><td>'.implode('</td><td>',$colnames).'</td></tr>';
	
	$response = '<table id="board" class="board p">';
	$response .= $colnames;
	for($i = $bounds[0]; $i != $bounds[1]; $i += $bounds[2]) {
		$response .= '<tr><td>'.(8 - $i).'</td>';
		for ($j = $bounds[0]; $j != $bounds[1]; $j += $bounds[2]) {
			$response .= '<td class="'.
				((($i + $j)%2 == $bounds[3]) ? 'bw' : 'bb').
				'">'.formatPiece($board[($i * 8)+$j], $game['theme']).'</td>'
			;
		}
		$response .= '<td>'.(8 - $i).'</td></tr>';
	}
	$response .= $colnames;
	$response .= '</table>';
	return $response;
}

function viewMessage() {
	if (! isset($_SESSION['info'])) return;
	echo
	'<div class="info" id="info">'.
		'<h2>'.$_SESSION['info'].'</h2>'.
		'<a href="javascript:;" onClick="hideInfo();" class="closer"></a>'.
	'</div>';
	unset($_SESSION['info']);
}

function viewMenu() {
	global $game;
	if ($game['whoami'] != PLAYER_S) {
		echo '<a href="?p=options&amp;id='.$game['auth'].'">Options</a> | ';
		echo '<a href="?p=game&amp;id='.$game['auth_spec'].'">Spectate</a> | ';			
	}
	if (strlen($game['auth'])) {		
		echo '<a href="rss.php?id='.$game['auth'].'">RSS</a> | ';
		echo '<a href="?p=game&amp;id='.$game['auth'].'">Board</a> | ';
		echo '<a href="?p=home">Log out</a>';
	} else {
		echo '<a href="?p=home">Home</a>';
	}	
}

function viewFooter() {
	global $page_create_time;
	echo '<div class="footer">';
	echo '-- '.APP_NAME . ' :: Page generated in '.
		number_format((microtime() - $page_create_time)*1000, 1).
	'ms --';
	echo '</div>';
}

function viewHistory($count = HISTORY_COUNT) {
	global $game;
	
	// Set timezone
	$timezone = DEFAULT_TZONE;
	if ($game['whoami'] == PLAYER_W) {
		$timezone = $game['player1']['timezone'];
	} elseif ($game['whoami'] == PLAYER_B) {
		$timezone = $game['player2']['timezone'];
	}
	date_default_timezone_set($timezone);
	
	// Load history
	$history = DB\GetHistory($game['id'], $count);
	
	foreach( $history as $hitem) {	
		if (! strlen($hitem['val'])) continue;
		if ($hitem['type'] == 1) {
			$h_content = S_PLAYED.$hitem['val'];
			$h_player = ($hitem['key'] == PLAYER_W) ? PLAYER_B : PLAYER_W;
		} else {
			$h_content = nl2br(htmlspecialchars($hitem['val']));
			$h_player = $hitem['key'];
		}
		$h_author = ($h_player == PLAYER_W) ? $game['player1']['name'] : $game['player2']['name'];
		
?>
		<div class="hist_<?php echo $h_player; ?>">
			<?php echo $h_content; ?> 
			<br />
			<span class="hist_author">
				-- <?php echo htmlspecialchars($h_author).', '.date(S_DATEFMT, $hitem['time']); ?> 
			</span>
		</div>
<?php 
	}
}

// ##########################################################
// HTML main views
// ##########################################################


function viewHome() {
?>
	<h2>About:</h2>
	<p><?php echo @file_get_contents('about.htm'); ?></p>
	<p>
		<?php echo APP_NAME; ?> is currently hosting
		<?php echo DB\CountGames(); ?> game(s).
	</p>

	<h2>New game:</h2>
	<form method="POST" action="?p=create" class="p">
		<input type="text" name="name1" title="<?php echo ST_WHITENAME;?>" />
		&nbsp;v&nbsp;
		<input type="text" name="name2" title="<?php echo ST_BLACKNAME;?>" />
		<input type="submit" value="Create &gt; &gt;" />			
	</form>

<?php
}

function viewOptions() {
	global $game;
	
	// Determine player
	switch ($game['whoami']) {
		case PLAYER_W: {
			$player = &$game['player1'];
		} break;
		case PLAYER_B: {
			$player = &$game['player2'];
		} break;
		default: return;
	}
	
	// Format email details
	$email_checked = ($player['doEmail'] == 1) ? 'checked' : '';
	$email_str = htmlspecialchars($player['email']);
	
	// Format theme details
	$themes = array(
		'0' => 'System Default (unicode)',
		'1' => 'Character Notation',
		'2' => 'Theme A',
		'3' => 'Theme B',
		'4' => 'Theme C'
	);
	$theme_html = array();
	foreach($themes as $th_id=>$th_name) {
		$theme_html[] = 
			'<option value="'.$th_id.'"'.
			(($player['theme']==$th_id)?'selected ':'').
			'>'.$th_name.'</option>'
		;
	}
	$theme_html = implode("\n", $theme_html);
	
	// Template
?>
	<h2>Options:</h2>
	<form method="POST" action="?p=setoptions" class="p">
	<input type="hidden" name="auth" value="<?php echo $game['auth']; ?>" />
		<table>
			<tr>
				<th colspan="2">Email Notifications</th>
			</tr>
			<tr>
				<td>Email Address:</td>
				<td><input type="email" name="email" title="Email address (optional)" value="<?php echo $email_str; ?>"/></td>
			</tr>
			<tr>
				<td>Enable notifications:</td>
				<td><input type="checkbox" name="doEmail" <?php echo $email_checked; ?> /></td>
			</tr>
			<tr>
				<th colspan="2">Cosmetic</th>
			</tr>
			<tr>
				<td>Timezone:</td>
				<td>
					<select name="timezone">
						<?php echo timezone_select_interior($player['timezone']); ?> 
					</select>
				</td>
			</tr>
			<tr>
				<td>Piece theme:</td>
				<td>
					<select name="theme">
						<?php echo $theme_html; ?>
					</select>
				</td>
			</tr>					
			<tr>
				<th colspan="2">Update</th>
			</tr>
			<tr>
				<td>&nbsp;</td>
				<td><input type="submit" value="Update &gt; &gt;" /></td>
			</tr>
		</table>
	</form>	
	
<?php
}

function viewGame() {
	global $game;
	
	// Format game title
	$player1 = htmlspecialchars($game['player1']['name']);
	$player2 = htmlspecialchars($game['player2']['name']);
	if (strlen($game['player1']['email'])) {
		$player1 = '<a href="mailto:'.htmlspecialchars($game['player1']['email']).'">'.$player1.'</a>';
	}
	if (strlen($game['player2']['email'])) {
		$player2 = '<a href="mailto:'.htmlspecialchars($game['player2']['email']).'">'.$player2.'</a>';
	}
	
	// Format authorisation
	$authtext = '';
	switch ($game['whoami']) {
		case PLAYER_W: $authtext = '(playing as white)'; break;
		case PLAYER_B: $authtext = '(playing as black)'; break;
		case PLAYER_S: $authtext = '(spectating)'; break;
	}
	
	// Move texts
	$col_to_move = ($game['turn'] == PLAYER_W) ? 'white' : 'black';
	$to_play = ($game['turn'] == PLAYER_W) ? $game['player1']['name'] : $game['player2']['name'];
	$formal_description = htmlspecialchars($to_play).'\'s turn to play '.$col_to_move.'.';
	
	// Flip URL
	$flip_url = main_url().'?p=game&amp;id='.$game['auth'].'&amp;flip='.($game['flipped'] ? '0' : '1');
	
	// Template
?>
	
	<h2>Game in Progress:</h2>
	<p>
		<?php echo $player1.' v '.$player2; ?><br/>
		<?php echo $authtext; ?> 
	</p>
	
	<h2>Board: <a href="<?php echo $flip_url; ?>">flip</a></h2>
	<?php echo formatBoard(explode('|', $game['board'])); ?> 
		
	<h2>Move:</h2>
<?php if ($game['turn'] == $game['whoami']) { ?> 
	<form method="POST" action="?p=move" class="p">
		<input type="hidden" name="auth" value="<?php echo $game['auth']; ?>" />
		<input type="text" maxlength=5 id="moveEntry" name="move"/>
		<input type="submit" value="Move &gt; &gt;" title="Your turn to play <?php echo $col_to_move; ?>" />
	</form>
<?php } else { ?>
	<p>
		<?php echo $formal_description; ?> 
	</p>
<?php } ?>
	
<?php if ($game['whoami'] != PLAYER_S) { ?> 
	<h2>Chat:</h2>
	<form method="POST" action="?p=chat" class="p">
		<input type="hidden" name="auth" value="<?php echo $game['auth']; ?>" />
		<textarea name="message"></textarea>
		<br/>
		<input type="submit" value="Say &gt; &gt;" />
	</form>
<?php } ?> 
	
	<h2>History: <a href="javascript:;" id="moreHistory">more</a></h2>
	<div id="history" class="p">
<?php viewHistory(); ?> 
	</div>
<?php
}


// ##########################################################
// HTML Template
// ##########################################################

$flip_board = ($game['flipped'] xor $game['whoami'] == PLAYER_B);

?>
<!DOCTYPE html>
<html>
	<head>
		<title><?php echo APP_NAME; ?></title>
	
		<link rel="shortcut icon" href="favicon.ico" />
		<link rel="stylesheet" type="text/css" href="style.css" />
<?php if (strlen($game['auth'])) { ?>
		<link rel="alternate" type="application/rss+xml" href="rss.php?id=<?php echo $game['auth']; ?>" />
		<script>
var myAuth = "<?php echo $game['auth']; ?>";
var board_reverse = <?php echo $flip_board ? 'true' : 'false'; ?>;
		</script>
<?php } ?>
				
		<script src="scripts.js"></script>
	</head>
	<body>
		<div class="content">
			<div class="menu">
				<div class="logo">&#9822;<?php echo APP_NAME; ?></div>
				<?php viewMenu(); ?> 
			</div>
		
			<?php viewMessage(); ?>
			
			<div class="padder">
<?php
switch ($current_mode) {
	case MODE_HOME: {
		viewHome();
	} break;
	case MODE_GAME: {
		viewGame();
	} break;
	case MODE_OPTIONS: {
		viewOptions();
	} break;
}
viewFooter();
?> 
			</div>
		</div>
	</body>
</html>
