<?php

/**
 * THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 *
 * @package fusionLib
 * @copyright Copyright (c) 2011 - 2013 fusionLib. All rights reserved.
 * @link http://fusionlib.com
 */

/**
 * flExceptionErrorController Class
 *
 * Abstract base class from which to derive a custom error controller, the error controller must
 * be named errorController in order for it to be used in reporting of errors.
 *
 * @package fusionLib
 */
class flExceptionErrorController extends flController {
	/**
	 * The exception object.
	 *
	 * @var flException
	 */
	private $exception;

	/**
	 * Construct the error controller.
	 *
	 * @param flRouter $router The router object.
	 * @param flRequest $request The request object.
	 * @param flException $exception The exception object.
	 */
	function __construct($router, $request, $exception = null) {

		// If router & request are null then try getting them from the application object
		if($router === null && $request === null) {
			$request = flApp::getRequest();
			$router = flApp::getRouter();
		}

		parent::__construct($router, $request);
		$this->exception = $exception === null ? new flException('Unknown error.') : $exception;
		$this->response = (flApp::getResponse() === null ? new flResponse() : flApp::getResponse());
		$this->response->setStatusCode($exception->getCode());
	}

	/**
	 * Get the exception object.
	 *
	 * @return flException The exception object.
	 */
	function getException() {
		return $this->exception;
	}

	/**
	 * Build a string holding backtrace information.
	 *
	 * @param flException $e The exception object.
	 * @param bool $html true if the result should be HTML formatted else false.
	 * @return string The string of information.
	 */
	static function buildBacktrace($e, $html) {
		$backtrace = $e->getStackTrace();

		$m = '';
		if(flExceptionReporter::withFullDebug() && is_array($backtrace) && count($backtrace)) {
			$ls = $html ? '<li>' : "\t";

			$m = $html ? "<h3>Backtrace</h3> <ul>\n" : "Backtrace\n\n";
			foreach($backtrace as $d) {
				$m .= ($html ? '<li>' : '') . $d['function'] . '(' . ($html ? '' : "\n");

				if(isset($d['args']) && count($d['args'])) {
					$alst = array();
					foreach($d['args'] as $a) {
						if(is_bool($a))
							$alst[] = $ls . ($a ? 'true' : 'false');
						else if(is_numeric($a))
							$alst[] = $ls . $a;
						else if(is_string($a))
							$alst[] = $ls . '"' . fusionLib::htmlChars(str_replace('"', '\"', $a)) . '"';
						else if(is_array($a)) {
							$arry = array();
							foreach($a as $i => $v) {
								if(is_bool($v)) $v = $v ? 'true' : 'false';
								else if(is_string($v)) $v = '"' . fusionLib::htmlChars(str_replace('"', '\"', $v)) . '"';
								else if(is_array($v)) $v = 'array(...)';
								else if(is_object($v)) $v = 'object(' . get_class($v) . ')';
								else if(is_null($v)) $v = 'null';
								$arry[] = '[' . (is_string($i) ? '"' . fusionLib::htmlChars(str_replace('"', '\"', $i)) . '"' : $i) . '] => ' . $v;
							}
							$alst[] = $ls . 'array(' . implode(', ', $arry) . ')';
						}
						else if(is_object($a))
							$alst[] = $ls . 'object(' . get_class($a) . ')';
						else if(is_null($a))
							$alst[] = $ls . 'null';
						else if(is_resource($a))
							$alst[] = $ls . 'resource';
						else
							$alst[] = $ls . '?';
					}
					if($html)
						$m .= '<ul>' . implode(',</li>', $alst) . '</li></ul>';
					else
						$m .= implode(",\n", $alst) . "\n";
				}

				if(isset($d['line']))
					$m .= ') at line ' . $d['line'] . ' of ' . preg_replace('/' . preg_quote(FLROOTPATH, '/') . '/', '', $d['file']) . ($html ? "</li>" : "\n") . "\n";
				else
					$m .= ")" . ($html ? '</li>' : "\n") . "\n";
			}
			$m .= $html ? "</ul>\n" : "\n";
		}

		return $m;
	}

	/**
	 * Get the file from the exception with the base path stripped off.
	 *
	 * @return string The file associated with the exception.
	 */
	function getFile() {
		return preg_replace('/^' . preg_quote(FLROOTPATH, '/') . '/', '', $this->getException()->getFile());
	}

	/**
	 * Get the exception message with the file base path stripped.
	 *
	 * @return string The exception message.
	 */
	function getMessage() {
		return preg_replace('/' . preg_quote(FLROOTPATH, '/') . '/', '', $this->getException()->getMessage());
	}

	/**
	 * The error action.
	 */
	function errorAction() {
		// Get the error document template, populate and output
		$string = file_get_contents(dirname(__FILE__) . '/errorDocument.html');
		foreach(array('code' => $this->getException()->getCode(),
			'descHeading' => __('Description', 'fusionLib'),
			'name' => $this->getException()->getName(),
			'message' => nl2br($this->getMessage()),
			'backtrace' => flExceptionErrorController::buildBacktrace($this->getException(), true),
			'fileline' => flExceptionReporter::withFullDebug() ? '<p><strong>File:</strong> ' . $this->getFile() . '<br /><strong>Line:</strong> ' . $this->getException()->getLine() . '</p>' : '') as $k => $v)
			$string = str_replace("[[+$k]]", $v, $string);

		$this->response->setBody($string)->sendResponse();
	}
}
