<?php
/**
* 単純なタスクマネージャを実装するクラス
*
* PHP version 5
*
* @package    task_managers
* @author     stk2k <stk2k@sazysoft.com>
* @copyright  2008 stk2k, sazysoft
*/

class DefaultTaskManager extends CharcoalComponent implements ITaskManager
{
	private $_tasks;
	private $_queue;

	/*
	 * コンストラクタ
	 */
	public function __construct()
	{
		parent::__construct();

		$this->_tasks  = new Vector();
		$this->_queue  = new EventQueue();
	}

	/*
	 * タスクを登録する
	 */
	public function registerTask( String $task_name )
	{
		// 作成済みなら何もしない
		if ( isset($this->_tasks["$task_name"]) ){
			return;
		}

		// タスクを作成
		$task = Factory::createTask( $task_name );

		// タスクを保存
		$this->_tasks["$task_name"] = $task;
	}

	/*
	 * タスクを登録を解除する
	 */
	private function _unregisterTask( String $task_name )
	{
		// タスクを保存
		unset( $this->_tasks["$task_name"] );
	}

	/*
	 * タスクが登録されているか
	 */
	public function isTaskRegistered( String $task_name )
	{
		return isset($this->_tasks["$task_name"]);
	}

	/*
	 * タスクを取得する
	 */
	public function getTask( String $task_name )
	{
		if ( isset($this->_tasks["$task_name"]) ){
			return $this->_tasks["$task_name"];
		}

		throw new TaskNotRegisteredException( $task_name );
	}

	/*
	 * スナップショットを取得する
	 */
	public function getSnapshot()
	{
		$snapshot = new TaskSnapshot();

		foreach( $this->_tasks as $task ){
			$snapshot->set( $task );
		}

		return $snapshot;
	}

	/**
	 *   ステートフルタスクの保存を行う
	 *
	 */
	public function saveStatefulTasks( Session $session )
	{
		foreach( $this->_tasks as $task ){
			if ( $task instanceof IStateful ){
				$data_id = "task://" . $task->getName();
				$session->set( s($data_id), $task->serializeContents() );
			}
		}
	}

	/**
	 *   ステートフルタスクの復帰を行う
	 *
	 */
	public function restoreStatefulTasks( Session $session )
	{
		foreach( $this->_tasks as $task ){
			if ( $task instanceof IStateful ){
				$data_id = "task://" . $task->getName();
				if ( isset($_SESSION[ $data_id ]) ){
					$data = $_SESSION[ $data_id ];
					$task->deserializeContents( unserialize($data) );
				}
			}
		}
	}

	/*
	 * イベントディスパッチを行う
	 *
	 */
	public function dispatchEvents( IRequest $request )
	{
		log_info( "system",  "イベントディスパッチを開始します。", __FILE__, __LINE__ );

		$queue = $this->_queue;

		// ログ
		log_info( 'event', "starting to dispatch events.", __FILE__, __LINE__ );

		// リクエストイベントの作成
		$event = Factory::createRequestEvent( s('http_request'), $request );

		// イベントをイベントキューに追加
		$queue->enqueue( $event );

		// ログ
		log_info( 'event', "request event[$event] wad dispatched.", __FILE__, __LINE__ );

		log_info( "system",  "イベントディスパッチを終了しました。", __FILE__, __LINE__ );
	}

	/**
	 *   イベント処理を行う
	 *
	 */
	public function processEvents()
	{
//		try{
			$queue = $this->_queue;

			// タスク実行リスト
			$task_exec_list = clone $this->_tasks;

			// 最大イベント処理回数
			$max_event_loop = Profile::getInteger( s('TM_MAX_EVENT_LOOP'), 100 )->getValue();

			// 最大イベント処理時間
			$max_event_time = Profile::getInteger( s('TM_MAX_EVENT_TIME'), 10 )->getValue();

			$start = Benchmark::nowTime();

			$event_count = 0;
			while( !$queue->isEmpty() ){
				// ログ
				log_info( 'event', "loop count: $event_count", __FILE__, __LINE__ );
				log_info( 'event', "event queue: [ " . implode( ',', $queue->toArray() ) . " ]", __FILE__, __LINE__ );

				// イベントを取得
				$event = $queue->dequeue();
				$event_name = $event->getName();

				// ログ
				log_info( 'event', "starting process an event[$event].", __FILE__, __LINE__ );

				// すべてのタスクにイベントをディスパッチする
				$next_task_exec_list = clone $task_exec_list;
				log_info( 'event', "task execution list $task_exec_list.", __FILE__, __LINE__ );
				foreach( $task_exec_list as $task ){
					log_info( 'event', "event[$event] is dispatching to task[$task].", __FILE__, __LINE__ );

					// イベントフィルタ
					$process = FALSE;
					$event_filters = $task->getEventFilters();
					log_info( 'event', "task[$task] event filter: $event_filters.", __FILE__, __LINE__ );
					foreach( $event_filters as $filter ){
						if ( $event_name->equals( s($filter) ) ){
							$process = TRUE;
							break;
						}
					}

					if ( !$process ){
						log_info( 'event', "event[$event] is not in the event filters $event_filters.", __FILE__, __LINE__ );
						continue;
					}

					// ガード条件判定
					$process = TRUE;
					$guard_conditions = $task->getGuardConditions();
					log_info( 'event', "task[$task]'s guard conditions are $guard_conditions", __FILE__, __LINE__ );
					foreach( $guard_conditions as $task_name => $conditions ){
						if ( $this->isTaskRegistered(s($task_name)) ){
							$guard_task = $this->getTask( s($task_name) );
							foreach( $conditions as $key => $value ){
								// valueを型名：値に分ける
								$pos = strpos($value,':');
								if ( $pos === FALSE ){
									// 型指定がない場合、デフォルトは文字列
									$value = s($value);
								}
								else{
									$type = substr($value,0,$pos);
									$value = substr($value,$pos+1);
									switch( $type ){
									case 'b':	$value = boolval($value);		break;
									case 'i':	$value = intval($value);		break;
									case 's':	$value = $value;				break;
									default:
										_throw ( new TaskGuardConditionException($task,$guard_task,$key,"invalid data type in guard value[$value]" ) );
									}
								}
								if ( $guard_task->$key !== $value ){
									// ガード条件を満たさないので処理しない
									$type = gettype($guard_task->$key);
									$type2 = gettype($value);
									log_info( 'event', "guard task[$guard_task] status[$key => $type:{$guard_task->$key}] did not meet the guard conditions: task[$task_name] status[$key => $type2:{$value}].", __FILE__, __LINE__ );
									$process = FALSE;
									break 2;
								}
							}
						}
					}

					if ( !$process ){
						continue;
					}

					// イベント処理
					$result = $task->processEvent( $event, $this->getSnapshot() );

					// 例外スタックをクリア
					ExceptionStack::clear();

					// ログ
					log_info( 'event', "[$event] was processed by task[$task]: result=[$result]", __FILE__, __LINE__ );

					// 結果がIEventでもBooleanでもなければエラー
					if ( $result instanceof IEvent ){
						// イベントをキューに再投入
						$queue->enqueue( $result );
						log_info( 'event', "event[$result] was enqueued[$queue].", __FILE__, __LINE__ );	
					}
					else if ( !($result instanceof Boolean) ){
						_throw( new ProcessEventException( $event, $task, $result, s('ITask::processEvent() must return a [Boolean] or [IEvent] value.') ) );
					}

					// ポストアクション
					$post_actions = $task->getPostActions();
					log_info( 'event', "task[$task] post actions $post_actions .", __FILE__, __LINE__ );	
					if ( isset($post_actions[$event_name]) ){
						// ポストアクション実行
						$actions = $post_actions[$event_name];
						// 複数のアクション
						$actions = v(explode( ',', $actions ));
						log_info( 'event', "post actions for the event[$event] are [$actions].", __FILE__, __LINE__ );	
						foreach( $actions as $key => $action ){
							$action = trim($action);
							log_info( 'event', "executing post action[$action].", __FILE__, __LINE__ );	
							switch( $action ){
							case 'remove_task';
								// タスク実行リストからタスクを削除
								$task_name = $task->getName();
								unset( $next_task_exec_list["$task_name"] );
								log_info( 'event', "task[$task] was unregistered from execution list: $next_task_exec_list.", __FILE__, __LINE__ );
								break;
							case 'remove_event':
								// イベントを削除
								log_info( 'event', "event[$event] was removed.", __FILE__, __LINE__ );		
								$event = NULL;
								break;
							case 'continue_event':
								// イベントをキューに再投入						
								break;
							}
						}
					}
					else{
						log_info( 'event', "no post action is  defined for event[$event].", __FILE__, __LINE__ );	
					}

					// イベント削除ならループを抜ける
					if ( !$event ){
						break;
					}
				}

				// 次のイベントで使うタスク実行リスト
				$task_exec_list = $next_task_exec_list;

				// イベントカウントのインクリメント
				$event_count ++;
				// イベントカウントが最大を超えていたらワーニングを出して終了
				if ( $event_count > $max_event_loop ){
					log_warning( 'event', "イベント処理回数が設定値[$max_event_loop]を超えたため、イベント処理を中断しました。", __FILE__, __LINE__ );
					break;
				}

				// 経過時間チェック
				$now = Benchmark::nowTime();
				if ( $now - $start > $max_event_time ){
					log_warning( 'event', "イベント実行時間が設定値[$max_event_time]を超えたため、イベント処理を中断しました。", __FILE__, __LINE__ );
					break;
				}
			}

			if ( $queue->isEmpty() ){
				log_info( 'event', "event queue is empty.", __FILE__, __LINE__ );	
			}

			// ログ
			$now = Benchmark::nowTime();
			$elapse = round( $now - $start, 4 );
			log_info( 'event', "event loop end. count=[$event_count] time=[$elapse]sec.", __FILE__, __LINE__ );
//		}
//		catch( Exception $ex ){
//			print "catch: $ex <BR>";
//		}
	}



}

return __FILE__;
