<?php

namespace Engine\Logger;

/**
 * Class for logging errors in XML files
 * @author dan
 */
class Xml extends GeneralAbstract
{

	/**
	 * The root directory where XML files will be saved
	 *  - monthly subdirectories will be automatically created
	 */
	private $directory = null;

	/**
	 * Force a custom log file path
	 */
	private $fullFilePath = null;

	/**
	 * Root tag of the XML file
	 */
	private $rootTag = "errorLog";


	/**
	 * Constructor
	 */
	public function __construct()
	{
		parent::__construct();

		global $CFG;
		$this->directory = realpath($CFG['logger']['pathToRoot'])."/";
	}

	/**
	 * Set custom log file path
	 */
	public function setFullFilePath($fullFilePath)
	{
		$this->fullFilePath = $fullFilePath;
	}

	/**
	 * Get custom log file path
	 */
	public function getFullFilePath()
	{
		return $this->fullFilePath;
	}

	/**
	 * Save the message in XML file
	 * (non-PHPdoc)
	 * @see \Engine\Logger\GeneralAbstract::writeRawMessage()
	 */
	public function writeRawMessage($fields)
	{
		$fullFilePath = $this->fullFilePath;
		if ($fullFilePath===null) {
			// if the daily file doesn't exist create it
			// if the daily file exists append to it

			$monthDir = date("Ym").'/';
			$filename = sprintf("%s.xml", date("Y-m-d"));

			// create dir
			if (!\Engine\Dir::check($this->directory.$monthDir)) {
				\Engine\Dir::create($this->directory.$monthDir);
			}

			$fullFilePath = $this->directory.$monthDir.$filename;
		}

		// create file
		if (!\Engine\File::check($fullFilePath)) {
			$xml = simplexml_load_string("<?xml version=\"1.0\" encoding=\"UTF-8\"?><".$this->rootTag."></".$this->rootTag.">");
			$errXml = $xml->addChild('error');
			$errXml = $this->composeXmlError($errXml, $fields['level'], $fields['message'], $fields['context'], $fields['stacktrace'], $fields['idUser'], $fields['addTime'], $fields['logSession']);
			$xml->asXML($fullFilePath);
		}
		// append to file
		else {

			// new error element
			$tempTag = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
			$errXml = simplexml_load_string("{$tempTag}<error></error>");
			$errXml = $this->composeXmlError($errXml, $fields['level'], $fields['message'], $fields['context'], $fields['stacktrace'], $fields['idUser'], $fields['addTime'], $fields['logSession']);
			$xmlStr = str_replace($tempTag, '', $errXml->asXML());  // get a clean xml element

			// file resource
			$fh = fopen($fullFilePath, 'a+');
			$stat = fstat($fh);

			ftruncate($fh, $stat['size']-(strlen($this->rootTag)+4)); // delete the root tag from the end of the file
			fwrite($fh, $xmlStr."</".$this->rootTag.">");             // append the new element

			fclose($fh);
		}

		return true;
	}

	/**
	 * Generate XML error
	 * @param \SimpleXMLElement $errXml
	 * @return \SimpleXMLElement
	 */
	private function composeXmlError($errXml, $level, $message, $context, $stacktrace, $idUser, $addTime, $logSession)
	{
		$errXml->addTime = $addTime;
		$errXml->level = $level;
		$errXml->logSession = $logSession;
		$errXml->message = $message;
		$errXml->context = '';
		$this->composeContext($errXml->context, $context);
		$errXml->stacktrace = '';
		$this->composeStacktrace($errXml->stacktrace, $stacktrace);
		$errXml->idUser = $idUser;

		return $errXml;
	}

	/**
	 * Convert the stacktrace array to XML format
	 * @param \SimpleXMLElement $xmlStack
	 * @param Array $stacktrace
	 * @return \SimpleXMLElement
	 */
	private function composeStacktrace($xmlStack, Array $stacktrace)
	{
		$prefixIgnore = realpath(MODULE_REL_PATH).'/';

		foreach ($stacktrace as $step) {
			$xmlStack->addChild("trace", sprintf("%s:%s, %s->%s()\n", str_replace($prefixIgnore, "", $step['file']), $step['line'], $step['class'], $step['function']));
		}

		return $xmlStack;
	}

	/**
	 * Convert the context array to XML format
	 * @param \SimpleXMLElement $xmlCtx
	 * @param array $context
	 * @return \SimpleXMLElement
	 */
	private function composeContext($xmlCtx, Array $context)
	{
		foreach ($context as $key=>$value) {
			// if value is object, array, or resource -> serialize it
			if (is_object($value) || is_array($value) || is_resource($value)) {
				$xmlValue = serialize($value);
			}
			else {
				$xmlValue = (string)$value;
			}

			$xmlParam = $xmlCtx->addChild("param", $xmlValue);
			$xmlParam['name'] = $key;
			$xmlParam['type'] = gettype($value);
		}

		return $xmlCtx;
	}

}
