<?php
/**
 * Pimcore
 *
 * This source file is subject to the GNU General Public License version 3 (GPLv3)
 * For the full copyright and license information, please view the LICENSE.md and gpl-3.0.txt
 * files that are distributed with this source code.
 *
 * @copyright  Copyright (c) 2009-2016 pimcore GmbH (http://www.pimcore.org)
 * @license    http://www.pimcore.org/license     GNU General Public License version 3 (GPLv3)
 */

namespace Pimcore;

use Pimcore\Model;

class Placeholder
{

    /**
     * Prefix for the Placeholders
     *
     * @var string
     */
    protected static $placeholderPrefix = '%';

    /**
     * Suffix for the Placeholders
     *
     * @var string
     */
    protected static $placeholderSuffix = ';';

    /**
     * Prefixes for the Placeholder Classes
     *
     * @var string
     */
    protected static $placeholderClassPrefixes = ['Pimcore_Placeholder_', 'Website_Placeholder_', "\\Pimcore\\Placeholder\\","\\Website\\Placeholder\\"];

    /**
     * Contains the document object
     *
     * @var Model\Document | null
     */
    protected $document;

    /**
     * @param $classPrefix
     * @throws \Exception
     */
    public static function addPlaceholderClassPrefix($classPrefix){
        if(!is_string($classPrefix) || $classPrefix == ''){
            throw new \Exception('$classPrefix has to be a valid string and mustn\'t be empty');
        }

        self::$placeholderClassPrefixes[] = $classPrefix;
    }

    /**
     * @param $classPrefix
     * @return bool
     * @throws \Exception
     */
    public static function removePlaceholderClassPrefix($classPrefix){
        if(!is_string($classPrefix) || $classPrefix == ''){
            throw new \Exception('$classPrefix has to be a valid string and mustn\'t be empty');
        }

        $arrayIndex = array_search($classPrefix,self::$placeholderClassPrefixes);

        if($arrayIndex === false){
            return false;
        }else{
            unset(self::$placeholderClassPrefixes[$arrayIndex]);
            return true;
        }
    }

    /**
     * Returns the Placeholder class prefixes
     * @static
     * @return array
     */
    public static function getPlaceholderClassPrefixes(){
        return array_reverse(self::$placeholderClassPrefixes);
    }

    /**
     * Sets a custom website class prefix for the Placeholder Classes
     *
     * @static
     * @param $string
     * @deprecated deprecated since version 1.4.6
     */
    public static function setWebsiteClassPrefix($string)
    {
        self::addPlaceholderClassPrefix($string);
    }

    /**
     * Returns the website class prefix for the Placeholder Classes
     *
     * @static
     * @return string
     * @deprecated deprecated since version 1.4.6
     */
    public static function getWebsiteClassPrefix()
    {
        return self::$placeholderClassPrefixes[1];
    }

    /**
     * Set a custom Placeholder prefix
     *
     * @throws Exception
     * @param string $prefix
     * @return void
     * @deprecated deprecated since version 1.4.6
     */
    public static function setPlaceholderPrefix($prefix)
    {
        self::addPlaceholderClassPrefix($prefix);
    }

    /**
     * Returns the Placeholder prefix
     *
     * @return string
     */
    public static function getPlaceholderPrefix()
    {
        return self::$placeholderPrefix;
    }

    /**
     * Returns the Placeholder suffix
     *
     * @return string
     */
    public static function getPlaceholderSuffix()
    {
        return self::$placeholderSuffix;
    }

    /**
     * @param $suffix
     * @throws \Exception
     */
    public function setPlaceholderSuffix($suffix)
    {
        if (!is_string($suffix)) {
            throw new \Exception("\$suffix mustn'n be empty");
        }
        self::$placeholderSuffix = $suffix;
    }


    /**
     * Detects the Placeholders in a string and returns an array with the placeholder information
     *
     * @param string $contentString
     * @param null | array $params
     * @param null | Model\Document $document
     * @return array
     */
    public function detectPlaceholders($contentString, $params, $document = null)
    {
        $placeholderStack = array();

        $regex = "/" . self::$placeholderPrefix . "([a-z_]+)\(([a-z_0-9]+)[\s,]*(.*?)\)" . self::$placeholderSuffix . "/is";
        preg_match_all($regex, $contentString, $matches);

        if (is_array($matches[1])) {

            foreach ($matches[1] as $key => $match) {
                $placeholderString = $matches[0][$key]; //placeholder string
                $placeholderClass = $matches[1][$key]; //placeholder php class
                $placeholderKey = $matches[2][$key]; //key for the dynamic param
                $placeholderConfigString = $matches[3][$key];

                if ($placeholderConfigString) {
                    //try to create the json config object
                    try {
                        $configJsonString = str_replace(array("&quot;","'"), '"', $placeholderConfigString);
                        $placeholderConfig = new \Zend_Config_Json($configJsonString,null,array('ignoreconstants' => true));
                    } catch (\Exception $e) {
                        \Logger::warn('PlaceholderConfig is not a valid JSON string. PlaceholderConfig for ' . $placeholderClass . ' ignored.');
                        continue;
                    }
                } else {
                    //create an empty config object if no config object was passed
                    $placeholderConfig = new \Zend_Config_Json("{}");
                }

                $placeholderStack[] = array('placeholderString' => $placeholderString,
                    'placeholderClass' => $placeholderClass,
                    'placeholderKey' => $placeholderKey,
                    'placeholderConfig' => $placeholderConfig,
                    'document' => $document,
                    'params' => $params,
                    'contentString' => $contentString);
            }
        }

        return $placeholderStack;
    }

    /**
     * Helper to simply replace the placeholders with their value
     *
     * @param string | Model\Document $mixed
     * @param array $params
     * @param null | Model\Document $document
     * @return string
     */
    public function replacePlaceholders($mixed, $params = array(), $document = null,$enableLayoutOnPlaceholderReplacement = true)
    {
        if (is_string($mixed)) {
            $contentString = $mixed;
        } elseif ($mixed instanceof Model\Document) {
            $contentString = Model\Document\Service::render($mixed, $params, $enableLayoutOnPlaceholderReplacement);
        }
        if ($document instanceof Model\Document === false) {
            $document = null;
        }

        //detects the placeholders
        $placeholderStack = $this->detectPlaceholders($contentString, $params, $document);

        //replaces the placeholders if any were found
        if (!empty($placeholderStack)) {
            $replacedString = $this->replacePlaceholdersFromStack($placeholderStack);
            return $replacedString;
        } else {
            return $contentString;
        }
    }

    /**
     * Creates the Placeholder objects and replaces the placeholder string
     * with the rendered content of the placeholder object
     *
     * @param array $placeholderStack
     * @return string
     */
    protected function replacePlaceholdersFromStack($placeholderStack = array())
    {
        $stringReplaced = null;
        if (!empty($placeholderStack)) {

            foreach ($placeholderStack as $placeholder) {
                $placeholderObject = null;
                $placeholderClassPrefixes = self::getPlaceholderClassPrefixes();

                $placeholderObject = null;

                foreach($placeholderClassPrefixes as $classPrefix){
                    $className = $classPrefix . $placeholder['placeholderClass'];
                    if(Tool::classExists($className)){
                        $placeholderObject = new $className();
                        break;
                    }
                }

                if (is_null($stringReplaced)) {
                    $stringReplaced = $placeholder['contentString'];
                }

                if ($placeholderObject instanceof Placeholder\AbstractPlaceholder) {

                    //setting values from placeholder stack to placeholder objects
                    foreach (array_keys($placeholder) as $key) {
                        if ($key == 'placeholderClass') {
                            continue;
                        }
                        $placeholderObject->{'set' . ucfirst($key)}($placeholder[$key]);
                    }
                    $placeholderObject->setLocale();

                    $replaceWith = $placeholderObject->getReplacement();
                    if (!isset($replaceWith)) {
                        $replaceWith = $placeholderObject->getEmptyValue();
                    }
                    $stringReplaced = str_replace($placeholderObject->getPlaceholderString(), $replaceWith, $stringReplaced);
                } else {
                    \Logger::warn('Ignoring Placeholder "' . $placeholder['placeholderClass'] . '" -> Class not Found or not an instance of Pimcore_Placeholder_Abstract!');
                }
            }
        }
        return $stringReplaced;
    }
}