<?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;
use Pimcore\Model\Element;

class View extends \Zend_View {

    /**
     * @var \Zend_Controller_Request_Abstract
     */
    protected $request;

    /**
     * @var bool
     */
    protected static $addComponentIds = false;

    /**
     * @param $type
     * @param $realName
     * @param array $options
     * @return Model\Document\Tag
     */
    public function tag($type, $realName, $options = array()) {

        $type = strtolower($type);

        try {
            $document = $this->document;
            $name = Model\Document\Tag::buildTagName($type,$realName, $document);

            if($document instanceof Model\Document) {
                $tag = $document->getElement($name);
                if ($tag instanceof Model\Document\Tag && $tag->getType() == $type) {

                    // call the load() method if it exists to reinitialize the data (eg. from serializing, ...)
                    if(method_exists($tag, "load")) {
                        $tag->load();
                    }

                    // set view & controller, editmode
                    $tag->setController($this->controller);
                    $tag->setView($this);
                    $tag->setEditmode($this->editmode);
                    
                    $tag->setOptions($options);
                }
                else {
                    $tag = Model\Document\Tag::factory($type, $name, $document->getId(), $options, $this->controller, $this, $this->editmode);
                    $document->setElement($name, $tag);
                }

                // set the real name of this editable, without the prefixes and suffixes from blocks and areablocks
                $tag->setRealName($realName);
            }
            
            return $tag;
        }
        catch (\Exception $e) {
            \Logger::warning($e);
        }
    }

    /**
     * @param string $script
     */
    public function includeTemplateFile($script){
        $showTemplatePaths = isset($_REQUEST["pimcore_show_template_paths"]);
        if($showTemplatePaths && \Pimcore::inDebugMode()){
            echo "\n<!-- start template inclusion: " . $script . " -->\n";
        }
        include($script);
        if($showTemplatePaths && \Pimcore::inDebugMode()){
            echo "\n<!-- finished template inclusion: " . $script . " -->\n";
        }
    }

    /**
     * @param $scriptPath
     * @param array $params
     * @param bool $resetPassedParams
     * @param bool $capture
     * @return string
     */
    public function template($scriptPath, $params = array(), $resetPassedParams = false, $capture = false) {

        foreach ($params as $key => $value) {
            $this->assign($key, $value);
        }

        if($capture){
            $captureKey = (is_string($capture)) ? $capture : 'pimcore_capture_template';
            $this->placeholder($captureKey)->captureStart(\Zend_View_Helper_Placeholder_Container_Abstract::SET);
        }

        $found = false;
        $paths = $this->getScriptPaths();
        $paths[] = PIMCORE_DOCUMENT_ROOT;

        foreach ($paths as $path) {
            $p = $path . $scriptPath;
            if (is_file($p) && !$found) {
                $found = true;
                $this->includeTemplateFile($p);
                break;
            }
        }

        if(!$found) {
            if(is_file($scriptPath)) {
                $found = true;
                $this->includeTemplateFile($scriptPath);
            }
        }

        if($resetPassedParams){
            foreach($params as $key => $value){
                $this->$key = null;
            }
        }

        if($capture){
            $this->placeholder($captureKey)->captureEnd();
            return trim($this->placeholder($captureKey)->getValue());
        }
    }

    /**
     * includes a document
     *
     * @param $include
     * @param array $params
     * @return string
     */
    public function inc($include, $params = null, $cacheEnabled = true) {

        if(!is_array($params)) {
            $params = [];
        }

        // check if output-cache is enabled, if so, we're also using the cache here
        $cacheKey = null;
        $cacheConfig = false;

        if($cacheEnabled) {
            if($cacheConfig = Tool\Frontend::isOutputCacheEnabled()) {

                // cleanup params to avoid serializing Element\ElementInterface objects
                $cacheParams = $params;
                $cacheParams["~~include-document"] = $include;
                array_walk($cacheParams, function (&$value, $key) {
                    if($value instanceof Element\ElementInterface) {
                        $value = $value->getId();
                    } else if (is_object($value) && method_exists($value, "__toString")) {
                        $value = (string) $value;
                    }
                });

                $cacheKey = "tag_inc__" . md5(serialize($cacheParams));
                if($content = Cache::load($cacheKey)) {
                    return $content;
                }
            }
        }


        $editmodeBackup = \Zend_Registry::get("pimcore_editmode");
        \Zend_Registry::set("pimcore_editmode", false);

        $includeBak = $include;

        // this is if $this->inc is called eg. with $this->href() as argument
        if(!$include instanceof Model\Document\PageSnippet && is_object($include) && method_exists($include, "__toString")) {
            $include = (string) $include;
        }

        if (is_string($include)) {
            try {
                $include = Model\Document::getByPath($include);
            }
            catch (\Exception $e) {
                $include = $includeBak;
            }
        }
        else if (is_numeric($include)) {
            try {
                $include = Model\Document::getById($include);
            }
            catch (\Exception $e) {
                $include = $includeBak;
            }
        }

        $params = array_merge($params, array("document" => $include));
        $content = "";

        if ($include instanceof Model\Document\PageSnippet && $include->isPublished()) {
            if ($include->getAction() && $include->getController()) {
                $content = $this->action($include->getAction(), $include->getController(), $include->getModule(), $params);
            } else if ($include->getTemplate()) {
                $content = $this->action("default", "default", null, $params);
            }

            // in editmode, we need to parse the returned html from the document include
            // add a class and the pimcore id / type so that it can be opened in editmode using the context menu
            // if there's no first level HTML container => add one (wrapper)
            if($this->editmode) {

                include_once("simple_html_dom.php");

                $editmodeClass = " pimcore_editable pimcore_tag_inc ";

                // this is if the content that is included does already contain markup/html
                // this is needed by the editmode to highlight included documents
                if($html = str_get_html($content)) {
                    $childs = $html->find("*");
                    if(is_array($childs)) {
                        foreach ($childs as $child) {
                            $child->class = $child->class . $editmodeClass;
                            $child->pimcore_type = $include->getType();
                            $child->pimcore_id = $include->getId();
                        }
                    }
                    $content = $html->save();

                    $html->clear();
                    unset($html);
                } else {
                    // add a div container if the include doesn't contain markup/html
                    $content = '<div class="' . $editmodeClass . '" pimcore_id="' . $include->getId() . '" pimcore_type="' . $include->getType() . '">' . $content . '</div>';
                }
            }

            // we need to add a component id to all first level html containers
            $componentId = "";
            if($this->document instanceof Model\Document) {
                $componentId .= 'document:' . $this->document->getId() . '.';
            }
            $componentId .= 'type:inc.name:' . $include->getId();;
            $content = \Pimcore\Tool\Frontend::addComponentIdToHtml($content, $componentId);
        }

        \Zend_Registry::set("pimcore_editmode", $editmodeBackup);

        // write contents to the cache, if output-cache is enabled
        if($cacheConfig) {
            Cache::save($content, $cacheKey, array("output", "output_inline"), $cacheConfig["lifetime"]);
        }

        return $content;
    }

    /**
     * @param $key
     * @param $default
     * @return mixed
     */
    public function getParam($key, $default = null) {
        $value = $this->getRequest()->getParam($key);
        if ((null === $value || '' === $value) && (null !== $default)) {
            $value = $default;
        }

        return $value;
    }

    /**
     * @param $key
     * @param null $default
     * @return mixed
     */
    public function _getParam($key, $default = null) {
        return $this->getParam($key, $default);
    }

    /**
     * @return array
     */
    public function getAllParams () {
        return $this->getRequest()->getParams();
    }

    /**
     * @deprecated
     * @return array
     */
    public function _getAllParams () {
        return $this->getAllParams();
    }

    /**
     * @return \Zend_Controller_Request_Http
     */
    public function getRequest() {
        return $this->request;
    }

    /**
     * @param \Zend_Controller_Request_Abstract $request
     * @return void
     */
    public function setRequest(\Zend_Controller_Request_Abstract $request) {
        $this->request = $request;
        return $this;
    }

    /**
     * shorthand for $this->translate() view helper
     */
    public function t() {
        return call_user_func_array(array($this, "translate"), func_get_args());
    }

    /**
     * shorthand for $this->translateAdmin() view helper
     */
    public function ts() {
        return call_user_func_array(array($this, "translateAdmin"), func_get_args());
    }

    /**
     * @param string $method
     * @param array $arguments
     * @return mixed|Model\Document\Tag|string
     * @throws \Exception
     */
    public function __call($method, $arguments) {

        $class = "\\Pimcore\\Model\\Document\\Tag\\" . ucfirst(strtolower($method));

        $classFound = true;
        if(!\Pimcore\Tool::classExists($class)) {
            $oldStyleClass = "Document_Tag_" . ucfirst(strtolower($method));
            if(!\Pimcore\Tool::classExists($oldStyleClass)) {
                $classFound = false;
            }
        }

        if ($classFound) {
            if(!isset($arguments[0])) {
                throw new \Exception ("You have to set a name for the called tag (editable): " . $method);
            }

            // set default if there is no editable configuration provided
            if(!isset($arguments[1])) {
                $arguments[1] = array();
            }
            return $this->tag($method, $arguments[0], $arguments[1]);
        }

        if ($this->document instanceof Model\Document) {
            if (method_exists($this->document, $method)) {
                return call_user_func_array(array($this->document, $method), $arguments);
            }
        }

        return parent::__call($method, $arguments);
    }

    /**
     * @static
     * @return string
     */
    public static function getViewScriptSuffix () {
        // default is php
        $viewSuffix = "php";

        // custom view suffixes are only available for the frontend module (website)
        if(\Zend_Controller_Front::getInstance()->getRequest()->getModuleName() == PIMCORE_FRONTEND_MODULE) {
            $customViewSuffix = Config::getSystemConfig()->general->viewSuffix;
            if(!empty($customViewSuffix)) {
                $viewSuffix = $customViewSuffix;
            }
        }

        return $viewSuffix;
    }

    /**
     * @return boolean
     */
    public static function addComponentIds()
    {
        return self::$addComponentIds;
    }

    /**
     * @param boolean $addComponentIds
     */
    public static function setAddComponentIds($addComponentIds)
    {
        self::$addComponentIds = $addComponentIds;
    }

}
