<?php
namespace Engine\Logger\Context;

class ExceptionAsHtml extends \Engine\Logger\Context\StringRaw
{
	public function __construct(\Throwable $ex)
	{
		$extractedExceptionData = [];
		$extractedExceptionData = $this->descendIntoPreviousExceptions($ex, $extractedExceptionData);
		$this->value = $extractedExceptionData;
	}


	protected function renderOperations_groups($classes)
	{
		$outputOperations = "";
		if (in_array('limit-height', $classes)) {
			$outputOperations.= "<span class='group'>";
			$outputOperations.= "<button data-group='height' data-type='collapse'>minimize</button>";
			$outputOperations.= "<button data-group='height' data-type='maximize'>maximize &#x2195;</button>";
			$outputOperations.= "<button data-group='height' data-type='reset' class='active'>reset</button>";
			$outputOperations.= "</span>";
		}
		if (in_array('limit-width', $classes)) {
			$outputOperations.= "<span class='group'>";
			$outputOperations.= "<button data-group='width' data-type='maximize'>maximize &#x2194;</button>";
			$outputOperations.= "<button data-group='width' data-type='reset' class='active'>reset</button>";
			$outputOperations.= "</span>";
		}

		$outputOperations.= "<span class='group'>";
		$outputOperations.= "<button data-group='stacktrace' data-type='method' class='active'>method</button>";
		$outputOperations.= "<button data-group='stacktrace' data-type='file'>file</button>";
		$outputOperations.= "</span>";

		return $outputOperations;
	}


	private function descendIntoPreviousExceptions(\Throwable $ex, $extractedExceptionData)
	{
		$exceptionData = [
			'class' => '',
			'code' => '',
			'message' => '',
			'stacktrace' => [],
		];

		if (get_class($ex)=='App\Entity\Exception') {
			foreach ($ex->getMessagesCollector()->getMessages() as $msg) {
				$exceptionData['message'].= sprintf("%s: %s\n", $msg->getType(), $msg->getText());
			}
		}
		else {
			$exceptionData['message'] = $ex->getMessage();
		}

		$exceptionData['class']= get_class($ex);
		$exceptionData['code'].= $ex->getCode();

		$exceptionData['stacktrace'][]= [
			'file' => str_replace(APP_REAL_PATH, '/', $ex->getFile()),
			'line' => $ex->getLine(),
		];
		foreach ($ex->getTrace() as $rawtr) {
			$tr = [];
			$ltr = end($exceptionData['stacktrace']);

			$tr['file'] = 		str_replace(APP_REAL_PATH, '/', (string)$rawtr['file']);
			$tr['line'] = 		$rawtr['line'];

			$ltr['class'] = 		$rawtr['class'];
			$ltr['type'] = 		$rawtr['type'];
			$ltr['function'] = 	$rawtr['function'];
			$ltr['args'] = 		sprintf('%d args', count((array)$rawtr['args']));

			$exceptionData['stacktrace'][count($exceptionData['stacktrace'])-1]= $ltr;
			$exceptionData['stacktrace'][]= $tr;
		}

		$exceptionData['previous'] = null;
		$extractedExceptionData['rawExceptionData'][]= $exceptionData;
		#$extractedExceptionData['collectedMessages'][]= $exceptionData['message'];
		#$extractedExceptionData['collectedStacktraces'][]= $exceptionData['stacktrace'];

		if ($ex->getPrevious()) {
			$extractedExceptionData = $this->descendIntoPreviousExceptions($ex->getPrevious(), $extractedExceptionData);
		}

		return $extractedExceptionData;
	}


	protected function formatFile($file, $line)
	{
		$replacements = [
			"/(.*vendor.*)/i" => 						   	"<span style='color: hsl(180, 40%, 66%);'>$1 : __LINE__</span>",
			"/(.*files\/smartyTemplatesCompiled\/.*)/i" => 	"<span style='color: hsl(160, 40%, 66%);'>$1 : __LINE__</span>",

			"/(.*\/_classes\/App\/.*)/i" => 				"<span style='color: hsl( 30, 40%, 50%);'>$1 : __LINE__</span>",
			"/(.*\/_classes\/Engine\/.*)/i" => 				"<span style='color: hsl( 75, 40%, 50%);'>$1 : __LINE__</span>",
			"/(.*\/_classes\/.*)/i" => 						"<span style='color: hsl( 60, 40%, 50%);'>$1 : __LINE__</span>",

			"/(.*\/Modules\/.*)/i" => 						"<span style='color: hsl(  0, 75%, 30%);'>$1 : __LINE__</span>",
			"/(.*\/Plugins\/.*)/i" => 						"<span style='color: hsl( 20, 90%, 30%);'>$1 : __LINE__</span>",

			"/(.*\/disp.php)/i" => 						    "<span style='color: hsl(  0, 50%, 66%);'>$1 : __LINE__</span>",
		];

		foreach ($replacements as $searchRegexp=>$replaceRegexp) {
			if (preg_match($searchRegexp, $file)) {
				$replaceRegexp = str_replace('__LINE__', $line, $replaceRegexp);
				return sprintf("<span class='file'>%s</span>",
					preg_replace($searchRegexp, $replaceRegexp, $file)
				);
			}
		}

		return sprintf("<span class='file'>%s</span> : %d", $file, $line);
	}


	protected function formatMethod($class, $type, $function, $args)
	{
		$replacements = [
			"/(.*(Smarty_Template_|Smarty_Internal_|Block_).*)/i" =>"<span style='color: hsl(160, 40%, 66%);'>$1</span>",

			"/^(App\\\\.*)/i" => 									"<span style='color: hsl( 30, 40%, 50%);'>$1</span>",
			"/^(Engine\\\\.*)/i" => 								"<span style='color: hsl( 75, 40%, 50%);'>$1</span>",

			"/^(Modules\\\\.*)/i" => 								"<span style='color: hsl(  0, 75%, 30%);'>$1</span>",
			"/^(Plugins\\\\.*)/i" => 								"<span style='color: hsl( 20, 90%, 30%);'>$1</span>",
		];

		foreach ($replacements as $searchRegexp=>$replaceRegexp) {
			$caller = sprintf("%s%s%s()", $class, $type, $function);
			if (preg_match($searchRegexp, $caller)) {
				return sprintf("<span class='file'>%s</span>",
					(string)preg_replace($searchRegexp, $replaceRegexp, $caller)
				);
			}
		}

		return sprintf("<span class='class'>%s</span>%s<span class='method'>%s</span>()", $class, $type, $function);
	}


	public function render($logRow=null, $key=null)
	{
		$html = "";

		foreach ($this->value['rawExceptionData'] as $exceptionIndex=>$exceptionData) {
			$index = count($this->value['rawExceptionData'])-$exceptionIndex;

			if ($html) {
				$html.= sprintf("<hr/>");
			}
			$html.= sprintf("<h3 style='margin-bottom: 0;'>[%s #%d] [%s] %s</h3><div><sub>code: %s</sub></div>",
				($index==1?'throw&nbsp;&nbsp;':'rethrow'),
				$index,
				$exceptionData['class'],
				$exceptionData['message'],
				$exceptionData['code']
			);

			$stacktraceHtml = "";
			$stacktraceHtml.= <<<HTML
<table class='content' style='margin-top: 1em;'>
	<tbody>
		<tr class='stacktrace'>
			<td>
				<table>
					<tbody>
HTML;

			foreach ($exceptionData['stacktrace'] as $tr) {
				$stacktraceHtml.= sprintf(<<<HTML
<tr class='source logger'>
	<td class='file' style='display: none;'>
		%s
	</td>
	<td class='method'>
		%s
	</td>
</tr>
HTML
	,
					$this->formatFile($tr['file'], $tr['line']),
					($tr['class'] || $tr['function']?
						$this->formatMethod($tr['class'], $tr['type'], $tr['function'], $tr['args'])
						:''
					)
);
			}


			$stacktraceHtml.= <<<HTML
				</tbody>
			</table>
		</td>
	</tr>
</table>
HTML;
			$html.= $stacktraceHtml;
		}


		// "standard" html tag container
		$output = '';
		$classes = $this->calculateDimensionClass($html);
		$output.= sprintf("%s<div class='html %s'>%s</div>", $this->renderOperations($classes), 	implode(" ", $classes), $html);
		return $output;
	}

}
