<?php
/*-------------------------------------------------------
*
*   LiveStreet Engine Social Networking
*   Copyright © 2008 Mzhelskiy Maxim
*
*--------------------------------------------------------
*
*   Official site: www.livestreet.ru
*   Contact e-mail: rus.engine@gmail.com
*
*   GNU General Public License, version 2:
*   http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*
---------------------------------------------------------
*/
/**
 * Модуль поддержки хуков(hooks)
 *
 */
class ModuleHook extends Module {		
		
	/**
	 * Содержит список хуков
	 *
	 * @var array( 'name' => array(
	 * 		array(
	 * 			'type' => 'module' | 'hook' | 'function',
	 * 			'callback' => 'callback_name',
	 * 			'priority'	=> 1,
	 * 			'params' => array()
	 * 		),
	 * 	),
	 * )
	 */
	protected $aHooks = array();
	protected $aPregNames = array();
	protected $aHookPool = array();
	
	/**
	 * Инициализация модуля
	 *
	 */
	public function Init() {	
		
	}
	
	public function Add($sName,$sType,$sCallBack,$iPriority=1,$aParams=array()) {
		$bPreg = preg_match('#^([^\w\d]).*\1[A-Za-z]*$#', $sName);
		$sName = $bPreg
			? $sName
			: strtolower($sName)
		;
		$sType = strtolower($sType);
		if (!in_array($sType,array('module','hook','function'))) {
			return false;
		}
		$this->aHooks[$sName][]=array('type'=>$sType,'callback'=>$sCallBack,'params'=>$aParams,'priority'=>(int)$iPriority);
		if($bPreg && !in_array($sName, $this->aPregNames)){
			$this->aPregNames[] = $sName;
		}
		return true;
	}
	
	public function AddExecModule($sName,$sCallBack,$iPriority=1) {
		return $this->Add($sName,'module',$sCallBack,$iPriority);
	}
	
	public function AddExecFunction($sName,$sCallBack,$iPriority=1) {
		return $this->Add($sName,'function',$sCallBack,$iPriority);
	}
	
	public function AddExecHook($sName,$sCallBack,$iPriority=1,$aParams=array()) {
		return $this->Add($sName,'hook',$sCallBack,$iPriority,$aParams);
	}
	
	public function AddDelegateModule($sName,$sCallBack,$iPriority=1) {
		return $this->Add($sName,'module',$sCallBack,$iPriority,array('delegate'=>true));
	}
	
	public function AddDelegateFunction($sName,$sCallBack,$iPriority=1) {
		return $this->Add($sName,'function',$sCallBack,$iPriority,array('delegate'=>true));
	}
	
	public function AddDelegateHook($sName,$sCallBack,$iPriority=1,$aParams=array()) {
		$aParams['delegate']=true;
		return $this->Add($sName,'hook',$sCallBack,$iPriority,$aParams);
	}
	
	public function Run($sName,&$aVars=array()) {
		$result=array();
		$sName=strtolower($sName);
		dump(__METHOD__.": $sName");
		$bTemplateHook=strpos($sName,'template_')===0 ? true : false;
		
		$aSelectedHooks = array();
		if (isset($this->aHooks[$sName])) {
			$aSelectedHooks = $this->aHooks[$sName];
		}
		foreach($this->aPregNames as $sPregName){
			if($sName != $sPregName && preg_match($sPregName, $sName)){
				$aSelectedHooks = array_merge($aSelectedHooks, $this->aHooks[$sPregName]);
			}
		}
		
		if ($aSelectedHooks) {
			$aVars['__hook_name'] = $sName;
			$aHookNum=array();
			$aHookNumDelegate=array();
			/**
			 * Все хуки делим на обычные(exec) и делигирующие(delegate)
			 */
			for ($i=0;$i<count($aSelectedHooks);$i++) {				
				if (isset($aSelectedHooks[$i]['params']['delegate']) and $aSelectedHooks[$i]['params']['delegate']) {
					$aHookNumDelegate[$i]=$aSelectedHooks[$i]['priority'];
				} else {
					$aHookNum[$i]=$aSelectedHooks[$i]['priority'];
				}				
			}			
			arsort($aHookNum,SORT_NUMERIC);
			arsort($aHookNumDelegate,SORT_NUMERIC);
			/**
			 * Сначала запускаем на выполнение простые
			 */
			foreach ($aHookNum as $iKey => $iPr) {
				$aHook=$aSelectedHooks[$iKey];
				if ($bTemplateHook) {
					/**
					 * Если это шаблонных хук то сохраняем результат 
					 */
					$result['template_result'][]=$this->RunType($aHook,$aVars);
				} else {
					$this->RunType($aHook,$aVars);
				}
			}
			/**
			 * Теперь запускаем делигирующие
			 * Делегирующий хук должен вернуть результат в формате:
			 * 
			 */
			foreach ($aHookNumDelegate as $iKey => $iPr) {
				$aHook=$aSelectedHooks[$iKey];				
				$result=array(
					'delegate_result'=>$this->RunType($aHook,$aVars)
				);
				/**
				 * На данный момент только один хук может быть делегирующим
				 */
				break;
			}
		}
		return $result;
	}
	
	protected function RunType($aHook,&$aVars) {
		$result=null;
		$sHookName = isset($aVars['__hook_name']) ? $aVars['__hook_name'] : null;
		unset($aVars['__hook_name']);
		switch ($aHook['type']) {
			case 'module':
				$result=call_user_func_array(array($this,$aHook['callback']),array(&$aVars));
				break;
			case 'function':
				$result=call_user_func_array($aHook['callback'],array(&$aVars));
				break;
			case 'hook':
				if (isset($aHook['params']['sClassName']) and class_exists($aHook['params']['sClassName'])) {
					$sHookClass = $aHook['params']['sClassName'];
					$oHook = isset($this->aHookPool[$sHookClass])
						? $this->aHookPool[$sHookClass]
						: ($this->aHookPool[$sHookClass] = new $sHookClass)
					;
					$oHook->sHookName = $sHookName;
					$result=call_user_func_array(array($oHook,$aHook['callback']),array(&$aVars));
				}
				break;
			default:
				break;
		}
		return $result;
	}
}
?>
