<?php

namespace Velis\Mvc\View\Engine;

use Phalcon\Di\DiInterface;
use Phalcon\Mvc\View\Engine\AbstractEngine;
use Phalcon\Mvc\ViewBaseInterface;
use Smarty;
use SmartyException;
use Velis\App;
use Velis\Mvc\View\SmartyResourceIoncube;
use Velis\Output;

/**
 * Extension of Phalcon templating engines
 *
 * @author Olek Procki <olo@velis.pl>
 * @author Jan Małysiak <jan.malysiak@velis.pl>
 *
 * @property ViewBaseInterface $view
 */
class SmartyEngine extends AbstractEngine implements EngineInterface
{
    private Smarty $smarty;

    /**
     * Class constructor.
     *
     * @param ViewBaseInterface $view
     * @param DiInterface $di
     */
    public function __construct(ViewBaseInterface $view, DiInterface $di)
    {
        $this->smarty = new Smarty();
        $this->smarty->setTemplateDir('.');
        $this->smarty->setCompileDir(DATA_PATH . 'smarty-compiled');
        $this->smarty->setDefaultResourceType('ioncube');
        $this->smarty->registerResource('ioncube', new SmartyResourceIoncube());

        $this->smarty->compile_check = !in_array(APP_ENV, [App::ENV_PRODUCTION, App::ENV_RELEASE]);

        foreach (array_slice(scandir(MODULE_PATH), 2) as $module) {
            if (file_exists(MODULE_PATH . $module . '/view/plugin')) {
                $this->smarty->addPluginsDir(MODULE_PATH . $module . '/view/plugin');
            }
        }

        $this->smarty->assign('this', $view);
        $di['SmartyRenderer'] = $this;

        foreach (App::$config->viewAlias as $alias => $class) {
            $this->smarty->registerClass($alias, $class);
        }

        parent::__construct($view, $di);
    }

    /**
     * {@inheritdoc}
     * @throws SmartyException
     */
    public function render($path, $params, $mustClean = null)
    {
        if (!isset($params['content'])) {
            $params['content'] = $this->getViewContent();
        }

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

        $this->setViewContent($this->smarty->fetch($path));
    }

    /**
     * Return view output
     * @return string
     */
    protected function getViewContent(): string
    {
        return $this->view->getContent();
    }

    /**
     * Set the view content
     * @param string $content
     */
    protected function setViewContent(string $content): void
    {
        $this->view->setContent($content);
    }

    /**
     * Set Smarty's options
     * @param array $options
     */
    public function setOptions(array $options)
    {
        foreach ($options as $key => $value) {
            $setter = 'set' . str_replace(' ', '', ucwords(str_replace('_', ' ', $key)));

            if (method_exists($this->smarty, $setter)) {
                $this->smarty->$setter($value);
            } elseif (property_exists($this->smarty, $key)) {
                $this->smarty->$key = $value;
            }
        }
    }

    /**
     * Include element in template
     *
     * @param string $name
     * @param string $module
     * @param mixed $data
     * @return string
     * @throws SmartyException
     */
    public function includeElement(string $name, string $module = 'application', $data = null): string
    {
        $file = MODULE_PATH . ucfirst(Output::toPascalCase($module)) . '/view/element/' . $name . '.tpl';

        if ($data !== null) {
            $this->assign('_' . lcfirst(Output::toPascalCase($name)), $data);

            if (is_array($data)) {
                foreach ($data as $key => $value) {
                    if (is_string($key)) {
                        $this->smarty->assign($key, $value);
                    }
                }
            }
        }

        return $this->smarty->fetch($file, null, null, $this->smarty);
    }

    /**
     * {@inheritDoc}
     */
    public function assign($spec, $value = null): void
    {
        if (is_array($spec)) {
            $this->smarty->assign($spec);
        } else {
            $this->smarty->assign($spec, $value);
        }
    }

    /**
     * {@inheritDoc}
     */
    public function register($class, $alias): EngineInterface
    {
        $this->smarty->registerClass($alias, $class);

        return $this;
    }

    /**
     * Clear all assigned variables
     *
     * Clears all variables assigned to Zend_View either via
     * {@link assign()} or property overloading
     * ({@link __get()}/{@link __set()}).
     *
     * @return void
     */
    public function clearVars()
    {
        $this->smarty->clearAllAssign();
    }

    /**
     * {@inheritDoc}
     * @throws SmartyException
     */
    public function fetch($name)
    {
        return $this->smarty->fetch($name);
    }

    /**
     * {@inheritDoc}
     */
    public function getVars($varName = null)
    {
        return $this->smarty->getTemplateVars($varName);
    }

    /**
     * {@inheritDoc}
     */
    public function setTemplateDir($templateDir): EngineInterface
    {
        $this->smarty->setTemplateDir($templateDir);

        return $this;
    }
}
