<?php
require_once("class.xmlparser.php");

define("TYPE_PLUGIN", 1);
define("TYPE_MODULE", 2);
define("TYPE_CONFIG", 3);
define("TYPE_NOTIFIER", 4);

/**
 * Managing component for all plugins
 *
 * This class handles all the plugin interaction with the webaccess on the server side.
 *
 * @package core
 */
class PluginManager
{
	// True if the Plugin framework is enabled
	var $enabled;

	// List of all plugins and their data
	var $plugindata;

	/**
	 * List of all hooks registered by plugins. 
	 * [eventID][] = plugin
	 */
	var $hooks;

	/**
	 * List of all plugin objects
	 * [pluginname] = pluginObj
	 */
	var $plugins;

	/**
	 * List of all provided modules
	 * [modulename] = moduleFile
	 */
	var $modules;

	/**
	 * List of all provided notifiers
	 * [notifiername] = notifierFile
	 */
	var $notifiers;

	/**
	 * List of sessiondata from plugins. 
	 * [pluginname] = sessiondata
	 */
	var $sessionData;

	/**
	 * Mapping for the XML 'load' attribute values
	 * to the corresponding define.
	 */
	var $loadMap = Array(
		'release' => LOAD_RELEASE,
		'debug' => LOAD_DEBUG,
		'source' => LOAD_SOURCE
	);

	/**
	 * Mapping for the XML 'type' attribute values
	 * to the corresponding define.
	 */
	var $typeMap = Array(
		'plugin' => TYPE_PLUGIN,
		'module' => TYPE_MODULE,
		'notifier' => TYPE_NOTIFIER
	);

	/**
	 * Constructor
	 */
	function PluginManager($enable = ENABLE_PLUGINS)
	{
		$this->enabled = $enable && defined('PATH_PLUGIN_DIR'); 
		$this->plugindata = Array();
		$this->hooks = Array();
		$this->plugins = Array();
		$this->modules = Array();
		$this->notifiers = Array();
		$this->sessionData = false;
	} 

	/**
	 * pluginsEnabled
	 * 
	 * Checks whether the plugins have been enabled by checking if the proper 
	 * configuration keys are set.
	 * @return boolean Returns true when plugins enabled, false when not.
	 */
	function pluginsEnabled(){
		return $this->enabled;
	}
	
	/**
	 * detectPlugins
	 * 
	 * Detecting the installed plugins either by using the already ready data 
	 * from the state object or otherwise read in all the data and write it into
	 * the state.
	 *
	 * @param String $disabled The list of plugins to disable, this list is seperated
	 * by the ';' character.
	 */
	function detectPlugins($disabled = ''){
		if (!$this->pluginsEnabled()) {
			return false;
		}

		// Get the plugindata from the state.
		$pluginState = new State('plugin');
		$pluginState->open();
		$this->plugindata = $pluginState->read("plugindata");

		// If no plugindata has been stored yet, get it from the plugins dir.
		if(!$this->plugindata || DEBUG_PLUGINS_DISABLE_CACHE){
			$disabledPlugins = Array();
			if (!empty($disabled)) {
				$disabledPlugins = explode(';', $disabled);
			}

			$this->plugindata = Array();
			$pluginsdir = opendir(PATH_PLUGIN_DIR);
			if($pluginsdir) {
				while(($plugin = readdir($pluginsdir)) !== false){
					if ($plugin != '.' && $plugin != '..' && !in_array($plugin, $disabledPlugins)){
						if(is_dir(PATH_PLUGIN_DIR.'/'.$plugin)){
							if(is_file(PATH_PLUGIN_DIR.'/'.$plugin.'/manifest.xml')){
								$this->processPlugin($plugin);
							}
						}
					}
				}

				closedir($pluginsdir);
			}
			// Write the plugindata to the state
			$pluginState->write("plugindata", $this->plugindata);
		}

		// Free the state again.
		$pluginState->close();
	}

	/**
	 * initPlugins
	 * 
	 * This function includes the server plugin classes, instantiate and 
	 * initialize them.
	 * 
	 * @param number $load One of LOAD_RELEASE, LOAD_DEBUG, LOAD_SOURCE. This will filter
	 * the files based on the 'load' attribute. 
	 */
	function initPlugins($load = LOAD_RELEASE){
		if(!$this->pluginsEnabled()){
			return false;
		}

		$files = $this->getServerFiles($load);
		foreach ($files['server'] as $file) {
			include_once($file);
		}

		// Inlcude the root files of all the plugins and instantiate the plugin
		foreach($this->plugindata as $plugName => &$plugData){
			$pluginClassName = 'Plugin' . $plugName;
			if(class_exists($pluginClassName)){
				$this->plugins[$plugName] = new $pluginClassName;
				$this->plugins[$plugName]->setPluginName($plugName);
				$this->plugins[$plugName]->init();
			}
		}
		unset($plugData);

		$this->modules = $files['modules'];
		$this->notifiers = $files['notifiers'];
	}

	/**
	 * processPlugin
	 * 
	 * Read in the manifest and get the files that need to be included 
	 * for placing hooks, defining modules, etc. 
	 * 
	 * @param $dirname string name of the directory of the plugin
	 */
	function processPlugin($dirname){
		// Read XML manifest file of plugin
		$handle = fopen(PATH_PLUGIN_DIR.'/'.$dirname.'/manifest.xml', 'rb');
		$xml = '';
		if($handle){
			while (!feof($handle)) { 
				$xml .= fread($handle, 8192);
			}
			fclose($handle);
		}

		$plugindata = $this->extractPluginDataFromXML($xml);
		if ($plugindata) {
			// Apply the name to the object
			$plugindata['pluginname'] = $dirname;
			$this->plugindata[$dirname] = $plugindata;
		} else {
			if (DEBUG_PLUGINS) {
				dump('[PLUGIN ERROR] Plugin "'.$dirname.'" has an invalid manifest.');
			}
		}
	}

	/**
	 * loadSessionData
	 * 
	 * Loads sessiondata of the plugins from disk. 
	 * To improve performance the data is only loaded if a 
	 * plugin requests (reads or saves) the data.
	 * 
	 * @param $pluginname string Identifier of the plugin
	 */
	function loadSessionData($pluginname) {
		// lazy reading of sessionData
		if (!$this->sessionData) {
			$sessState = new State('plugin_sessiondata');
			$sessState->open();
			$this->sessionData = $sessState->read("sessionData");
			if (!isset($this->sessionData) || $this->sessionData == "")
				$this->sessionData = array();
			$sessState->close();
		}
		if ($this->pluginExists($pluginname)) {
			if (!isset($this->sessionData[$pluginname])) {
				$this->sessionData[$pluginname] = array();
			}
			$this->plugins[ $pluginname ]->setSessionData($this->sessionData[$pluginname]);
		}
	}

	/**
	 * saveSessionData
	 * 
	 * Saves sessiondata of the plugins to the disk. 
	 * 
	 * @param $pluginname string Identifier of the plugin
	 */	
	function saveSessionData($pluginname) {
		if ($this->pluginExists($pluginname)) {
			$this->sessionData[$pluginname] = $this->plugins[ $pluginname ]->getSessionData();
		}
		if ($this->sessionData) {
			$sessState = new State('plugin_sessiondata');
			$sessState->open();
			$sessState->write("sessionData", $this->sessionData);
			$sessState->close();
		}
	}
	
	/**
	 * pluginExists
	 * 
	 * Checks if plugin exists.
	 * 
	 * @param $pluginname string Identifier of the plugin
	 * @return boolen True when plugin exists, false when it does not.
	 */
	function pluginExists($pluginname){
		if(isset($this->plugindata[ $pluginname ])){
			return true;
		}else{
			return false;
		}
	}

	/**
	 * getModuleFilePath
	 *
	 * Obtain the filepath of the given modulename
	 *
	 * @param $modulename string Identifier of the modulename
	 * @return string The path to the file for the module
	 */
	function getModuleFilePath($modulename)
	{
		if (isset($this->modules[$modulename])) {
			return $this->modules[$modulename];
		}

		return false;
	}

	/**
	 * getNotifierFilePath
	 *
	 * Obtain the filepath of the given notifiername
	 *
	 * @param $notifiername string Identifier of the notifiername
	 * @return string The path to the file for the notifier
	 */
	function getNotifierFilePath($notifiername)
	{
		if (isset($this->notifiers[$notifiername])) {
			return $this->notifiers[$notifiername];
		}

		return false;
	}

	/**
	 * registerHook
	 * 
	 * This function allows the plugin to register their hooks.
	 * 
	 * @param $eventID string Identifier of the event where this hook must be triggered.
	 * @param $pluginName string Name of the plugin that is registering this hook.
	 */
	function registerHook($eventID, $pluginName){
		$this->hooks[ $eventID ][ $pluginName ] = $pluginName;
	}

	/**
	 * triggerHook
	 * 
	 * This function will call all the registered hooks when their event is triggered.
	 * 
	 * @param $eventID string Identifier of the event that has just been triggered.
	 * @param $data mixed (Optional) Usually an array of data that the callback function can modify.
	 * @return mixed Data that has been changed by plugins.
	 */
	function triggerHook($eventID, $data = Array()){
		if(isset($this->hooks[ $eventID ]) && is_array($this->hooks[ $eventID ])){
			foreach($this->hooks[ $eventID ] as $key => $pluginname){
				$this->plugins[ $pluginname ]->execute($eventID, $data);
			}
		}
		return $data;
	}

	/**
	 * getServerFilesForComponent
	 *
	 * Called by getServerFiles() to return the list of files which are provided
	 * for the given component in a particular plugin.
	 * The paths which are returned start at the root of the webapp.
	 *
	 * This function might call itself recursively if it couldn't find any files for
	 * the given $load type. If no 'source' files are found, it will obtain the 'debug'
	 * files, if that too files it will fallback to 'release' files. If the latter is
	 * not found either, no files are returned.
	 *
	 * @param String $pluginname The name of the plugin (this is used in the pathname)
	 * @param Array $component The component to read the serverfiles from
	 * @param number $load One of LOAD_RELEASE, LOAD_DEBUG, LOAD_SOURCE. This will filter
	 * the files based on the 'load' attribute. 
	 * @return array list of paths to the files in this component
	 */
	function getServerFilesForComponent($pluginname, $component, $load)
	{
		$componentfiles = Array(
			'server' => Array(),
			'modules' => Array(),
			'notifiers' => Array()
		);

		foreach ($component['serverfiles'][$load] as &$file) {
			// FIXME: With WA-3351 config files should be handled differently, and this
			// if-statement must be updated.
			switch ($file['type']) {
				case TYPE_CONFIG:
					$componentfiles['server'][] = PATH_PLUGIN_CONFIG_DIR . '/' . $pluginname . '/' . $file['file'];
					break;
				case TYPE_PLUGIN:
					$componentfiles['server'][] = PATH_PLUGIN_DIR . '/' . $pluginname . '/' . $file['file'];
					break;
				case TYPE_MODULE:
					$componentfiles['modules'][ $file['module'] ] = PATH_PLUGIN_DIR.'/'.$pluginname.'/'.$file['file'];
					break;
				case TYPE_NOTIFIER:
					$componentfiles['notifiers'][ $file['notifier'] ] = PATH_PLUGIN_DIR.'/'.$pluginname.'/'.$file['file'];
					break;
			}

		}
		unset($file);

		return $componentfiles;
	}

	/**
	 * getServerFiles
	 * 
	 * Returning an array of paths to files that need to be included.
	 * The paths which are returned start at the root of the webapp.
	 *
	 * This calls getServerFilesForComponent() to obtain the files
	 * for each component inside the requested plugin
	 *
	 * @param string $pluginname The name of the plugin for which the server files are requested.
	 * @param number $load One of LOAD_RELEASE, LOAD_DEBUG, LOAD_SOURCE. This will filter
	 * the files based on the 'load' attribute. 
	 * @return array List of paths to files.
	 */
	function getServerFiles($load = LOAD_RELEASE)
	{
		$files = Array(
			'server' => Array(),
			'modules' => Array(),
			'notifiers' => Array()
		);

		foreach($this->plugindata as $pluginname => &$plugin){
			foreach($plugin['components'] as &$component) {
				if (!empty($component['serverfiles'][$load])) {
					$componentfiles = $this->getServerFilesForComponent($pluginname, $component, $load);
				} else if ($load === LOAD_SOURCE && !empty($component['serverfiles'][LOAD_DEBUG])) {
					$componentfiles = $this->getServerFilesForComponent($pluginname, $component, LOAD_DEBUG);
				} else if ($load !== LOAD_RELEASE && !empty($component['serverfiles'][LOAD_RELEASE])) {
					$componentfiles = $this->getServerFilesForComponent($pluginname, $component, LOAD_RELEASE);
				} // else tough luck, at least release should be present

				if (isset($componentfiles)) {
					$files['server'] = array_merge($files['server'], $componentfiles['server']);
					$files['modules'] = array_merge($files['modules'], $componentfiles['modules']);
					$files['notifiers'] = array_merge($files['notifiers'], $componentfiles['notifiers']);
					unset($componentfiles);
				}
			}
			unset($component);
		}
		unset($plugin);

		return $files;
	}

	/**
	 * getClientFilesForComponent
	 *
	 * Called by getClientFiles() to return the list of files which are provided
	 * for the given component in a particular plugin.
	 * The paths which are returned start at the root of the webapp.
	 *
	 * This function might call itself recursively if it couldn't find any files for
	 * the given $load type. If no 'source' files are found, it will obtain the 'debug'
	 * files, if that too files it will fallback to 'release' files. If the latter is
	 * not found either, no files are returned.
	 *
	 * @param String $pluginname The name of the plugin (this is used in the pathname)
	 * @param Array $component The component to read the clientfiles from
	 * @param number $load One of LOAD_RELEASE, LOAD_DEBUG, LOAD_SOURCE. This will filter
	 * the files based on the 'load' attribute. 
	 * @return array list of paths to the files in this component
	 */
	function getClientFilesForComponent($pluginname, $component, $load)
	{
		$componentfiles = Array();

		foreach ($component['clientfiles'][$load] as &$file) {
			$componentfiles[] = PATH_PLUGIN_DIR.'/'.$pluginname.'/'.$file['file'];
		}
		unset($file);

		return $componentfiles;
	}

	/**
	 * getClientFiles
	 * 
	 * Returning an array of paths to files that need to be included.
	 * The paths which are returned start at the root of the webapp.
	 *
	 * This calls getClientFilesForComponent() to obtain the files
	 * for each component inside each plugin.
	 *
	 * @param number $load One of LOAD_RELEASE, LOAD_DEBUG, LOAD_SOURCE. This will filter
	 * the files based on the 'load' attribute. 
	 * @return array List of paths to files.
	 */
	function getClientFiles($load = LOAD_RELEASE){
		$files = Array();

		foreach($this->plugindata as $pluginname => &$plugin){
			foreach($plugin['components'] as &$component) {
				if (!empty($component['clientfiles'][$load])) {
					$componentfiles = $this->getClientFilesForComponent($pluginname, $component, $load);
				} else if ($load === LOAD_SOURCE && !empty($component['clientfiles'][LOAD_DEBUG])) {
					$componentfiles = $this->getClientFilesForComponent($pluginname, $component, LOAD_DEBUG);
				} else if ($load !== LOAD_RELEASE && !empty($component['clientfiles'][LOAD_RELEASE])) {
					$componentfiles = $this->getClientFilesForComponent($pluginname, $component, LOAD_RELEASE);
				} // else tough luck, at least release should be present

				if (isset($componentfiles)) {
					$files = array_merge($files, $componentfiles);
					unset($componentfiles);
				}
			}
			unset($component);
		}
		unset($plugin);

		return $files;
	}

	/**
	 * getResourceFilesForComponent
	 *
	 * Called by getResourceFiles() to return the list of files which are provided
	 * for the given component in a particular plugin.
	 * The paths which are returned start at the root of the webapp.
	 *
	 * This function might call itself recursively if it couldn't find any files for
	 * the given $load type. If no 'source' files are found, it will obtain the 'debug'
	 * files, if that too files it will fallback to 'release' files. If the latter is
	 * not found either, no files are returned.
	 *
	 * @param String $pluginname The name of the plugin (this is used in the pathname)
	 * @param Array $component The component to read the resourcefiles from
	 * @param number $load One of LOAD_RELEASE, LOAD_DEBUG, LOAD_SOURCE. This will filter
	 * the files based on the 'load' attribute. 
	 * @return array list of paths to the files in this component
	 */
	function getResourceFilesForComponent($pluginname, $component, $load)
	{
		$componentfiles = Array();

		foreach ($component['resourcefiles'][$load] as &$file) {
			$componentfiles[] = PATH_PLUGIN_DIR.'/'.$pluginname.'/'.$file['file'];
		}
		unset($file);

		return $componentfiles;
	}

	/**
	 * getResourceFiles
	 * 
	 * Returning an array of paths to files that need to be included.
	 * The paths which are returned start at the root of the webapp.
	 *
	 * This calls getResourceFilesForComponent() to obtain the files
	 * for each component inside each plugin.
	 *
	 * @param number $load One of LOAD_RELEASE, LOAD_DEBUG, LOAD_SOURCE. This will filter
	 * the files based on the 'load' attribute. 
	 * @return array List of paths to files.
	 */
	function getResourceFiles($load = LOAD_RELEASE) {
		$files = Array();

		foreach($this->plugindata as $pluginname => &$plugin){
			foreach($plugin['components'] as &$component) {
				if (!empty($component['resourcefiles'][$load])) {
					$componentfiles = $this->getResourceFilesForComponent($pluginname, $component, $load);
				} else if ($load === LOAD_SOURCE && !empty($component['resourcefiles'][LOAD_DEBUG])) {
					$componentfiles = $this->getResourceFilesForComponent($pluginname, $component, LOAD_DEBUG);
				} else if ($load !== LOAD_RELEASE && !empty($component['resourcefiles'][LOAD_RELEASE])) {
					$componentfiles = $this->getResourceFilesForComponent($pluginname, $component, LOAD_RELEASE);
				} // else tough luck, at least release should be present

				if (isset($componentfiles)) {
					$files = array_merge($files, $componentfiles);
					unset($componentfiles);
				}
			}
			unset($component);
		}
		unset($plugin);

		return $files;
	}

	/**
	 * getTranslationFilePaths
	 * 
	 * Returning an array of paths to to the translations files. This will be 
	 * used by the gettext functionality.
	 * 
	 * @return array List of paths to translations.
	 */
	function getTranslationFilePaths(){
		$paths = Array();

		foreach($this->plugindata as $pluginname => &$plugin){
			if ($plugin['translationsdir']) {
				$translationPath =  PATH_PLUGIN_DIR.'/'.$pluginname.'/'.$plugin['translationsdir']['dir'];
				if(is_dir($translationPath)){
					$paths[$pluginname] = $translationPath;
				}
			}
		}
		unset($plugin);

		return $paths;
	}

	/**
	 * extractPluginDataFromXML
	 * 
	 * Extracts all the data from the Plugin XML manifest.
	 * 
	 * @param $xml string XML manifest of plugin
	 * @return array Data from XML converted into array that the PluginManager can use.
	 */
	function extractPluginDataFromXML($xml)
	{
		$this->xmlParser = new XMLParser(array('configfile', 'component', 'serverfile', 'clientfile', 'resourcefile'));

		$plugindata = Array(
			'components' => Array(),
			'translationsdir' => null
		);

		// Parse all XML data
		$data = $this->xmlParser->getData($xml);

		// Parse the <plugin> attributes
		if (isset($data['attributes'])) {
			if (isset($data['attributes']['version']) && intval($data['attributes']['version']) !== 2) {
				if (DEBUG_PLUGINS) {
					dump('[PLUGIN ERROR] Plugin manifest uses version ' . $data['attributes']['version'] . ' while only version 2 is supported');
				}
				return false;
			}
		}


		// Parse the <config> element
		if (isset($data['config']) && isset($data['config']['configfile'])) {
			// FIXME: For now we write the config info into the serverfiles array, this will change with WA-3351
			$newfiles = $this->getConfigFileInfoFromXML($data['config']['configfile']);
			$plugindata['components'][] = Array(
				'serverfiles' => $newfiles,
				'clientfiles' => Array(),
				'resourcefiles' => Array(),
			);
		}

		// Parse the <translations> element
		if (isset($data['translations']) && isset($data['translations']['translationsdir'])) {
			$translations = $this->getTranslationsDirInfoFromXML($data['translations']['translationsdir']);
			$plugindata['translationsdir'] = $translations;
		}

		// Parse the <components> element
		if (isset($data['components']) && isset($data['components']['component'])) {
			$components = $data['components']['component'];

			foreach ($components as $index => &$component) {
				$componentdata = array(
					'serverfiles' => Array(
						LOAD_SOURCE => Array(),
						LOAD_DEBUG => Array(),
						LOAD_RELEASE => Array()
					),
					'clientfiles' => Array(
						LOAD_SOURCE => Array(),
						LOAD_DEBUG => Array(),
						LOAD_RELEASE => Array()
					),
					'resourcefiles' => Array(
						LOAD_SOURCE => Array(),
						LOAD_DEBUG => Array(),
						LOAD_RELEASE => Array()
					),
				);

				if (isset($component['files'])) {
					if (isset($component['files']['server']) && isset($component['files']['server']['serverfile'])) {
						$newfiles = $this->getServerFileInfoFromXML($component['files']['server']['serverfile']);
						$componentdata['serverfiles'][LOAD_SOURCE] = array_merge($componentdata['serverfiles'][LOAD_SOURCE], $newfiles[LOAD_SOURCE]);
						$componentdata['serverfiles'][LOAD_DEBUG] = array_merge($componentdata['serverfiles'][LOAD_DEBUG], $newfiles[LOAD_DEBUG]);
						$componentdata['serverfiles'][LOAD_RELEASE] = array_merge($componentdata['serverfiles'][LOAD_RELEASE], $newfiles[LOAD_RELEASE]);
					}
					if (isset($component['files']['client']) && isset($component['files']['client']['clientfile'])) {
						$newfiles = $this->getClientFileInfoFromXML($component['files']['client']['clientfile']);
						$componentdata['clientfiles'][LOAD_SOURCE] = array_merge($componentdata['clientfiles'][LOAD_SOURCE], $newfiles[LOAD_SOURCE]);
						$componentdata['clientfiles'][LOAD_DEBUG] = array_merge($componentdata['clientfiles'][LOAD_DEBUG], $newfiles[LOAD_DEBUG]);
						$componentdata['clientfiles'][LOAD_RELEASE] = array_merge($componentdata['clientfiles'][LOAD_RELEASE], $newfiles[LOAD_RELEASE]);
					}
					if (isset($component['files']['resources']) && isset($component['files']['resources']['resourcefile'])) {
						$newfiles = $this->getResourceFileInfoFromXML($component['files']['resources']['resourcefile']);
						$componentdata['resourcefiles'][LOAD_SOURCE] = array_merge($componentdata['resourcefiles'][LOAD_SOURCE], $newfiles[LOAD_SOURCE]);
						$componentdata['resourcefiles'][LOAD_DEBUG] = array_merge($componentdata['resourcefiles'][LOAD_DEBUG], $newfiles[LOAD_DEBUG]);
						$componentdata['resourcefiles'][LOAD_RELEASE] = array_merge($componentdata['resourcefiles'][LOAD_RELEASE], $newfiles[LOAD_RELEASE]);
					}
				}

				$plugindata['components'][] = $componentdata;
			}
			unset($component);
		} else {
			if (DEBUG_PLUGINS) {
				dump('[PLUGIN ERROR] Plugin manifest didn\t provide any components');
			}
			return false;
		}

		return $plugindata;
	}

	/**
	 * getConfigFileInfoFromXML
	 * 
	 * Transform the config files info from a manifest XML to an usable array.
	 * 
	 * @param $fileData array Piece of manifest XML parsed into array.
	 * @return array List of config files.
	 */
	function getConfigFileInfoFromXML($fileData) {
		$files = Array(
			LOAD_SOURCE => Array(),
			LOAD_DEBUG => Array(),
			LOAD_RELEASE => Array()
		);

		for ($i = 0, $len = count($fileData); $i < $len; $i++) {
			$filename = false;

			if (is_string($fileData[$i])) {
				$filename = $fileData[$i];
			} elseif(isset($fileData[$i]['_content'])) {
				$filename = $fileData[$i]['_content'];
			} else {
				if (DEBUG_PLUGINS) {
					dump('[PLUGIN ERROR] Plugin manifest contains empty configfile declaration');
				}
			}

			if ($filename) {
				$files[LOAD_RELEASE][] = Array(
					'file' => $filename,
					// FIXME: Remove in WA-3351
					'type' => TYPE_CONFIG,
					'load' => LOAD_RELEASE,
					'module' => null,
				);
			}
		}

		return $files;
	}

	/**
	 * getServerFileInfoFromXML
	 * 
	 * Transform the <serverfile> element array from the manifest into a useable array.
	 * Each <serverfile> supports the 'load' attribute which needs to be saved into
	 * the array as well. When this attribute is not provided, the value 'release'
	 * is assumed.
	 * Additionally the attributes 'type' and 'module' are available. The 'type' attribute
	 * indicates if this a plugin file or a module file. A plugin file will be loaded into
	 * the PHP at all times, while the module file will only be loaded on demand. The 'module'
	 * attribute will only be useful when 'type' is module. If not provided, the 'type' attribute
	 * will be set to plugin and the 'module' attribute will remain empty.
	 * 
	 * @param $fileData array Piece of manifest XML which represents all <serverfile> nodes
	 * @return array List of server files.
	 */
	function getServerFileInfoFromXML($fileData) {
		$files = Array(
			LOAD_SOURCE => Array(),
			LOAD_DEBUG => Array(),
			LOAD_RELEASE => Array()
		);

		for ($i = 0, $len = count($fileData); $i < $len; $i++) {
			$filename = false;
			$load = LOAD_RELEASE;	// release | debug | source
			$type = TYPE_PLUGIN;	// plugin | module | notifier
			$module = null;

			if (is_string($fileData[$i])) {
				$filename = $fileData[$i];
			} elseif(isset($fileData[$i]['_content'])) {
				$filename = $fileData[$i]['_content'];
				if (isset($fileData[$i]['attributes'])) {
					if (isset($fileData[$i]['attributes']['load'])) {
						$load = $fileData[$i]['attributes']['load'];
						$load = $this->loadMap[$load];
					}
					if (isset($fileData[$i]['attributes']['type'])) {
						$type = $fileData[$i]['attributes']['type'];
						$type = $this->typeMap[$type];
					}
					if (isset($fileData[$i]['attributes']['module'])) {
						$module = $fileData[$i]['attributes']['module'];
					}
				}
			} else {
				if (DEBUG_PLUGINS) {
					dump('[PLUGIN ERROR] Plugin manifest contains empty serverfile declaration');
				}
			}

			if ($filename) {
				$files[$load][] = Array(
					'file' => $filename,
					'type' => $type,
					'load' => $load,
					'module' => $module,
				);
			}
		}
 
		return $files;
	}

	/**
	 * getClientFileInfoFromXML
	 * 
	 * Transform the <clientfile> element array from the manifest into a useable array.
	 * Each <clientfile> supports the 'load' attribute which needs to be saved into
	 * the array as well. When this attribute is not provided, the value 'release'
	 * is assumed. 
	 * 
	 * @param $fileData array Piece of manifest XML which represents all <clientfile> nodes
	 * @return array List of client files.
	 */
	function getClientFileInfoFromXML($fileData){
		$files = Array(
			LOAD_SOURCE => Array(),
			LOAD_DEBUG => Array(),
			LOAD_RELEASE => Array()
		);

		for ($i = 0, $len = count($fileData); $i < $len; $i++) {
			$filename = false;
			$load = LOAD_RELEASE;   // release | debug | source

			if (is_string($fileData[$i])) {
				$filename = $fileData[$i];
			} elseif(isset($fileData[$i]['_content'])) {
				$filename = $fileData[$i]['_content'];
				if (isset($fileData[$i]['attributes'])) {
					if(isset($fileData[$i]['attributes']['load'])){
						$load = $fileData[$i]['attributes']['load'];
						$load = $this->loadMap[$load];
					}
				}
			} else {
				if (DEBUG_PLUGINS) {
					dump('[PLUGIN ERROR] Plugin manifest contains empty clientfile declaration');
				}
			}

			$files[$load][] = Array(
				'file' => $filename,
				'load' => $load,
			);
		}

		return $files;
	}

	/**
	 * getResourceFileInfoFromXML
	 * 
	 * Transform the <resourcefile> element array from the manifest into a useable array.
	 * Each <resourcefile> supports the 'load' attribute which needs to be saved into
	 * the array as well. When this attribute is not provided, the value 'release'
	 * is assumed. 
	 *
	 * @param $fileData array Piece of manifest XML which represents all <resourcefile> nodes
	 * @return array List of resource files.
	 */
	function getResourceFileInfoFromXML($fileData) {
		$files = Array(
			LOAD_SOURCE => Array(),
			LOAD_DEBUG => Array(),
			LOAD_RELEASE => Array()
		);

		for ($i = 0, $len = count($fileData); $i < $len; $i++) {
			$filename = false;
			$load = LOAD_RELEASE;   // release | debug | source

			if (is_string($fileData[$i])) {
				$filename = $fileData[$i];
			} elseif(isset($fileData[$i]['_content'])) {
				$filename = $fileData[$i]['_content'];
				if (isset($fileData[$i]['attributes'])) {
					if(isset($fileData[$i]['attributes']['load'])){
						$load = $fileData[$i]['attributes']['load'];
						$load = $this->loadMap[$load];
					}
				}
			} else {
				if (DEBUG_PLUGINS) {
					dump('[PLUGIN ERROR] Plugin manifest contains empty clientfile declaration');
				}
			}

			$files[$load][] = Array(
				'file' => $filename,
				'load' => $load,
			);
		}

		return $files;
	}

	/**
	 * getTranslationsDirInfoFromXML
	 * 
	 * Transform the <translationsdir> element array from the manifest into a usable array
	 * The <translationsdir> doesn't support any attributes.
	 *
	 * @param $dirData array Piece of manifest XML which represents all <translationsdir> nodes
	 * @return string Path to translations dir.
	 */
	function getTranslationsDirInfoFromXML($dirData){
		if (is_string($dirData)) {
			$dirname = $dirData;
		} elseif(isset($dirData['_content'])) {
			$dirname = $dirData['_content'];
		} else {
			if (DEBUG_PLUGINS) {
				dump('[PLUGIN ERROR] Plugin manifest contains empty translationsdir declaration');
			}

			return false;
		}

		return Array(
			'dir' => $dirname
		);
	}
}
?>
