<?php

namespace Velis\Mvc;

use BadMethodCallException;
use Laminas\View\Model\ViewModel;
use Phalcon\Mvc\View as PhalconView;
use Velis\App;
use Velis\Filter;
use Velis\Mvc\View\Engine\EngineInterface;
use Velis\Output;

/**
 * MVC view
 * @author Olek Procki <olo@velis.pl>
 */
class View extends PhalconView
{
    /**
     * @var string
     */
    protected $_pageTitle;


    /**
     * Helper objects
     * @var array
     */
    protected $_helpers;


    /**
     * Smarty renderer engine
     * @var EngineInterface
     */
    protected $_smartyRenderer;


    /**
     * Template context
     * @var string
     */
    protected $_context;


    /**
     * Scripts buffer
     * @var string
     */
    protected $_scripts;


    /**
     * Breadcrumbs links
     * @var array
     */
    protected $_breadcrumbsPages = array();

    /**
     * Sets smarty renderer engine
     */
    public function setSmartyEngine(EngineInterface $engine): void
    {
        $this->_smartyRenderer = $engine;
    }

    /**
     * Sets template rendering context
     * @param string $context
     */
    public function setContext($context): void
    {
        $this->_context = $context;
    }

    /**
     * Sets title for current page
     *
     * @param string $title
     * @param bool $suppressHeader
     */
    public function setTitle($title, $suppressHeader = false): self
    {
        $this->_pageTitle = $title;

        if ($suppressHeader) {
            $this->_suppressHeader = 1;
        }

        return $this;
    }

    /**
     * Returns page title with default application name
     *
     * @param bool $includePostfix
     * @return string
     */
    public function getTitle($includePostfix = false)
    {
        $title = '';

        if (!$includePostfix) {
            return $this->_pageTitle;
        }

        if ($this->_pageTitle) {
            $title .= $this->_pageTitle . ' - ' . App::$config->layout->title;
        } else {
            $title = App::$config->layout->title;
        }

        return $title;
    }

    /**
     * Sets view to render action template only
     */
    public function setBlank(): self
    {
        $this->setRenderLevel(self::LEVEL_ACTION_VIEW);

        return $this;
    }

    /**
     * Sets application to render json instead of HTML template
     * (used with frontend app mode)
     *
     * @param bool $json
     */
    public function json($json = true): void
    {
        $this->initFrontendApp = $json;
    }


    /**
     * Returns all template vars
     * @return array
     */
    public function getData()
    {
        return $this->_smartyRenderer->getVars();
    }


    /**
     * Sets selected menu
     *
     * @param string $mainMenuOption
     * @param string $subMenuOption
     *
     * @return $this
     */
    public function selectMenu($mainMenuOption, $subMenuOption = null, $thirdMenuLevel = null)
    {
        $this->menuSelected = [
            'main'  => $mainMenuOption,
            'sub'   => $subMenuOption,
            'third' => $thirdMenuLevel,
        ];

        return $this;
    }


    /**
     * Returns selected main menu
     * @return string
     */
    public function getMainMenu()
    {
        return $this->menuSelected['main'];
    }


    /**
     * Returns selected submenu
     * @return string
     */
    public function getSubMenu()
    {
        return $this->menuSelected['sub'];
    }


    /**
     * Returns third level selected submenu
     * @return string
     */
    public function getThirdMenu()
    {
        return $this->menuSelected['third'];
    }


    /**
     * Adds script to scripts buffer
     * @param string $script
     */
    public function addScript($script)
    {
        $this->_scripts .= $script;
    }


    /**
     * Returns buffered scripts
     * @return string
     */
    public function getScriptsBuffer()
    {
        return $this->_scripts;
    }


    /**
     * Returns route url
     *
     * @param string $routeName
     * @param array $params
     *
     * @return string
     */
    public function url($routeName, array $params = array())
    {
        return App::getRouteUrl($routeName, $params);
    }


    /**
     * Returns true if ajax call
     *
     * @param bool $ignoreInstant pass true to handle instant request like regular one
     * @return bool
     */
    public function isAjax($ignoreInstant = false)
    {
        $request = App::getService('request');
        return $request->isAjax($ignoreInstant);
    }


    /**
     * Adds breadcrumb link
     *
     * @param string $label
     * @param string $uri
     * @return View
     */
    public function breadcrumb($label, $uri = null)
    {
        $this->_breadcrumbsPages[] = array('label' => $label, 'uri' => $uri);
        return $this;
    }

    /**
     * Clear breadcrumbs
     *
     * @return View
     */
    public function clearBreadcrumbs()
    {
        $this->_breadcrumbsPages = array();
        return $this;
    }


    /**
     * Returns assigned breadcrumbs links
     * @return array
     */
    public function getBreadcrumbsPages()
    {
        return $this->_breadcrumbsPages;
    }


    /**
     * Returns helper instance
     *
     * @param string $helperName
     * @return object
     */
    public function helper($helperName)
    {
        if (!$this->_helpers[$helperName]) {
            $class = '\Velis\Mvc\View\Helper\\' . ucfirst($helperName);
            if (class_exists($class)) {
                $this->_helpers[$helperName] = new $class();
            }
        }
        return $this->_helpers[$helperName];
    }


    /**
     * Renders template by path or controller/action pair
     *
     * @param string $controllerOrPath
     * @param string $action
     * @param array $params
     *
     * @return string
     */
    public function render($controllerOrPath, $action = null, $params = array())
    {
        if ($action === null && is_string($controllerOrPath) && strpos($controllerOrPath, '/')) {
            list($module, $controller, $action) = explode('/', $controllerOrPath);
            $template = MODULE_PATH . Output::toPascalCase($module) . '/view/' . $controllerOrPath;

            if (!Filter::endsWith($template, '.tpl')) {
                $template .= '.tpl';
            }

            return $this->_smartyRenderer->fetch($template);
        } else {
            if ($this->_context) {
                $action .= '.' . $this->_context;
            }

            if ($controllerOrPath instanceof ViewModel) {
                $model = $controllerOrPath;
                foreach ($model->getVariables() as $var => $val) {
                    $this->assign($var, $val);
                }
                return $this->render($model->getTemplate());
            } else {
                return parent::render(
                    Filter::filterToDash($controllerOrPath),
                    Filter::filterToDash($action),
                    $params
                );
            }
        }
    }


    /**
     * Engine method delegation wrapper
     *
     * @param string $name
     * @param array $arguments
     *
     * @return mixed
     */
    public function __call($name, $arguments)
    {
        if (method_exists($this->_smartyRenderer, $name)) {
            return call_user_func_array(array($this->_smartyRenderer, $name), $arguments);
        } elseif ($this->helper($name)) {
            $helper = $this->helper($name);
            return call_user_func_array(array($helper, '__invoke'), $arguments);
        } else {
            throw new BadMethodCallException(
                'Method ' . __CLASS__ . "::" . $name  . " doesn't exist"
            );
        }
    }
}
