<?php

	/**
	 * NRE
	 *
	 * @author	coderkun <olli@coderkun.de>
	 * @copyright	2013 coderkun (http://www.coderkun.de)
	 * @license	http://www.gnu.org/licenses/gpl.html
	 * @link	http://www.coderkun.de/projects/nre
	 */
	
	namespace nre\agents;
	
	
	/**
	 * The ToplevelAgent assumes the task of a FrontController. There is
	 * only one per request.
	 * 
	 * @author	coderkun <olli@coderkun.de>
	 */
	class ToplevelAgent extends \nre\core\Agent
	{
		/**
		 * Stage: Load
		 * 
		 * @var string
		 */
		const STAGE_LOAD	=	'load';
		/**
		 * Stage: Run
		 * 
		 * @var string
		 */
		const STAGE_RUN		=	'run';
		
		/**
		 * Current request
		 * 
		 * @var Request
		 */
		private $request;
		/**
		 * Current response
		 * 
		 * @var Response
		 */
		private $response;
		/**
		 * Layout instace
		 * 
		 * @var Layout
		 */
		private $layout = null;
		/**
		 * IntermediateAgent instance
		 * 
		 * @var IntermediateAgent
		 */
		private $intermediateAgent = null;
		
		
		
		
		/**
		 * Construct a ToplevelAgent.
		 * 
		 * @throws	ServiceUnavailableException
		 * @throws	DatamodelException
		 * @throws	DriverNotValidException
		 * @throws	DriverNotFoundException
		 * @throws	ViewNotFoundException
		 * @throws	ModelNotValidException
		 * @throws	ModelNotFoundException
		 * @throws	ControllerNotValidException
		 * @throws	ControllerNotFoundException
		 * @param	Request		$request	Current request
		 * @param	Response	$respone	Current response
		 * @param	Logger		$log		Log-system
		 */
		protected function __construct(\nre\core\Request $request, \nre\core\Response $response, \nre\core\Logger $log=null)
		{
			// Store values
			$this->request = $request;
			$this->response = $response;
			
			
			// Create response
			$response = clone $response;
			$response->clearParams(1);
			$response->addParams(
				null,
				\nre\configs\CoreConfig::$defaults['action']
			);
			
			// Call parent constructor
			parent::__construct($request, $response, $log, true);
			
			
			// Load IntermediateAgent
			$this->loadIntermediateAgent();
		}
		
		
		
		
		/**
		 * Run the Controller of this Agent and its SubAgents.
		 * 
		 * @throws	ServiceUnavailableException
		 * @param	Request		$request	Current request
		 * @param	Response	$response	Current response
		 * @return	Exception			Last occurred exception of SubAgents
		 */
		public function run(\nre\core\Request $request, \nre\core\Response $response)
		{
			try {
				return $this->_run($request, $response);
			}
			catch(\nre\exceptions\AccessDeniedException $e) {
				$this->error($e, \nre\core\WebUtils::HTTP_FORBIDDEN, self::STAGE_RUN);
			}
			catch(\nre\exceptions\ParamsNotValidException $e) {
				$this->error($e, \nre\core\WebUtils::HTTP_NOT_FOUND, self::STAGE_RUN);
			}
			catch(\nre\exceptions\IdNotFoundException $e) {
				$this->error($e, \nre\core\WebUtils::HTTP_NOT_FOUND, self::STAGE_RUN);
			}
			catch(\nre\exceptions\DatamodelException $e) {
				$this->error($e, \nre\core\WebUtils::HTTP_SERVICE_UNAVAILABLE, self::STAGE_RUN);
			}
			catch(\nre\exceptions\ActionNotFoundException $e) {
				$this->error($e, \nre\core\WebUtils::HTTP_NOT_FOUND, self::STAGE_RUN);
			}
		}
		
		
		/**
		 * Generate output of the Controller of this Agent and its
		 * SubAgents.
		 * 
		 * @param	array	$data	View data
		 * @return	string		Generated output
		 */
		public function render($data=array())
		{
			// Render IntermediateAgent
			$data = array();
			$data['intermediate'] = $this->intermediateAgent->render();
			
			
			// Render ToplevelAgent
			return parent::render($data);
		}
		
		
		/**
		 * Return the IntermediateAgent.
		 * 
		 * @return	IntermediateAgent	IntermediateAgent
		 */
		public function getIntermediateAgent()
		{
			return $this->intermediateAgent;
		}
		
		
		
		
		/**
		 * Load a SubAgent and add it.
		 * 
		 * @throws	ServiceUnavailableException
		 * @throws	FatalDatamodelException
		 * @throws	AgentNotFoundException
		 * @throws	AgentNotValidException
		 * @param	string	$agentName	Name of the Agent to load
		 * @param	mixed	…		Additional parameters for the agent
		 */
		protected function addSubAgent($agentName)
		{
			try {
				call_user_func_array(
					array(
						$this,
						'_addSubAgent'
					),
					func_get_args()
				);
			}
			catch(\nre\exceptions\DatamodelException $e) {
				throw new \nre\exceptions\FatalDatamodelException($e->getDatamodelMessage(), $e->getDatamodelErrorNumber());
			}
		}
		
		
		
		
		/**
		 * Load IntermediateAgent defined by the current request.
		 * 
		 * @throws	ServiceUnavailableException
		 */
		private function loadIntermediateAgent()
		{
			try {
				$this->_loadIntermediateAgent();
			}
			catch(\nre\exceptions\ViewNotFoundException $e) {
				$this->error($e, \nre\core\WebUtils::HTTP_NOT_FOUND);
			}
			catch(\nre\exceptions\DatamodelException $e) {
				$this->error($e, \nre\core\WebUtils::HTTP_SERVICE_UNAVAILABLE);
			}
			catch(\nre\exceptions\DriverNotValidException $e) {
				$this->error($e, \nre\core\WebUtils::HTTP_SERVICE_UNAVAILABLE);
			}
			catch(\nre\exceptions\DriverNotFoundException $e) {
				$this->error($e, \nre\core\WebUtils::HTTP_SERVICE_UNAVAILABLE);
			}
			catch(\nre\exceptions\ModelNotValidException $e) {
				$this->error($e, \nre\core\WebUtils::HTTP_SERVICE_UNAVAILABLE);
			}
			catch(\nre\exceptions\ModelNotFoundException $e) {
				$this->error($e, \nre\core\WebUtils::HTTP_SERVICE_UNAVAILABLE);
			}
			catch(\nre\exceptions\ControllerNotValidException $e) {
				$this->error($e, \nre\core\WebUtils::HTTP_SERVICE_UNAVAILABLE);
			}
			catch(\nre\exceptions\ControllerNotFoundException $e) {
				$this->error($e, \nre\core\WebUtils::HTTP_NOT_FOUND);
			}
			catch(\nre\exceptions\AgentNotValidException $e) {
				$this->error($e, \nre\core\WebUtils::HTTP_SERVICE_UNAVAILABLE);
			}
			catch(\nre\exceptions\AgentNotFoundException $e) {
				$this->error($e, \nre\core\WebUtils::HTTP_NOT_FOUND);
			}
		}
		
		
		/**
		 * Load IntermediateAgent defined by the current request.
		 * 
		 * @throws	ServiceUnavailableException
		 */
		private function _loadIntermediateAgent()
		{
			// Determine IntermediateAgent
			$agentName = $this->response->getParam(1);
			if(is_null($agentName)) {
				$agentName = $this->request->getParam(1, 'intermediate');
				$this->response->addParam($agentName);
			}
			
			// Load IntermediateAgent
			IntermediateAgent::load($agentName);
			
			
			// Determine Action
			$action = $this->response->getParam(2);
			if(is_null($action)) {
				$action = $this->request->getParam(2, 'action');
				$this->response->addParam($action);
			}
			
			// Construct IntermediateAgent
			$this->intermediateAgent = \nre\agents\IntermediateAgent::factory(
				$agentName,
				$this->request,
				$this->response,
				$this->log
			);
		}
		
		
		/**
		 * Run the Controller of this Agent and its SubAgents.
		 * 
		 * @throws	AccessDeniedException
		 * @throws	IdNotFoundException
		 * @throws	ServiceUnavailableException
		 * @throws	DatamodelException
		 * @param	Request		$request	Current request
		 * @param	Response	$response	Current response
		 * @return	Exception			Last occurred exception of SubAgents
		 */
		private function _run(\nre\core\Request $request, \nre\core\Response $response)
		{
			// Run IntermediateAgent
			$this->runIntermediateAgent();
			
			
			// TODO Request instead of response?
			$response = clone $response;
			$response->clearParams(2);
			$response->addParam(\nre\configs\CoreConfig::$defaults['action']);
			
			
			// Run ToplevelAgent
			return parent::run($request, $response);
		}
		
		
		/**
		 * Run IntermediateAgent.
		 * 
		 * @throws	AccessDeniedException
		 * @throws	ParamsNotValidException
		 * @throws	IdNotFoundException
		 * @throws	ServiceUnavailableException
		 * @throws	DatamodelException
		 */
		private function runIntermediateAgent()
		{
			$this->intermediateAgent->run(
				$this->request,
				$this->response
			);
		}
		
		
		/**
		 * Handle an error that occurred during
		 * loading/cnostructing/running of the IntermediateAgent.
		 * 
		 * @throws	ServiceUnavailableException
		 * @param	Exception	$exception	Occurred exception
		 * @param	int		$httpStatusCode	HTTP-statuscode
		 * @param	string		$stage		Stage of execution
		 */
		private function error($exception, $httpStatusCode, $stage=self::STAGE_LOAD)
		{
			// Log error
			$this->log($exception, \nre\core\Logger::LOGMODE_AUTO);
			
			
			try {
				// Define ErrorAgent
				$this->response->clearParams(1);
				$this->response->addParams(
					\nre\configs\AppConfig::$defaults['intermediate-error'],
					\nre\configs\CoreConfig::$defaults['action'],
					$httpStatusCode
				);
				
				// Load ErrorAgent
				$this->_loadIntermediateAgent();
				
				// Run ErrorAgent
				if($stage == self::STAGE_RUN) {
					$this->_run($this->request, $this->response);
				}
			}
			catch(\nre\exceptions\ActionNotFoundException $e) {
				throw new \nre\exceptions\ServiceUnavailableException($e);
			}
			catch(\nre\exceptions\DatamodelException $e) {
				throw new \nre\exceptions\ServiceUnavailableException($e);
		 	}
			catch(\nre\exceptions\DriverNotValidException $e) {
				throw new \nre\exceptions\ServiceUnavailableException($e);
		 	}
			catch(\nre\exceptions\DriverNotFoundException $e) {
				throw new \nre\exceptions\ServiceUnavailableException($e);
		 	}
			catch(\nre\exceptions\ModelNotValidException $e) {
				throw new \nre\exceptions\ServiceUnavailableException($e);
		 	}
		 	catch(\nre\exceptions\ModelNotFoundException $e) {
		 		throw new \nre\exceptions\ServiceUnavailableException($e);
		 	}
		 	catch(\nre\exceptions\ViewNotFoundException $e) {
		 		throw new \nre\exceptions\ServiceUnavailableException($e);
		 	}
			catch(\nre\exceptions\ControllerNotValidException $e) {
				throw new \nre\exceptions\ServiceUnavailableException($e);
			}
			catch(\nre\exceptions\ControllerNotFoundException $e) {
				throw new \nre\exceptions\ServiceUnavailableException($e);
			}
			catch(\nre\exceptions\AgentNotValidException $e) {
				throw new \nre\exceptions\ServiceUnavailableException($e);
			}
			catch(\nre\exceptions\AgentNotFoundException $e) {
				throw new \nre\exceptions\ServiceUnavailableException($e);
			}
			catch(Exception $e) {
				throw new \nre\exceptions\ServiceUnavailableException($e);
			}
		}
		
		
	}

?>
