<?php

/*
	Phoronix Test Suite
	URLs: http://www.phoronix.com, http://www.phoronix-test-suite.com/
	Copyright (C) 2010 - 2012, Phoronix Media
	Copyright (C) 2010 - 2012, Michael Larabel

	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 3 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, see <http://www.gnu.org/licenses/>.
*/

class pts_openbenchmarking
{
	protected static $openbenchmarking_index_refreshed = false;

	public static function stats_hardware_list()
	{
		return array(
			'cpu' => array('cpu', 'model'),
			'cpu_count' => array('cpu', 'core-count'),
			'cpu_speed' => array('cpu', 'mhz-default-frequency'),
			'chipset' => array('chipset'),
			'motherboard' => array('motherboard'),
			'gpu' => array('gpu', 'model'),
			'disk' => array('disk', 'identifier'),
			'audio' => array('audio', 'identifier'),
			'monitor' => array('monitor', 'identifier')
			);
	}
	public static function valid_user_name()
	{
		$invalid_users = array('pts', 'phoronix', 'local');
		// TODO: finish function
	}
	public static function stats_software_list()
	{
		return array(
			'os' => array('system', 'operating-system'),
			'os_architecture' => array('system', 'kernel-architecture'),
			'kernel' => array('system', 'kernel'),
			'display_server' => array('system', 'display-server'),
			'display_driver' => array('system', 'display-driver-string'),
			'opengl' => array('system', 'opengl-driver'),
			'desktop' => array('system', 'desktop-environment'),
			'compiler' => array('system', 'compiler'),
			'file_system' => array('system', 'filesystem'),
			'screen_resolution' => array('gpu', 'screen-resolution-string')
			);
	}
	public static function is_valid_gsid_format($gsid)
	{
		$gsid_valid = false;

		if(strlen($gsid) == 9)
		{
			if(ctype_upper(substr($gsid, 0, 6)) && ctype_digit(substr($gsid, 6, 3)))
			{
				$gsid_valid = true;
			}
		}

		return $gsid_valid;
	}
	public static function is_valid_gsid_e_format($gside)
	{
		$gside_valid = false;

		if(strlen($gside) == 12)
		{
			if(ctype_upper(substr($gside, 0, 10)) && ctype_digit(substr($gside, 10, 2)))
			{
				$gside_valid = true;
			}
		}

		return $gside_valid;
	}
	public static function is_valid_gsid_p_format($gsidp)
	{
		$gsidp_valid = false;

		if(strlen($gsidp) == 10)
		{
			if(ctype_upper(substr($gsidp, 0, 9)) &&	ctype_digit(substr($gsidp, 9, 1)))
			{
				$gsidp_valid = true;
			}
		}

		return $gsidp_valid;
	}
	public static function is_openbenchmarking_result_id($id)
	{
		$is_id = false;

		if(self::is_string_openbenchmarking_result_id_compliant($id))
		{
			$json_response = pts_openbenchmarking::make_openbenchmarking_request('is_openbenchmarking_result', array('i' => $id));
			$json_response = json_decode($json_response, true);

			if(is_array($json_response) && isset($json_response['openbenchmarking']['result']['valid']) && $json_response['openbenchmarking']['result']['valid'] == 'TRUE')
			{
				$is_id = true;
			}
		}

		return $is_id;
	}
	public static function clone_openbenchmarking_result(&$id, $return_xml = false)
	{
		$json_response = pts_openbenchmarking::make_openbenchmarking_request('clone_openbenchmarking_result', array('i' => $id));
		$json_response = json_decode($json_response, true);
		$valid = false;

		if(is_array($json_response) && isset($json_response['openbenchmarking']['result']['composite_xml']))
		{
			$composite_xml = $json_response['openbenchmarking']['result']['composite_xml'];

			$result_file = new pts_result_file($composite_xml);

			if($result_file->xml_parser->validate())
			{
				$result_file_writer = new pts_result_file_writer();
				$result_file_writer->add_result_file_meta_data($result_file, $id);
				$result_file_writer->add_system_information_from_result_file($result_file);
				$result_file_writer->add_results_from_result_file($result_file);
				//$id = strtolower($id);

				$valid = $return_xml ? $result_file_writer->get_xml() : pts_client::save_test_result($id . '/composite.xml', $result_file_writer->get_xml(), true);

				if(PTS_IS_CLIENT && $json_response['openbenchmarking']['result']['system_logs_available'])
				{
					// Fetch the system logs and toss them into the results directory system-logs/
					pts_openbenchmarking::clone_openbenchmarking_result_system_logs($id, pts_client::setup_test_result_directory($id), $json_response['openbenchmarking']['result']['system_logs_available']);
				}
			}
			else
			{
				trigger_error('Validating the result file schema failed.', E_USER_ERROR);
			}
		}
		else if(PTS_IS_CLIENT && isset($json_response['openbenchmarking']['result']['error']))
		{
			trigger_error($json_response['openbenchmarking']['result']['error'], E_USER_ERROR);
		}

		return $valid;
	}
	public static function clone_openbenchmarking_result_system_logs(&$id, $extract_to, $sha1_compare = null)
	{
		$system_log_response = pts_openbenchmarking::make_openbenchmarking_request('clone_openbenchmarking_system_logs', array('i' => $id));
		$extracted = false;

		if($system_log_response != null)
		{
			$zip_temp = pts_client::create_temporary_file();
			file_put_contents($zip_temp, $system_log_response);

			if($sha1_compare == null || sha1_file($zip_temp) == $sha1_compare)
			{
				// hash check of file passed or was null
				$extracted = pts_compression::zip_archive_extract($zip_temp, $extract_to);
			}

			unlink($zip_temp);
		}

		return $extracted;
	}
	public static function is_string_openbenchmarking_result_id_compliant($id)
	{
		$valid = false;

		if(strlen($id) == 22)
		{
			$segments = explode('-', $id);

			if(count($segments) == 3)
			{
				if(strlen($segments[0]) == 7 && is_numeric($segments[0]))
				{
					$us = strlen($segments[1]);

					if($us > 1 && $us < 9 && ctype_alnum($segments[1]))
					{
						if(ctype_alnum($segments[2]))
						{
							$valid = true;
						}
					}
				}
			}
		}

		return $valid;
	}
	public static function is_abstract_id($id)
	{
		$valid = false;

		if(strlen($id) == 4)
		{
			if(ctype_alpha($id))
			{
				$valid = true;
			}
		}

		return $valid;
	}
	public static function openbenchmarking_host()
	{
		static $host = null;

		if($host == null)
		{
			// Use HTTPS if OpenSSL is available as a check to see if HTTPS can be handled
			// OpenSSL seems to have problems on OpenIndiana at least, TODO: investigate
			// Using a proxy seems to have problems for HTTPS
			$host = ((extension_loaded('openssl') && getenv('NO_OPENSSL') == false && php_uname('s') == 'Linux' && (!PTS_IS_CLIENT || !pts_network::is_proxy_setup())) ? 'https://' : 'http://') . 'openbenchmarking.org/';
		}

		return $host;
	}
	public static function refresh_repository_lists($repos = null, $force_refresh = false)
	{
		if($repos == null)
		{
			if($force_refresh == false)
			{
				if(!defined('HAS_REFRESHED_OBO_LIST'))
				{
					pts_define('HAS_REFRESHED_OBO_LIST', true);
				}
				else
				{
					return true;
				}
			}

			$repos = self::linked_repositories();
		}

		foreach($repos as $repo_name)
		{
			pts_file_io::mkdir(PTS_OPENBENCHMARKING_SCRATCH_PATH . $repo_name);

			if($repo_name == 'local')
			{
				// Local is a special case, not actually a real repository
				continue;
			}

			if(!is_dir(PTS_OPENBENCHMARKING_SCRATCH_PATH . $repo_name))
			{
				mkdir(PTS_OPENBENCHMARKING_SCRATCH_PATH . $repo_name, 0777, true);
			}

			$index_file = PTS_OPENBENCHMARKING_SCRATCH_PATH . $repo_name . '.index';

			if(is_file($index_file))
			{
				$repo_index = json_decode(file_get_contents($index_file), true);
				$generated_time = $repo_index['main']['generated'];

				// Refreshing the indexes once every few days should be suffice
				// Refresh approximately every three days by default
				$index_cache_ttl = 3;
				if(PTS_IS_CLIENT && ($config_ttl = pts_config::read_user_config('PhoronixTestSuite/Options/OpenBenchmarking/IndexCacheTTL')))
				{
					if($config_ttl === 0)
					{
						// if the value is 0, only rely upon manual refreshes
						continue;
					}
					else if(is_numeric($config_ttl) && $config_ttl >= 1)
					{
						$index_cache_ttl = $config_ttl;
					}
				}

				if($generated_time > (time() - (86400 * $index_cache_ttl)) && $force_refresh == false && (!defined('FIRST_RUN_ON_PTS_UPGRADE') || FIRST_RUN_ON_PTS_UPGRADE == false))
				{
					// The index is new enough
					continue;
				}
			}

			$server_index = pts_openbenchmarking::make_openbenchmarking_request('repo_index', array('repo' => $repo_name));
			self::$openbenchmarking_index_refreshed = true;

			if(json_decode($server_index) != false)
			{
				file_put_contents($index_file, $server_index);
			}
			else if(PTS_IS_CLIENT && is_file('/var/cache/phoronix-test-suite/openbenchmarking.org/' . $repo_name . '.index'))
			{
				copy('/var/cache/phoronix-test-suite/openbenchmarking.org/' . $repo_name . '.index', $index_file);
			}

			if(!is_file($index_file))
			{
				static $reported_read_failure_notice;

				if(!isset($reported_read_failure_notice[$repo_name]) && PTS_IS_CLIENT)
				{
					trigger_error('Failed To Fetch OpenBenchmarking.org Repository Data: ' . $repo_name, E_USER_WARNING);
					$reported_read_failure_notice[$repo_name] = true;
				}
			}
		}
	}
	public static function openbenchmarking_has_refreshed()
	{
		return self::$openbenchmarking_index_refreshed;
	}
	public static function linked_repositories()
	{
		$repos = array('local', 'pts');

		if(PTS_IS_CLIENT && pts_openbenchmarking_client::user_name() != false)
		{
			array_push($repos, pts_openbenchmarking_client::user_name());
		}

		return $repos;
	}
	public static function make_openbenchmarking_request($request, $post = array())
	{
		$url = pts_openbenchmarking::openbenchmarking_host() . 'f/client.php';
		$to_post = array_merge(array(
			'r' => $request,
			'client_version' => PTS_CORE_VERSION,
			'gsid' => (defined('PTS_GSID') ? PTS_GSID : null),
			'gsid_e' => (defined('PTS_GSID_E') ? PTS_GSID_E : null)
			), $post);

		if(PTS_IS_CLIENT && ($account = pts_openbenchmarking_client::get_openbenchmarking_account()) && is_array($account))
		{
			$to_post = array_merge($to_post, $account);
		}

		return pts_network::http_upload_via_post($url, $to_post);
	}
	public static function read_repository_index($repo_name)
	{
		$index_file = PTS_OPENBENCHMARKING_SCRATCH_PATH . $repo_name . '.index';

		if(is_file($index_file))
		{
			$index_file = file_get_contents($index_file);
			$index_file = json_decode($index_file, true);
		}

		return $index_file;
	}
	public static function download_test_profile($qualified_identifier)
	{
		if(is_file(PTS_TEST_PROFILE_PATH . $qualified_identifier . '/test-definition.xml'))
		{
			return true;
		}

		$file = PTS_OPENBENCHMARKING_SCRATCH_PATH . $qualified_identifier . '.zip';

		$hash_json = pts_openbenchmarking::make_openbenchmarking_request('test_hash', array('i' => $qualified_identifier));
		$hash_json = json_decode($hash_json, true);
		$hash_check = isset($hash_json['openbenchmarking']['test']['hash']) ? $hash_json['openbenchmarking']['test']['hash'] : null;  // should also check for ['openbenchmarking']['test']['error'] problems

		if(!is_file($file))
		{
			$test_profile = pts_openbenchmarking::make_openbenchmarking_request('download_test', array('i' => $qualified_identifier));

			if($test_profile != null && ($hash_check == null || $hash_check == sha1($test_profile)))
			{
				// save it
				file_put_contents($file, $test_profile);
				$hash_check = null;
			}
			else if(is_file('/var/cache/phoronix-test-suite/openbenchmarking.org/' . $qualified_identifier . '.zip') && ($hash_check == null || sha1_file('/var/cache/phoronix-test-suite/openbenchmarking.org/' . $qualified_identifier . '.zip') == $hash_check))
			{
				copy('/var/cache/phoronix-test-suite/openbenchmarking.org/' . $qualified_identifier . '.zip', $file);
			}
			else if(PTS_IS_CLIENT && $test_profile === false)
			{
				trigger_error('Network support is needed to obtain ' . $qualified_identifier . ' data.' . PHP_EOL, E_USER_ERROR);
				return false;
			}
		}

		if(!is_file(PTS_TEST_PROFILE_PATH . $qualified_identifier . '/test-definition.xml') && is_file($file) && ($hash_check == null || sha1_file($file) == $hash_check))
		{
			// extract it
			pts_file_io::mkdir(PTS_TEST_PROFILE_PATH . dirname($qualified_identifier));
			pts_file_io::mkdir(PTS_TEST_PROFILE_PATH . $qualified_identifier);
			return pts_compression::zip_archive_extract($file, PTS_TEST_PROFILE_PATH . $qualified_identifier);
		}

		return false;
	}
	public static function available_tests($download_tests = true)
	{
		$available_tests = array();

		foreach(self::linked_repositories() as $repo)
		{
			$repo_index = pts_openbenchmarking::read_repository_index($repo);

			if(isset($repo_index['tests']) && is_array($repo_index['tests']))
			{
				foreach(array_keys($repo_index['tests']) as $identifier)
				{
					if($download_tests && pts_network::network_support_available())
					{
						$version = array_shift($repo_index['tests'][$identifier]['versions']);
						if(self::download_test_profile($repo . '/' . $identifier . '-' . $version) == false)
						{
							continue;
						}
					}

					array_push($available_tests, $repo . '/' . $identifier);
				}
			}
		}

		return $available_tests;
	}
	public static function available_suites($download_suites = true)
	{
		$available_suites = array();

		foreach(self::linked_repositories() as $repo)
		{
			$repo_index = pts_openbenchmarking::read_repository_index($repo);

			if(isset($repo_index['suites']) && is_array($repo_index['suites']))
			{
				foreach(array_keys($repo_index['suites']) as $identifier)
				{
					if($download_suites && pts_network::network_support_available())
					{
						$version = array_shift($repo_index['suites'][$identifier]['versions']);
						if(self::download_test_suite($repo . '/' . $identifier . '-' . $version) == false)
						{
							continue;
						}
					}
					array_push($available_suites, $repo . '/' . $identifier);
				}
			}
		}

		return $available_suites;
	}
	public static function download_test_suite($qualified_identifier)
	{
		if(is_file(PTS_TEST_SUITE_PATH . $qualified_identifier . '/suite-definition.xml'))
		{
			return true;
		}

		$file = PTS_OPENBENCHMARKING_SCRATCH_PATH . $qualified_identifier . '.zip';

		$hash_json = pts_openbenchmarking::make_openbenchmarking_request('suite_hash', array('i' => $qualified_identifier));
		$hash_json = json_decode($hash_json, true);
		$hash_check = isset($hash_json['openbenchmarking']['suite']['hash']) ? $hash_json['openbenchmarking']['suite']['hash'] : null;  // should also check for ['openbenchmarking']['suite']['error'] problems

		if(!is_file($file))
		{
			$test_suite = pts_openbenchmarking::make_openbenchmarking_request('download_suite', array('i' => $qualified_identifier));

			if($test_suite != null && ($hash_check == null || $hash_check == sha1($test_suite)))
			{
				// save it
				file_put_contents($file, $test_suite);
				$hash_check = null;
			}
			else if(is_file('/var/cache/phoronix-test-suite/openbenchmarking.org/' . $qualified_identifier . '.zip') && ($hash_check == null || sha1_file('/var/cache/phoronix-test-suite/openbenchmarking.org/' . $qualified_identifier . '.zip') == $hash_check))
			{
				copy('/var/cache/phoronix-test-suite/openbenchmarking.org/' . $qualified_identifier . '.zip', $file);
			}
			else if(PTS_IS_CLIENT && $test_suite === false)
			{
				trigger_error('Network support is needed to obtain ' . $qualified_identifier . ' data.' . PHP_EOL, E_USER_ERROR);
				return false;
			}
		}

		if(!is_file(PTS_TEST_SUITE_PATH . $qualified_identifier . '/suite-definition.xml') && ($hash_check == null || (is_file($file) && sha1_file($file) == $hash_check)))
		{
			// extract it
			pts_file_io::mkdir(PTS_TEST_SUITE_PATH . dirname($qualified_identifier));
			pts_file_io::mkdir(PTS_TEST_SUITE_PATH . $qualified_identifier);
			return pts_compression::zip_archive_extract($file, PTS_TEST_SUITE_PATH . $qualified_identifier);
		}

		return false;
	}
	protected static function check_only_type_compare($check_only_type, $is_type)
	{
		return $check_only_type == false || $check_only_type === $is_type;
	}
	public static function evaluate_string_to_qualifier($supplied, $bind_version = true, $check_only_type = false)
	{
		$qualified = false;
		$c_repo = null;
		$repos = self::linked_repositories();

		if(($c = strpos($supplied, '/')) !== false)
		{
			// A repository was explicitly defined
			$c_repo = substr($supplied, 0, $c);
			$test = substr($supplied, ($c + 1));

			// If it's in the linked repo list it should have refreshed when starting client
			if(!in_array($c_repo, $repos))
			{
				// Pull in this repository's index
				pts_openbenchmarking::refresh_repository_lists($repos);
			}

			$repos = array($c_repo);
		}
		else
		{
			// If it's in the linked repo list it should have refreshed when starting client
			$test = $supplied;
		}

		if(($c = strrpos($test, '-')) !== false)
		{
			$version = substr($test, ($c + 1));

			// TODO: functionalize this and read against types.xsd
			if(isset($version[2]) && !isset($version[8]) && pts_strings::string_only_contains($version, (pts_strings::CHAR_NUMERIC | pts_strings::CHAR_DECIMAL)))
			{
				$test = substr($test, 0, $c);
			}
			else
			{
				$version = null;
			}
		}
		else
		{
			$version = null;
		}

		foreach($repos as $repo)
		{
			if($repo == 'local')
			{
				if(self::check_only_type_compare($check_only_type, 'test'))
				{
					if(is_file(PTS_TEST_PROFILE_PATH . $repo . '/' . $test . '/test-definition.xml'))
					{
						return $repo . '/' . $test; // ($bind_version ? '-' . $version : null)
					}
					else if(is_file(PTS_TEST_PROFILE_PATH . $repo . '/' . $test . '-' . $version . '/test-definition.xml'))
					{
						return $repo . '/' . $test . '-' . $version; // ($bind_version ? '-' . $version : null)
					}
				}

				if(self::check_only_type_compare($check_only_type, 'suite'))
				{
					if(is_file(PTS_TEST_SUITE_PATH . $repo . '/' . $test . '/suite-definition.xml'))
					{
						return $repo . '/' . $test; // ($bind_version ? '-' . $version : null)
					}
					else if(is_file(PTS_TEST_SUITE_PATH . $repo . '/' . $test . '-' . $version . '/suite-definition.xml'))
					{
						return $repo . '/' . $test . '-' . $version; // ($bind_version ? '-' . $version : null)
					}
				}
			}

			$repo_index = pts_openbenchmarking::read_repository_index($repo);

			if(is_array($repo_index) && isset($repo_index['tests'][$test]) && self::check_only_type_compare($check_only_type, 'test'))
			{
				// The test profile at least exists

				// Looking for a particular test profile version?
				if($version != null)
				{
					if(!in_array($version, $repo_index['tests'][$test]['versions']))
					{
						// Grep to see if the version passed was e.g. 1.3 instead of 1.3.3
						$versions = $repo_index['tests'][$test]['versions'];
						sort($versions);
						foreach(array_reverse($versions) as $check_version)
						{
							if(strstr($check_version, $version) != false)
							{
								$version = $check_version;
								break;
							}
						}
					}

					if(in_array($version, $repo_index['tests'][$test]['versions']))
					{
						pts_openbenchmarking::download_test_profile($repo . '/' . $test . '-' . $version);
						return $repo . '/' . $test . ($bind_version ? '-' . $version : null);
					}
				}
				else
				{
					// Assume to use the latest version unless something else is installed
					$available_versions = $repo_index['tests'][$test]['versions'];
					$version = $available_versions[0]; // the latest version available

					if((pts_c::$test_flags & pts_c::is_run_process))
					{
						// Check to see if an older version of the test profile is currently installed
						foreach($available_versions as $i => $v)
						{
							if(is_file(pts_client::test_install_root_path() . $repo . '/' . $test . '-' . $v . '/pts-install.xml'))
							{
								$version = $v;

								if($i > 0 && (pts_c::$test_flags ^ pts_c::batch_mode))
								{
									// It's not the latest test profile version available
									trigger_error($repo . '/' . $test . ': The latest test profile version available for upgrade is ' . $available_versions[0] . ' but version ' . $version . ' is the latest currently installed.', E_USER_WARNING);
								}
								break;
							}
						}
					}

					pts_openbenchmarking::download_test_profile($repo . '/' . $test . '-' . $version);
					return $repo . '/' . $test . ($bind_version ? '-' . $version : null);
				}
			}
			if(is_array($repo_index) && isset($repo_index['suites'][$test]) && self::check_only_type_compare($check_only_type, 'suite'))
			{
				// The test profile at least exists

				// Looking for a particular test profile version?
				if($version != null)
				{
					if(!in_array($version, $repo_index['suites'][$test]['versions']))
					{
						// Grep to see if the version passed was e.g. 1.3 instead of 1.3.3
						$versions = $repo_index['suites'][$test]['versions'];
						sort($versions);
						foreach(array_reverse($versions) as $check_version)
						{
							if(strstr($check_version, $version) != false)
							{
								$version = $check_version;
								break;
							}
						}
					}

					if(in_array($version, $repo_index['suites'][$test]['versions']))
					{
						pts_openbenchmarking::download_test_suite($repo . '/' . $test . '-' . $version);
						return $repo . '/' . $test . ($bind_version ? '-' . $version : null);
					}
				}
				else
				{
					// Assume to use the latest version
					$version = array_shift($repo_index['suites'][$test]['versions']);
					pts_openbenchmarking::download_test_suite($repo . '/' . $test . '-' . $version);
					return $repo . '/' . $test . ($bind_version ? '-' . $version : null);
				}
			}
		}

		return false;
	}
	public static function upload_test_result(&$object)
	{
		return pts_openbenchmarking_client::upload_test_result($object);
	}
}

?>
