<?php
/* vim: set noexpandtab tabstop=2 softtabstop=2 shiftwidth=2: */

/**
 * TMNDataFetcher - Fetch TMN ladder/nation/server stats for a login
 * Created by (OoR-F)~fuckfish (fish@stabb.de)
 * Updated by Xymph <tm@gamers.org>
 *
 * v1.8: Improved error reporting via $error; fixed file check in extendedInfo
 *       processing; added magic __set_state function to support var_export()
 * v1.7: Improve handling of empty API responses
 * v1.6: Fixed $teamrank type into int; added User-Agent to the GET request
 * v1.5: Tweaked initial get_file return value check
 * v1.4: Fixed get_file return value checks; fixed $nationrank check
 * v1.3: Optimized get_file URL parsing
 * v1.2: Added get_file function to handle master server timeouts
 * v1.1: General code cleanup; added more comments; added $lastmatch,
 *       $totalplayers, $nationplayers, $nationpos, $nationpoints,
 *       $totalnations, $servernick, $serverdesc, $servernation;
 *       renamed $actualserver to $serverlogin
 * v1.0: Initial release
 */
class TMNDataFetcher {

	public $version, $extended, $error,
	       $login, $nickname, $worldrank, $totalplayers,
	       $points, $lastmatch, $wins, $losses, $draws,
	       $stars, $stardays, $teamname, $teamrank, $totalteams,
	       $nation, $nationrank, $nationplayers,
	       $nationpos, $nationpoints, $totalnations,
	       $online, $serverlogin, $servernick, $serverdesc, $servernation;

	/**
	 * Fetches a hell of a lot of data about a TMN login
	 *
	 * @param String $login
	 *        The TMN login to search for
	 * @param Boolean $extendedInfo
	 *        If true, the script also searches for the server that the
	 *        player is on at the moment (also determines online-state)
	 * @return TMNDataFetcher
	 *        If $nickname is empty, login was not found
	 */
	function TMNDataFetcher($login, $extendedInfo) {

		$this->version = '0.1.7.9';
		$this->error = '';
		$this->extended = $extendedInfo;
		$this->login = strtolower($login);
		$this->getData();
	}  // TMNDataFetcher

	public static function __set_state($import) {

		$tmn = new TMNDataFetcher('', true);

		$tmn->version       = $import['version'];
		$tmn->extended      = $import['extended'];
		$tmn->error         = '';
		$tmn->login         = $import['login'];
		$tmn->nickname      = $import['nickname'];
		$tmn->worldrank     = $import['worldrank'];
		$tmn->totalplayers  = $import['totalplayers'];
		$tmn->points        = $import['points'];
		$tmn->lastmatch     = $import['lastmatch'];
		$tmn->wins          = $import['wins'];
		$tmn->losses        = $import['losses'];
		$tmn->draws         = $import['draws'];
		$tmn->stars         = $import['stars'];
		$tmn->stardays      = $import['stardays'];
		$tmn->teamname      = $import['teamname'];
		$tmn->teamrank      = $import['teamrank'];
		$tmn->totalteams    = $import['totalteams'];
		$tmn->nation        = $import['nation'];
		$tmn->nationrank    = $import['nationrank'];
		$tmn->nationplayers = $import['nationplayers'];
		$tmn->nationpos     = $import['nationpos'];
		$tmn->nationpoints  = $import['nationpoints'];
		$tmn->totalnations  = $import['totalnations'];
		$tmn->online        = $import['online'];
		$tmn->serverlogin   = $import['serverlogin'];
		$tmn->servernick    = $import['servernick'];
		$tmn->serverdesc    = $import['serverdesc'];
		$tmn->servernation  = $import['servernation'];

		return $tmn;
	}  // __set_state

	private function getData() {

		$url = 'http://game.trackmanianations.com/online_game/getplayerinfos.php?ver=' . $this->version . '&lang=en&login=' . $this->login;
		$line = $this->get_file($url);
		if ($line === false) {
			$this->error = 'Connection or response error on ' . $url;
			return;
		} else if ($line === -1) {
			$this->error = 'Timed out while reading data from ' . $url;
			return;
		} else if ($line == '' || strpos($line, '<br>') !== false) {
			$this->error = 'No data returned from ' . $url;
			return;
		}

		$array = explode(';', $line);
		if (!isset($array[6])) {
			$this->error = 'Cannot parse data returned data from ' . $url;
			return;
		}
		// 0 = $array[0];
		// login = $array[1];
		$this->nickname = urldecode($array[2]);
		$this->nation = $array[3];
		// empty = $array[4];
		$this->stars = $array[5];
		$this->stardays = $array[6];

		$url = 'http://ladder.trackmanianations.com/ladder/getstats.php?ver=' . $this->version . '&laddertype=g&login=' . $this->login;
		$line = $this->get_file($url);
		if ($line === false) {
			$this->error = 'Connection or response error on ' . $url;
			return;
		} else if ($line === -1) {
			$this->error = 'Timed out while reading data from ' . $url;
			return;
		} else if ($line == '' || strpos($line, '<br>') !== false) {
			$this->error = 'No data returned from ' . $url;
			return;
		}

		$array = explode(';', $line);
		if (!isset($array[10])) {
			$this->error = 'Cannot parse data returned data from ' . $url;
			return;
		}
		// 0 = $array[0];
		$this->totalplayers = $array[1];
		$this->wins = $array[2];
		$this->losses = $array[3];
		$this->draws = $array[4];
		$this->worldrank = $array[5];
		$this->points = $array[6];
		$this->lastmatch = $array[7];
		$this->teamname = urldecode($array[8]);
		$this->teamrank = (int)$array[9];
		$this->totalteams = $array[10];

		$url = 'http://ladder.trackmanianations.com/ladder/getstats.php?ver=' . $this->version . '&laddertype=g&login=' . $this->login . '&country=' . $this->nation;
		$line = $this->get_file($url);
		if ($line === false) {
			$this->error = 'Connection or response error on ' . $url;
			return;
		} else if ($line === -1) {
			$this->error = 'Timed out while reading data from ' . $url;
			return;
		} else if ($line == '' || strpos($line, '<br>') !== false) {
			$this->error = 'No data returned from ' . $url;
			return;
		}

		$array = explode(';', $line);
		// 0 = $array[1];
		if (isset($array[5]))
			$this->nationrank = $array[5];
		else
			$this->nationrank = '';
		// the remaining fields are the same as the world stats above

		$url = 'http://ladder.trackmanianations.com/ladder/getrankings.php?ver=' . $this->version . '&laddertype=g&start=0&limit=0&country=' . $this->nation;
		$line = $this->get_file($url);
		if ($line === false) {
			$this->error = 'Connection or response error on ' . $url;
			return;
		} else if ($line === -1) {
			$this->error = 'Timed out while reading data from ' . $url;
			return;
		} else if ($line == '' || strpos($line, '<br>') !== false) {
			$this->error = 'No data returned from ' . $url;
			return;
		}

		$array = explode(';', $line);
		if (!isset($array[1])) {
			$this->error = 'Cannot parse data returned data from ' . $url;
			return;
		}
		// 0 = $array[1];
		$this->nationplayers = $array[1];
		// 1;login;nickname;nation;points = $array[2-6];
		// 2;login;nickname;nation;points = $array[7-11];  etc.etc.

		$url = 'http://ladder.trackmanianations.com/ladder/getcountriesrankings.php?ver=' . $this->version . '&laddertype=g&lang=en&start=0&limit=100';
		$line = $this->get_file($url);
		if ($line === false) {
			$this->error = 'Connection or response error on ' . $url;
			return;
		} else if ($line === -1) {
			$this->error = 'Timed out while reading data from ' . $url;
			return;
		} else if ($line == '' || strpos($line, '<br>') !== false) {
			$this->error = 'No data returned from ' . $url;
			return;
		}

		$array = explode(';', $line);
		if (!isset($array[1])) {
			$this->error = 'Cannot parse data returned data from ' . $url;
			return;
		}
		// 0 = $array[1];
		$this->totalnations = $array[1];
		// 1;nation;points = $array[2-4];
		// 2;nation;points = $array[5-7];  etc.etc.
		$i = 2;
		while (isset($array[$i]) && $array[$i] != '') {
			if ($array[$i+1] == $this->nation) {
				$this->nationpos = $array[$i];
				$this->nationpoints = $array[$i+2];
				break;
			}
			$i += 3;
		}

		$this->online = false;
		// check online status too?
		if ($this->extended) {
			$page = $this->get_file('http://game.trackmanianations.com/online_game/www_serverslist.php');
			if ($page === false || $page == -1 || $page == '')
				// no error message if main info was already fetched
				return;

			$lines = explode('<host>', $page);
			foreach ($lines as $line) {
				if (stripos($line, '<player>' . $this->login . '</player>') !== false) {
					$this->online = true;
					$this->serverlogin = substr($line, 0, strpos($line, '</host>'));
					break;
				}
			}

			if ($this->online) {
				$page = $this->get_file('http://game.trackmanianations.com/online_game/browse_top.php?ver=0.1.7.9&lang=en&key=XXXX-XXXX-XXXX-XXXX-XXX&nb=100&page=1&flatall=1');
				if ($page === false || $page == -1 || $page == '')
					// no error message if main info was already fetched
					return;

				$server = $this->serverlogin;  // can't use object member inside pattern
				if (preg_match("/^${server};([^;]+);([^;]+);([A-Z]+);/m", $page, $fields)) {
					$this->serverdesc = $fields[1];
					$this->servernick = ($fields[2] != 'x' ? urldecode($fields[2]) : '');
					$this->servernation = $fields[3];
				}
			}
		}
	}  // getData

	// Simple HTTP Get function with timeout
	// ok: return string || error: return false || timeout: return -1
	private function get_file($url) {

		$url = parse_url($url);
		$port = isset($url['port']) ? $url['port'] : 80;
		$query = isset($url['query']) ? "?" . $url['query'] : "";

		$fp = @fsockopen($url['host'], $port, $errno, $errstr, 4);
		if (!$fp)
			return false;

		fwrite($fp, 'GET ' . $url['path'] . $query . " HTTP/1.0\r\n" .
		            'Host: ' . $url['host'] . "\r\n" .
		            'User-Agent: TMNDataFetcher (' . PHP_OS . ")\r\n\r\n");
		stream_set_timeout($fp, 2);
		$res = '';
		$info['timed_out'] = false;
		while (!feof($fp) && !$info['timed_out']) {
			$res .= fread($fp, 512);
			$info = stream_get_meta_data($fp);
		}
		fclose($fp);

		if ($info['timed_out']) {
			return -1;
		} else {
			if (substr($res, 9, 3) != '200')
				return false;
			$page = explode("\r\n\r\n", $res, 2);
			return trim($page[1]);
		}
	}  // get_file
}  // class TMNDataFetcher
?>
