<?php
/**
 * Copyright 2009, 2010  Matthieu Aubry & Piwik team
 * All rights reserved.
 *
 * @link http://dev.piwik.org/trac/browser/trunk/libs/UserAgentParser
 * @license http://www.opensource.org/licenses/bsd-license.php BSD License
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 * * Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 *
 * * Neither the name of Matthieu Aubry nor the names of its contributors
 *   may be used to endorse or promote products derived from this
 *   software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/**
 * Example usage
 * 
 * Browser info:
 * var_dump(UserAgentParser::getBrowser($_SERVER['HTTP_USER_AGENT']));
 * 
 * Outputs:
 * array
 * 	'id' => 'FF' 
 *  'name' => 'Firefox'
 *  'short_name' => 'Firefox'
 *  'version' => '3.0'
 *  'major_number' => '3'
 *  'minor_number' => '0'
 * 
 * Operating System info:
 * var_dump(UserAgentParser::getOperatingSystem($_SERVER['HTTP_USER_AGENT']));
 *
 * Outputs:
 * array
 *  'id' => 'WXP'
 * 	'name' => 'Windows XP'
 * 	'short_name' => 'Win XP'
 * 
 */
class UserAgentParser 
{
	// browser regex => browser ID
	// if there are aliases, the common name should be last
	static protected $browsers = array(
			'abrowse'						=> 'AB',
			'amaya'							=> 'AM',
			'amigavoyager'					=> 'AV',
			'amiga-aweb'					=> 'AW',
			'arora'							=> 'AR',
			'beonex'						=> 'BE',

			// BlackBerry smartphones and tablets
			'blackberry'					=> 'BB', // BlackBerry 6 and PlayBook adopted webkit
			'bb10'							=> 'B2', // BlackBerry 10
			'playbook'						=> 'BP',

			'browsex'						=> 'BX',

			// Camino (and earlier incarnation)
			'chimera'						=> 'CA',
			'camino'						=> 'CA',

			'cheshire'						=> 'CS',

			// Chrome, Chromium, and ChromePlus
			'crmo'							=> 'CH',
			'chrome'						=> 'CH',

			// Chrome Frame
			'chromeframe'					=> 'CF',

			'cometbird'						=> 'CO',
			'dillo'							=> 'DI',
			'elinks'						=> 'EL',
			'epiphany'						=> 'EP',
			'fennec'						=> 'FE',

			// Dolfin (or Dolphin)
			'dolfin'						=> 'DF',

			// Firefox (in its many incarnations and rebranded versions)
			'phoenix'						=> 'PX',
			'mozilla firebird'				=> 'FB',
			'firebird'						=> 'FB',
			'bonecho'						=> 'FF',
			'minefield'						=> 'FF',
			'namoroka'						=> 'FF',
			'shiretoko'						=> 'FF',
			'granparadiso'					=> 'FF',
			'iceweasel'						=> 'FF',
			'icecat'						=> 'FF',
			'firefox'						=> 'FF',

			'thunderbird'					=> 'TB',

			'flock'							=> 'FL',
			'fluid'							=> 'FD',
			'galeon'						=> 'GA',
			'google earth'					=> 'GE',
			'hana'							=> 'HA',
			'hotjava'						=> 'HJ',
			'ibrowse'						=> 'IB',
			'icab'							=> 'IC',

			// IE (including shells: Acoo, AOL, Avant, Crazy Browser, Green Browser, KKMAN, Maxathon)
			'msie'							=> 'IE',
			'microsoft internet explorer'	=> 'IE',
			'internet explorer'				=> 'IE',

			'iron'							=> 'IR',
			'kapiko'						=> 'KP',
			'kazehakase'					=> 'KZ',
			'k-meleon'						=> 'KM',
			'konqueror'						=> 'KO',
			'links'							=> 'LI',
			'lynx'							=> 'LX',
			'midori'						=> 'MI',

			// SeaMonkey (formerly Mozilla Suite) (and rebranded versions)
			'mozilla'						=> 'MO',
			'gnuzilla'						=> 'SM',
			'iceape'						=> 'SM',
			'seamonkey'						=> 'SM',

			// NCSA Mosaic (and incarnations)
			'mosaic'						=> 'MC',
			'ncsa mosaic'					=> 'MC',

			// Netscape Navigator
			'navigator'						=> 'NS',
			'netscape6'						=> 'NS',
			'netscape'						=> 'NS',

			'nx'							=> 'NF',
			'netfront'						=> 'NF',

			'omniweb'						=> 'OW',

			// Opera
			'nitro) opera'					=> 'OP',
			'opera'							=> 'OP',

			'rekonq'						=> 'RK',

			// Safari
			'safari'						=> 'SF',
			'applewebkit'					=> 'SF',

			'titanium'						=> 'TI',

			'webos'							=> 'WO',
			'webpro'						=> 'WP',
		);

	// browser family (by layout engine)
	static protected $browserType = array(
			'ie'	 => array('IE'),
			'gecko'  => array('NS', 'PX', 'FF', 'FB', 'CA', 'GA', 'KM', 'MO', 'SM', 'CO', 'FE', 'KP', 'KZ', 'TB'),
			'khtml'  => array('KO'),
			'webkit' => array('SF', 'CH', 'OW', 'AR', 'EP', 'FL', 'WO', 'AB', 'IR', 'CS', 'FD', 'HA', 'MI', 'GE', 'DF', 'BB', 'BP', 'TI', 'CF', 'RK', 'B2', 'NF'),
			'opera'  => array('OP'),
		);

	// WebKit version numbers to Apple Safari version numbers (if Version/X.Y.Z not present)
	static protected $safariVersions = array(
			'536.25'	=> array('6', '0'),
			'534.48'	=> array('5', '1'),
			'533.16'	=> array('5', '0'),
			'533.4'		=> array('4', '1'),
			'526.11.2'	=> array('4', '0'),
			'525.26'	=> array('3', '2'),
			'525.13'	=> array('3', '1'),
			'522.11'	=> array('3', '0'),
			'412'		=> array('2', '0'),
			'312'		=> array('1', '3'),
			'125'		=> array('1', '2'),
			'100'		=> array('1', '1'),
			'85'		=> array('1', '0'),
			'73'		=> array('0', '9'),
			'48'		=> array('0', '8'),
		);

	// OmniWeb build numbers to OmniWeb version numbers (if Version/X.Y.Z not present)
	static protected $omniWebVersions = array(
			'622.15'	=> array('5', '11'),
			'622.10'	=> array('5', '10'),
			'622.8'		=> array('5', '9'),
			'622.3'		=> array('5', '8'),
			'621'		=> array('5', '7'),
			'613'		=> array('5', '6'),
			'607'		=> array('5', '5'),
			'563.34'	=> array('5', '1'),
			'558.36'	=> array('5', '0'),
			'496'		=> array('4', '5'),
		);

	// OS regex => OS ID
	static protected $operatingSystems = array(
			'Android'				=> 'AND',
			'Maemo'					=> 'MAE',
			'CrOS '					=> 'LIN',
			'Linux'					=> 'LIN',

			'Xbox'					=> 'XBX',

			// workaround for vendors who changed the WinPhone 7 user agent
			'WP7'					=> 'WPH',

			'CYGWIN_NT-6.2'			=> 'WI8',
			'Windows NT 6.2'		=> 'WI8',
			'Windows 8'				=> 'WI8',
			'CYGWIN_NT-6.1'			=> 'WI7',
			'Windows NT 6.1'		=> 'WI7',
			'Windows 7'				=> 'WI7',
			'CYGWIN_NT-6.0'			=> 'WVI',
			'Windows NT 6.0'		=> 'WVI',
			'Windows Vista'			=> 'WVI',
			'CYGWIN_NT-5.2'			=> 'WS3',
			'Windows NT 5.2'		=> 'WS3',
			'Windows Server 2003 / XP x64' => 'WS3',
			'CYGWIN_NT-5.1'			=> 'WXP',
			'Windows NT 5.1'		=> 'WXP',
			'Windows XP'			=> 'WXP',
			'CYGWIN_NT-5.0'			=> 'W2K',
			'Windows NT 5.0'		=> 'W2K',
			'Windows 2000'			=> 'W2K',
			'CYGWIN_NT-4.0'			=> 'WNT',
			'Windows NT 4.0'		=> 'WNT',
			'WinNT'					=> 'WNT',
			'Windows NT'			=> 'WNT',
			'CYGWIN_ME-4.90'		=> 'WME',
			'Win 9x 4.90'			=> 'WME',
			'Windows ME'			=> 'WME',
			'CYGWIN_98-4.10'		=> 'W98',
			'Win98'					=> 'W98',
			'Windows 98'			=> 'W98',
			'CYGWIN_95-4.0'			=> 'W95',
			'Win32'					=> 'W95',
			'Win95'					=> 'W95',		
			'Windows 95'			=> 'W95',

			// Windows Phone OS 7 and above
			'Windows Phone OS'		=> 'WPH',

			// Windows Mobile 6.x and some later versions of Windows Mobile 5
			'IEMobile'			=> 'WMO', // fallback
			'Windows Mobile'		=> 'WMO',

			// Windows CE, Pocket PC, and Windows Mobile 5 are indistinguishable without vendor/device specific detection
			'Windows CE'			=> 'WCE',

			'iPod'					=> 'IPD',
			'iPad'					=> 'IPA',
			'iPhone'				=> 'IPH',
//			'iOS'					=> 'IOS',
			'Darwin'				=> 'MAC',
			'Macintosh'				=> 'MAC',
			'Power Macintosh'		=> 'MAC',
			'Mac_PowerPC'			=> 'MAC', 
			'Mac PPC'				=> 'MAC',
			'PPC'					=> 'MAC',
			'Mac PowerPC'			=> 'MAC',
			'Mac OS'				=> 'MAC',

			'webOS'					=> 'WOS',
			'Palm webOS'			=> 'WOS',
			'PalmOS'				=> 'POS',
			'Palm OS'				=> 'POS',

			'BB10'					=> 'BBX',
			'BlackBerry'			=> 'BLB',
			'RIM Tablet OS'			=> 'QNX',
			'QNX'					=> 'QNX',

			'SymbOS'				=> 'SYM',
			'Symbian OS'			=> 'SYM',
			'SymbianOS'				=> 'SYM',

			'bada'					=> 'SBA',

			'SunOS'					=> 'SOS',
			'AIX'					=> 'AIX',
			'HP-UX'					=> 'HPX',
			'OpenVMS'				=> 'VMS',

			'FreeBSD'				=> 'BSD',
			'NetBSD'				=> 'NBS',
			'OpenBSD'				=> 'OBS',
			'DragonFly'				=> 'DFB',
			'Syllable'				=> 'SYL',

			'Nintendo WiiU'			=> 'WIU',
			'Nintendo Wii'			=> 'WII',
			'Nitro'					=> 'NDS',
			'Nintendo DSi'			=> 'DSI',
			'Nintendo DS' 			=> 'NDS',
			'Nintendo 3DS'			=> '3DS',

			'PlayStation Vita'		=> 'PSV',
			'PlayStation Portable'	=> 'PSP',
			'PlayStation 3'			=> 'PS3',

			'IRIX'					=> 'IRI',
			'OSF1'					=> 'T64',
			'OS/2'					=> 'OS2',
			'BEOS'					=> 'BEO',
			'Amiga'					=> 'AMI',
			'AmigaOS'				=> 'AMI',
		);
	
	// os family
	// NOTE: The keys in this array are used by plugins/UserSettings/functions.php . Any  changes
	// made here should also be made in that file.
	static protected $osType = array(
			'Windows' => array('WI8', 'WI7', 'WVI', 'WS3', 'WXP', 'W2K', 'WNT', 'WME', 'W98', 'W95'),
			'Linux' => array('LIN'),
			'Mac' => array('MAC'),
			'iOS' => array('IPD', 'IPA', 'IPH'),
			'Android' => array('AND'),
			'Windows Mobile' => array('WPH', 'WMO', 'WCE'),
			'Gaming Console' => array('WII', 'WIU', 'PS3', 'XBX'),
			'Mobile Gaming Console' => array('PSP', 'PSV', 'NDS', 'DSI', '3DS'),
			'Unix' => array('SOS', 'AIX', 'HP-UX', 'BSD', 'NBS', 'OBS', 'DFB', 'SYL', 'IRI', 'T64'),
			'Other Mobile' => array('MAE', 'WOS', 'POS', 'BLB', 'QNX', 'SYM', 'SBA'),
			'Other' => array('VMS', 'OS2', 'BEOS', 'AMI')
		);

	static protected $browserIdToName;
	static protected $browserIdToShortName;
	static protected $operatingSystemsIdToName;
	static protected $operatingSystemsIdToShortName;
	static private $init = false;
	
	/**
	 * Returns an array of the OS for the submitted user agent 
	 *		'id' => '',
	 *		'name' => '',
	 *		'short_name' => '',
	 * 
	 * @param string $userAgent
	 * @return string false if OS couldn't be identified, or 3 letters ID (eg. WXP)
	 * @see UserAgentParser/OperatingSystems.php for the list of OS (also available in self::$operatingSystems)
	 */
	static public function getOperatingSystem($userAgent)
	{
		$userAgent = self::cleanupUserAgent($userAgent);
		self::init();
		$info = array(
			'id' => '',
			'name' => '',
			'short_name' => '',
		);
		foreach(self::$operatingSystems as $key => $value) {
			if (stristr($userAgent, $key) !== false) {
				$info['id'] = $value;
				break;
			}
		}
		if(empty($info['id'])) {
			return false;
		}
		$info['name'] = self::getOperatingSystemNameFromId($info['id']);
		$info['short_name'] = self::getOperatingSystemShortNameFromId($info['id']);
		return $info;
	}

	static protected function cleanupUserAgent($userAgent)
	{
		// in case value is URL encoded
		return urldecode($userAgent);
	}
	
	/**
	 * Returns the browser information array, given a user agent string.
	 * 
	 * @param string $userAgent
	 * @return array false if the browser is "unknown", or 
	 * 				array(		'id' 			=> '', // 2 letters ID, eg. FF 
	 * 							'name' 			=> '', // 2 letters ID, eg. FF 
	 * 							'short_name'	=> '', // 2 letters ID, eg. FF 
	 * 							'major_number' 	=> '', // 2 in firefox 2.0.12
	 * 							'minor_number' 	=> '', // 0 in firefox 2.0.12
	 * 							'version' 		=> '', // major_number.minor_number
	 * 				);
	 * @see self::$browsers for the list of OS 
	 */
	static public function getBrowser($userAgent)
	{
		$userAgent = self::cleanupUserAgent($userAgent);

		self::init();

		$info = array(
			'id' 			=> '',
			'name'			=> '',
			'short_name'	=> '',
			'major_number' 	=> '',
			'minor_number' 	=> '',
			'version' 		=> '',
		);

		$browsers = self::$browsers;

		// derivative browsers often clone the base browser's useragent
		unset($browsers['firefox']);
		unset($browsers['mozilla']);
		unset($browsers['safari']);
		unset($browsers['applewebkit']);

		$browsersPattern = str_replace(')', '\)', implode('|', array_keys($browsers)));

		$results = array();

		// Misbehaving IE add-ons
		$userAgent = preg_replace('/[; ]Mozilla\/[0-9.]+ \([^)]+\)/', '', $userAgent);

		// Clean-up BlackBerry device UAs
		$userAgent = preg_replace('~^BlackBerry\d+/~', 'BlackBerry/', $userAgent);

		if (preg_match_all("/($browsersPattern)[\/\sa-z(]*([0-9]+)([\.0-9a-z]+)?/i", $userAgent, $results)
			|| (strpos($userAgent, 'Shiira') === false && preg_match_all("/(firefox|thunderbird|safari)[\/\sa-z(]*([0-9]+)([\.0-9a-z]+)?/i", $userAgent, $results))
			|| preg_match_all("/(applewebkit)[\/\sa-z(]*([0-9]+)([\.0-9a-z]+)?/i", $userAgent, $results)
			|| preg_match_all("/^(mozilla)\/([0-9]+)([\.0-9a-z-]+)?(?: \[[a-z]{2}\])? (?:\([^)]*\))$/i", $userAgent, $results)
			|| preg_match_all("/^(mozilla)\/[0-9]+(?:[\.0-9a-z-]+)?\s\(.* rv:([0-9]+)([.0-9a-z]+)\) gecko(\/[0-9]{8}|$)(?:.*)/i", $userAgent, $results)
            || (strpos($userAgent, 'Nintendo 3DS') !== false && preg_match_all("/^(mozilla).*version\/([0-9]+)([.0-9a-z]+)?/i", $userAgent, $results))
			)
		 {
			// browser code (usually the first match)
			$count = 0;
			$info['id'] = self::$browsers[strtolower($results[1][0])];

			// sometimes there's a better match at the end
			if(strpos($userAgent, 'chromeframe') !== false) {
				$count = count($results[0]) - 1;
				$info['id'] = 'CF';
			}
			elseif(($info['id'] == 'IE' || $info['id'] == 'LX') && (count($results[0]) > 1)) {
				$count = count($results[0]) - 1;
				$info['id'] = self::$browsers[strtolower($results[1][$count])];
			}

			// Netscape fix
			if($info['id'] == 'MO' && $count == 0) {
				if(stripos($userAgent, 'PlayStation') !== false) {
					return false;
				}
                if(strpos($userAgent, 'Nintendo 3DS') !== false) {
                    $info['id'] = 'NF';
                }
				elseif(count($results) == 4) {
				 	$info['id'] = 'NS';
				}
			}
			// BlackBerry devices
			elseif(strpos($userAgent, 'BlackBerry') !== false) {
				$info['id'] = 'BB';
			}
			elseif(strpos($userAgent, 'RIM Tablet OS') !== false) {
				$info['id'] = 'BP';
			}
			elseif(strpos($userAgent, 'BB10') !== false) {
				$info['id'] = 'B2';
			}
			elseif(strpos($userAgent, 'Playstation Vita') !== false) {
				$info['id'] = 'NF';

				if(preg_match_all("/(silk)[\/\sa-z(]*([0-9]+)([\.0-9a-z]+)?/i", $userAgent, $newResults))
				{
					$results = $newResults;
					$count = count($results[0])-1;
				}
			}

			// Version/X.Y.Z override
			if(preg_match_all("/(version)[\/\sa-z(]*([0-9]+)([\.0-9a-z]+)?/i", $userAgent, $newResults))
			{
				$results = $newResults;
				$count = count($results[0])-1;
			}

		 	// major version number (1 in mozilla 1.7)
		 	$info['major_number'] = $results[2][$count];
		 		
		 	// is an minor version number ? If not, 0
		 	$match = array();
		 		
		 	preg_match('/([.\0-9]+)?([\.a-z0-9]+)?/i', $results[3][$count], $match);
		 		
		 	if(isset($match[1])) {
		 		// find minor version number (7 in mozilla 1.7, 9 in firefox 0.9.3)
		 		$dot = strpos(substr($match[1], 1), '.');
		 		if($dot !== false) {
		 			$info['minor_number'] = substr($match[1], 1, $dot);
		 		} 
		 		else {
		 			$info['minor_number'] = substr($match[1], 1);
		 		}
		 	}
		 	else {
		 		$info['minor_number'] = '0';
		 	}
		 	$info['version'] = $info['major_number'] . '.' . $info['minor_number'];

			// IE compatibility mode
			if($info['id'] == 'IE'
				&& strncmp($userAgent, 'Mozilla/4.0', 11) == 0
				&& preg_match('~ Trident/([0-9]+)\.[0-9]+~', $userAgent, $tridentVersion))
			{
				$info['major_number'] = $tridentVersion[1] + 4;
				$info['minor_number'] = '0';
				$info['version'] = $info['major_number'] . '.' .$info['minor_number'];
			}

			// Safari fix
			if($info['id'] == 'SF') {
				foreach(self::$safariVersions as $buildVersion => $productVersion) {
					if(version_compare($info['version'], $buildVersion) >= 0) {
						$info['major_number'] = $productVersion[0];
						$info['minor_number'] = $productVersion[1];
						$info['version'] = $info['major_number'] . '.' . $info['minor_number'];
						break;
					}
				}
			}

			// OmniWeb fix
			if($info['id'] == 'OW') {
				foreach(self::$omniWebVersions as $buildVersion => $productVersion) {
					if(version_compare($info['version'], $buildVersion) >= 0) {
						$info['major_number'] = $productVersion[0];
						$info['minor_number'] = $productVersion[1];
						$info['version'] = $info['major_number'] . '.' . $info['minor_number'];
						break;
					}
				}
			}

			// SeaMonkey fix
			if($info['id'] == 'MO' && $info['version'] == '1.9') {
				$info['id'] = 'SM';
			}

		 	$info['name'] = self::getBrowserNameFromId($info['id']);
		 	$info['short_name'] = self::getBrowserShortNameFromId($info['id']);

		 	return $info;
		 }

		 return false;
	}
	
	static protected function init() {
		if(self::$init) {
			return;
		}
		self::$init = true;
		
		// init browser names and short names
		self::$browserIdToName = array_map('ucwords',array_flip(self::$browsers));
		self::$browserIdToName['AB'] = 'ABrowse';
		self::$browserIdToName['AV'] = 'AmigaVoyager';
		self::$browserIdToName['AW'] = 'Amiga AWeb';
		self::$browserIdToName['BB'] = 'BlackBerry';
		self::$browserIdToName['BP'] = 'PlayBook';
		self::$browserIdToName['B2'] = 'BlackBerry';
		self::$browserIdToName['BX'] = 'BrowseX';
		self::$browserIdToName['CF'] = 'Chrome Frame';
		self::$browserIdToName['CO'] = 'CometBird';
		self::$browserIdToName['EL'] = 'ELinks';
		self::$browserIdToName['FF'] = 'Firefox';
		self::$browserIdToName['HJ'] = 'HotJava';
		self::$browserIdToName['IB'] = 'IBrowse';
		self::$browserIdToName['IC'] = 'iCab';
		self::$browserIdToName['KM'] = 'K-Meleon';
		self::$browserIdToName['MC'] = 'NCSA Mosaic';
		self::$browserIdToName['NF'] = 'NetFront';
		self::$browserIdToName['OW'] = 'OmniWeb';
		self::$browserIdToName['SF'] = 'Safari';
		self::$browserIdToName['SM'] = 'SeaMonkey';
		self::$browserIdToName['WO'] = 'Palm webOS';
		self::$browserIdToName['WP'] = 'WebPro';
		
		self::$browserIdToShortName = self::$browserIdToName;
		self::$browserIdToShortName['AW'] = 'AWeb';
		self::$browserIdToShortName['FB'] = 'Firebird';
		self::$browserIdToShortName['IE'] = 'IE';
		self::$browserIdToShortName['MC'] = 'Mosaic';
		self::$browserIdToShortName['BP'] = 'PlayBook';
		self::$browserIdToShortName['WO'] = 'webOS';
		
		// init OS names and short names
		self::$operatingSystemsIdToName = array_merge(array_flip(self::$operatingSystems), array(
			'IPD' => 'iPod',
			'IPA' => 'iPad',
			'WME' => 'Windows Me',
			'BEO' => 'BeOS',
			'T64' => 'Tru64',
			'NDS' => 'Nintendo DS',
			'WIU' => 'Nintendo Wii U',
			'3DS' => 'Nintendo 3DS',
		
			// These are for BC purposes only
			'W75' => 'WinPhone 7.5', 
			'WP7' => 'WinPhone 7', 
			'W65' => 'WinMo 6.5', 
			'W61' => 'WinMo 6.1', 
	
		));
		self::$operatingSystemsIdToShortName = array_merge(self::$operatingSystemsIdToName, array(
			'PS3' => 'PS3',
			'PSP' => 'PSP',
			'WII' => 'Wii',
			'WIU' => 'Wii U',
			'NDS' => 'DS',
			'DSI' => 'DSi',
			'3DS' => '3DS',
			'PSV' => 'PS Vita',
			'WI8' => 'Win 8',
			'WI7' => 'Win 7',
			'WVI' => 'Win Vista',
			'WS3' => 'Win S2003',
			'WXP' => 'Win XP',
			'W98' => 'Win 98',
			'W2K' => 'Win 2000', 
			'WNT' => 'Win NT',
			'WME' => 'Win Me',
			'W95' => 'Win 95',
			'WPH' => 'WinPhone',
			'WMO' => 'WinMo',
			'WCE' => 'Win CE',
			'WOS' => 'webOS',
			'UNK' => 'Unknown',
		));
	}

	static public function getBrowserNameFromId($browserId)
	{
		self::init();
		if(isset(self::$browserIdToName[$browserId])) {
			return self::$browserIdToName[$browserId];
		}
		return false;
	}

	static public function getBrowserShortNameFromId($browserId)
	{
		self::init();
		if(isset(self::$browserIdToShortName[$browserId])) {
			return self::$browserIdToShortName[$browserId];
		}
		return false;
	}

	static public function getBrowserFamilyFromId($browserId)
	{
		self::init();
		$familyNameToUse = 'unknown';
		foreach(self::$browserType as $familyName => $aBrowsers)
		{			
			if(in_array($browserId, $aBrowsers))
			{
				$familyNameToUse = $familyName;
				break;				
			}
		}
		return $familyNameToUse;	
	}

	static public function getOperatingSystemNameFromId($osId)
	{
		self::init();
		if(isset(self::$operatingSystemsIdToName[$osId])) {
			return self::$operatingSystemsIdToName[$osId];
		}
		return false;
	}
	
	static public function getOperatingSystemShortNameFromId($osId)
	{
		self::init();
		if(isset(self::$operatingSystemsIdToShortName[$osId])) {
			return self::$operatingSystemsIdToShortName[$osId];
		}
		return false;
	}
	
	static public function getOperatingSystemIdFromName($osName)
	{
		return isset(self::$operatingSystems[$osName]) ? self::$operatingSystems[$osName] : false;
	}
	
	static public function getOperatingSystemFamilyFromId($osId)
	{
		self::init();
		foreach (self::$osType as $familyName => $aSystems)
		{
			if (in_array($osId, $aSystems))
			{
				return $familyName;
			}
		}
		return 'unknown';
	}
}
