<?php
/**
* @version	$Id: event_manager.php 15033 2012-01-10 14:39:36Z alex $
* @package	In-Portal
* @copyright	Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
* @license      GNU/GPL
* In-Portal is Open Source software.
* This means that this software may have been modified pursuant
* the GNU General Public License, and as distributed it includes
* or is derivative of works licensed under the GNU General Public License
* or other free or open source software licenses.
* See http://www.in-portal.org/license for copyright notices and details.
*/

defined('FULL_PATH') or die('restricted access!');

class kEventManager extends kBase implements kiCacheable {

	/**
	 * Instance of hook manager
	 *
	 * @var kHookManager
	 * @access protected
	 */
	protected $Hooks = null;

	/**
	 * Instance of scheduled task manager
	 *
	 * @var kScheduledTaskManager
	 * @access protected
	 */
	protected $ScheduledTasks = null;

	/**
	 * Instance of request manager
	 *
	 * @var kRequestManager
	 * @access protected
	 */
	protected $Request = null;

	/**
	 * Build events registred for pseudo classes.
	 * key - pseudo class, value - event name
	 *
	 * @var Array
	 * @access protected
	 */
	protected $buildEvents = Array ();

	/**
	 * Event recursion tracking stack
	 *
	 * @var Array
	 * @access protected
	 */
	protected $recursionStack = Array ();

	/**
	 * Creates new instance of kEventManager class
	 *
	 */
	public function __construct()
	{
		parent::__construct();

		$this->Hooks =& $this->Application->makeClass('kHookManager');
		$this->ScheduledTasks =& $this->Application->makeClass('kScheduledTaskManager');
		$this->Request =& $this->Application->makeClass('kRequestManager');
	}

	/**
	 * Sets data from cache to object
	 *
	 * @param Array $data
	 * @access public
	 */
	public function setFromCache(&$data)
	{
		$this->Hooks->setFromCache($data);
		$this->ScheduledTasks->setFromCache($data);

		$this->buildEvents = $data['EventManager.buildEvents'];
	}

	/**
	 * Gets object data for caching
	 *
	 * @return Array
	 * @access public
	 */
	public function getToCache()
	{
		return array_merge(
			$this->Hooks->getToCache(),
			$this->ScheduledTasks->getToCache(),
			Array (
				'EventManager.buildEvents' => $this->buildEvents,
			)
		);
	}

	/**
	 * Returns information about registered scheduled tasks
	 *
	 * @param bool $from_cache
	 * @return Array
	 * @access public
	 */
	public function getScheduledTasks($from_cache = false)
	{
		return $this->ScheduledTasks->getAll($from_cache);
	}

	/**
	 * Add new scheduled task
	 *
	 * @param string $short_name name to be used to store last maintenance run info
	 * @param string $event_string
	 * @param int $run_interval run interval in seconds
	 * @param int $status
	 * @access public
	 */
	public function registerScheduledTask($short_name, $event_string, $run_interval, $status = STATUS_ACTIVE)
	{
		$this->ScheduledTasks->add($short_name, $event_string, $run_interval, $status);
	}

	/**
	 * Run registered scheduled tasks with specified event type
	 *
	 * @param bool $from_cron
	 * @access public
	 */
	public function runScheduledTasks($from_cron = false)
	{
		$this->ScheduledTasks->runAll($from_cron);
	}

	/**
	 * Runs scheduled task based on given data
	 *
	 * @param Array $scheduled_task_data
	 * @return bool
	 * @access public
	 */
	public function runScheduledTask($scheduled_task_data)
	{
		return $this->ScheduledTasks->run($scheduled_task_data);
	}

	/**
	 * Registers Hook from sub-prefix event to master prefix event
	 *
	 * Pattern: Observer
	 *
	 * @param string $hook_event
	 * @param string $do_event
	 * @param int $mode
	 * @param bool $conditional
	 * @access public
	 */
	public function registerHook($hook_event, $do_event, $mode = hAFTER, $conditional = false)
	{
		$this->Hooks->registerHook($hook_event, $do_event, $mode, $conditional);
	}

	/**
	 * Registers build event for given pseudo class
	 *
	 * @param string $pseudo_class
	 * @param string $event_name
	 * @access public
	 */
	public function registerBuildEvent($pseudo_class, $event_name)
	{
		$this->buildEvents[$pseudo_class] = $event_name;
	}

	/**
	 * Runs build event for given $pseudo_class instance, when defined
	 *
	 * @param string $prefix_special
	 * @param string $pseudo_class
	 * @param Array $event_params
	 * @access public
	 */
	public function runBuildEvent($prefix_special, $pseudo_class, $event_params)
	{
		if ( !isset($this->buildEvents[$pseudo_class]) ) {
			return ;
		}

		$event = new kEvent($prefix_special . ':' . $this->buildEvents[$pseudo_class], $event_params);
		$this->HandleEvent($event);
	}

	/**
	 * Check if event is called twice, that causes recursion
	 *
	 * @param kEvent $event
	 * @return bool
	 * @access protected
	 */
	protected function isRecursion(&$event)
	{
		$event_key = $event->getPrefixSpecial() . ':' . $event->Name;

		return in_array($event_key, $this->recursionStack);
	}

	/**
	 * Adds event to recursion stack
	 *
	 * @param kEvent $event
	 * @access protected
	 */
	protected function pushEvent(&$event)
	{
		$event_key = $event->getPrefixSpecial() . ':' . $event->Name;

		array_push($this->recursionStack, $event_key);
	}

	/**
	 * Removes event from recursion stack
	 *
	 * @access protected
	 */
	protected function popEvent()
	{
		array_pop($this->recursionStack);
	}

	/**
	 * Allows to process any type of event
	 *
	 * @param kEvent $event
	 * @access public
	 */
	public function HandleEvent(&$event)
	{
		if ( $this->isRecursion($event) || !$this->verifyEventPrefix($event) ) {
			return;
		}

		$this->pushEvent($event);

		if ( !$event->SkipBeforeHooks ) {
			$this->Hooks->runHooks($event, hBEFORE);

			if ( $event->status == kEvent::erFATAL ) {
				return;
			}
		}

		$event_handler =& $this->Application->recallObject($event->Prefix . '_EventHandler');
		/* @var $event_handler kEventHandler */

		$event_handler->processEvent($event);

		if ( $event->status == kEvent::erFATAL ) {
			return;
		}

		if ( !$event->SkipAfterHooks ) {
			$this->Hooks->runHooks($event, hAFTER);
		}

		$this->popEvent();
	}

	/**
	 * Checks if event prefix is valid
	 *
	 * @param kEvent $event
	 * @param bool $is_fatal
	 * @return string
	 * @access public
	 */
	public function verifyEventPrefix(&$event, $is_fatal = false)
	{
		if ( !$this->Application->prefixRegistred($event->Prefix) ) {
			$this->Application->UnitConfigReader->loadConfig($event->Prefix);

			if ( !$this->Application->prefixRegistred($event->Prefix) ) {
				$error_msg = 'Prefix <strong>' . $event->Prefix . '</strong> not registred (requested event <strong>' . $event->Name . '</strong>)';

				if ($is_fatal) {
					throw new Exception($error_msg);
				}
				else {
					trigger_error($error_msg, E_USER_WARNING);
				}

				return false;
			}
		}

		return true;
	}

	/**
	 * Processes request
	 *
	 * @access public
	 */
	public function ProcessRequest()
	{
		$this->Request->process();
	}

	/**
	 * Allows to add new element to opener stack
	 *
	 * @param string $template
	 * @param Array $params
	 * @access public
	 */
	public function openerStackPush($template = '', $params = Array ())
	{
		$this->Request->openerStackPush($template, $params);
	}

	/**
	 * Set's new event for $prefix_special
	 * passed
	 *
	 * @param string $prefix_special
	 * @param string $event_name
	 * @access public
	 */
	public function setEvent($prefix_special,$event_name)
	{
		$actions =& $this->Application->recallObject('kActions');
		/* @var $actions Params */

		$actions->Set('events[' . $prefix_special . ']', $event_name);
	}

	/**
	 * Allows to determine, that required event is beeing processed right now
	 *
	 * @param string $event_key Event name in format prefix[.special]:event_name
	 * @return bool
	 * @access public
	 */
	public function eventRunning($event_key)
	{
		return array_search($event_key, $this->recursionStack) !== false;
	}
}