<?php

defined('HOSTCMS') || exit('HostCMS: access denied.');

/**
 * Shortcode Controller
 *
 * @package HostCMS
 * @subpackage Shortcode
 * @version 7.x
 * @copyright © 2005-2024, https://www.hostcms.ru
 */
class Shortcode_Controller
{
	/**
	 * The singleton instances.
	 * @var mixed
	 */
	static public $instance = NULL;

	/**
	 * Register an existing instance as a singleton.
	 * @return object
	 */
	static public function instance()
	{
		if (is_null(self::$instance))
		{
			self::$instance = new self();
			self::$instance->loadShortcodes();
		}

		return self::$instance;
	}

	/**
	 * Array of Shortcodes
	 */
	protected $_shortcodes = array();

	/**
	 * Array of Shortcodes' names
	 */
	protected $_shortcodeNames = NULL;

	/**
	 * Get Count of Shortcodes
	 * @return int
	 */
	public function getCount()
	{
		return count($this->_shortcodes);
	}

	/**
	 * Load List of Shortcodes
	 */
	public function loadShortcodes()
	{
		$aShortcodes = Core_Entity::factory('Shortcode')->getAllByActive(1);

		foreach ($aShortcodes as $oShortcode)
		{
			$this->_shortcodes[$oShortcode->shortcode] = $oShortcode;
			$this->_shortcodeNames[] = $oShortcode->shortcode;
		}

		return $this;
	}

	/**
	 * Get List of Shortcodes
	 * @return array
	 */
	public function getShortcodes()
	{
		return $this->_shortcodes;
	}

	/**
	 * Add double tab to the left
	 * @param string $str
	 * @return string
	 */
	static public function addStrTab($str)
	{
		return "\t\t" . trim($str, "\r");
	}

	/**
	 * Rebuild Shortcode List
	 * @return self
	 */
	public function rebuild()
	{
		$this->loadShortcodes();

		$content = '<?php

defined(\'HOSTCMS\') || exit(\'HostCMS: access denied.\');

/**
 * Shortcode List
 *
 * DO NOT EDIT THIS FILE BY HAND!
 * YOUR CHANGES WILL BE OVERWRITTEN!
 *
 * @package HostCMS\Shortcode
 * @version 7.x
 * @copyright © 2005-2024, https://www.hostcms.ru
 */

class Shortcode_List
{';

		foreach ($this->_shortcodes as $oShortcode)
		{
			$aExplode = explode("\n", $oShortcode->php);
			$aExplode = array_map(array('Shortcode_Controller', 'addStrTab'), $aExplode);
			$content .= PHP_EOL . "\t" . 'static public function ' . $oShortcode->shortcode . '($args, $body)'
				. PHP_EOL
				. "\t{\n"
				. implode("\n", $aExplode)
				. PHP_EOL
				. "\t}"
				. PHP_EOL;
		}

		$content .=	'}';

		Core_File::write(CMS_FOLDER . '/modules/shortcode/list.php', $content);
	}

	/**
	 * Apply Shortcode
	 * @param string $string
	 * @return string
	 */
	public function applyShortcodes($string)
	{
		if (is_string($string) && $string != '')
		{
			preg_match_all('/\[([^\s\[\]]++)/', $string, $matches);

			$aIntersect = array_intersect($this->_shortcodeNames, $matches[1]);
			foreach ($aIntersect as $sShortcodeName)
			{
				$sShortcodeNameQuoted = preg_quote($sShortcodeName);

				$pattern = '/(\[{1,2})'
					// Shortcode's name
					. '(' . $sShortcodeNameQuoted . ')(?![A-Za-z0-9А-Яа-яёЁ_-])'
					// Shortcode's options
					. '([^\]]*)'
					// Close Shortcode
					. '(\]{1,2})'
					// Body
					. '(?:'
						. '([^\[]*+(?:\[(?!\/' . $sShortcodeNameQuoted . '\])[^\[]*+)*+)'
						// Close Shortcode
						. '\[\/' . $sShortcodeNameQuoted . '\]'
					. ')?/';

				$string = preg_replace_callback($pattern, array('Shortcode_Controller', 'prepareCallShortcode'), $string);
			}
		}

		return (string) $string;
	}

	/**
	 * Prepare body and args to call shortcode
	 * @param array $matches
	 * @return string
	 */
	static public function prepareCallShortcode($matches)
	{
		if ($matches[1] == '[[')
		{
			return count($matches) == 6
				? '[' . $matches[2] . $matches[3] . ']' . $matches[5] . '[/' . $matches[2] . ']'
				: '[' . $matches[2] . $matches[3] . ']';
		}

		$methodName = $matches[2];

		if (method_exists('Shortcode_List', $methodName))
		{
			$attr = array();

			if ($matches[3] != '')
			{
				preg_match_all('/([A-Za-z0-9А-Яа-яёЁ_-]+)\s*=\s*(?:(?:"([^"]*)")|(?:\'([^\']*)\')|([^"\'\s]+))/', $matches[3], $matchesAttr);

				if (isset($matchesAttr[1]))
				{
					foreach ($matchesAttr[1] as $key => $attrName)
					{
						$value = $matchesAttr[2][$key] != ''
							? $matchesAttr[2][$key]
							: (
								$matchesAttr[3][$key] != ''
									? $matchesAttr[3][$key]
									: $matchesAttr[4][$key]
							);

						($value === 'true' || $value === 'TRUE') && $value = TRUE;
						($value === 'false' || $value === 'FALSE') && $value = FALSE;
						($value === 'null' || $value === 'NULL') && $value = NULL;

						$attr[$attrName] = $value;
					}
				}
			}

			return Shortcode_List::$methodName($attr, Core_Array::get($matches, 5));
		}
	}
}