<?php
namespace Backend\BaseBundle\Twig\Extension;

use Backend\BaseBundle\Event\TwigRenderEvent;
use Backend\BaseBundle\Guesser\ControllerNameGuesser;
use JMS\DiExtraBundle\Annotation\Observe;
use JMS\DiExtraBundle\Annotation\Service;
use JMS\DiExtraBundle\Annotation\Tag;
use JMS\DiExtraBundle\Annotation\Inject;
use JMS\DiExtraBundle\Annotation\InjectParams;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
use Symfony\Component\Form\FormView;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\Router;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Backend\BaseBundle\Model;
use Symfony\Component\Translation\TranslatorInterface;

/**
 * @Service
 * @Tag("twig.extension", attributes = {"public": false})
 */
class BackendTwigExtension extends \Twig_Extension
{
    /** @var  ExpressionLanguage */
    protected $language;

    /** @var  TokenStorageInterface */
    protected $tokenStorage;

    /** @var  Router */
    protected $router;

    /** @var  RequestStack */
    protected $requestStack;

    /** @var  TranslatorInterface */
    protected $translator;

    /** @var  EventDispatcherInterface */
    protected $eventDispatcher;

    /**
     * @InjectParams({
     *    "language" = @Inject("security.expression_language"),
     *    "tokenStorage" = @Inject("security.token_storage"),
     *    "translator" = @Inject("translator.default"),
     * })
     */
    public function injectService(ExpressionLanguage $language, TokenStorageInterface $tokenStorage, Router $router, RequestStack $requestStack, TranslatorInterface $translator, EventDispatcherInterface $eventDispatcher)
    {
        $this->language = $language;
        $this->tokenStorage = $tokenStorage;
        $this->router = $router;
        $this->requestStack = $requestStack;
        $this->translator = $translator;
        $this->eventDispatcher = $eventDispatcher;
    }

    public function getFunctions()
    {
        return array(
            new \Twig_SimpleFunction('has_role_or_superadmin', array($this, 'hasRoleOrSuperAdmin')),
            new \Twig_SimpleFunction('fetch_relation', array($this, 'fetchRelation')),
            new \Twig_SimpleFunction('print_r', array($this, 'print_r')),
            new \Twig_SimpleFunction('has_prefix', array($this, 'hasPrefix')),
            new \Twig_SimpleFunction('render_object', array($this, 'renderObject')),
            new \Twig_SimpleFunction('render_value', array($this, 'renderValue')),
            new \Twig_SimpleFunction('is_empty_value', array($this, 'isEmptyValue')),
        );
    }

    public function getFilters()
    {
        return array(
            new \Twig_SimpleFilter('filter_prefix', array($this, 'filterPrefix')),
            new \Twig_SimpleFilter('guess_route', array($this, 'guessRoute')),
            new \Twig_SimpleFilter('space', array($this, 'space'), array('pre_escape' => 'html', 'is_safe' => array('html'))),
            new \Twig_SimpleFilter('breadcrumb_guesser', array($this, 'breadcrumbGuesser')),
            new \Twig_SimpleFilter('count_children_error', array($this, 'countChildrenError')),
        );
    }

    public function countChildrenError(FormView $formView)
    {
        $errors = 0;
        foreach($formView->children as $child){
            $errors += count($child->vars['errors']);
        }
        return $errors;
    }

    public function isEmptyValue($value)
    {
        if($value instanceof \Traversable){
            return $this->isArrayEmptyValue(iterator_to_array($value));
        }

        if(is_array($value)){
            return $this->isArrayEmptyValue($value);
        }

        return $value === '' || $value === null || $value === false;
    }

    protected function isArrayEmptyValue($array)
    {
        if(empty($array)){
            return true;
        }

        foreach($array as $value){
            if($value){
                return false;
            }
        }

        return true;
    }

    public function renderValue($value)
    {
        if($value instanceof \DateTime){
            return $value->format('Y-m-d H:i:s');
        }

        return (string) $value;
    }

    public function renderObject($value)
    {
        $event = new TwigRenderEvent($value);
        $this->eventDispatcher->dispatch(TwigRenderEvent::EVENT_TWIG_RENDER_EVENT, $event);
        return $event->getRendered();
    }

    /**
     * @Observe(TwigRenderEvent::EVENT_TWIG_RENDER_EVENT, priority=0)
     */
    public function defaultObjectRenderer(TwigRenderEvent $event)
    {
        $value = $event->getValue();
        $event->setRendered(array(
            'type' => 'text',
            'value' => $this->print_r($value),
        ));
    }

    public function hasPrefix($columnName, $prefix)
    {
        if(preg_match('/^([\!\*@\#]+)/i', $columnName, $match)){
            return strpos($match[1], $prefix) !== false;
        }
        return false;
    }

    public function filterPrefix($columnName)
    {
        if(preg_match('/([0-9a-z_:]+)/i', $columnName, $match)){
            return $match[1];
        }
        return '';
    }

    public function space($value)
    {
        return str_replace(' ', '&nbsp;', $value);
    }

    public function print_r($value)
    {
        return print_r($value, true);
    }

    public function fetchRelation(\BaseObject $object, $columnName, $needTrans = false)
    {
        $value = $object;
        foreach(explode(':', $columnName) as $column){
            $value = call_user_func(array($value, "get$column"));
            if(!is_object($value)){
                break;
            }
        }

        if($needTrans){
            $peer = $object->getPeer();
            $tableName = $peer::TABLE_NAME;
            $fieldName = $peer::translateFieldName($columnName, \BasePeer::TYPE_PHPNAME, \BasePeer::TYPE_FIELDNAME);
            $value = $this->translator->trans("index_value.$tableName.$fieldName.$value", array(), 'forms');
        }
        return $value;
    }

    public function guessRoute($route)
    {
        if($this->router->getRouteCollection()->get($route) != null){
            return $route;
        }

        list($controllerName, $actionName) = explode('::', $this->requestStack->getCurrentRequest()->attributes->get('_controller'));
        $guesser = new ControllerNameGuesser($controllerName);
        return $guesser->getActionRouteName($route);
    }

    public function hasRoleOrSuperAdmin($role)
    {
        $user = $this->tokenStorage->getToken()->getUser();

        if(!$user instanceof Model\SiteUser){
            return false;
        }

        return $this->language->evaluate("has_role_or_superadmin('$role')", array(
            'user' => $user,
            'roles' => $user->getRoles(),
        ));
    }

    public function breadcrumbGuesser($controller)
    {
        list($controllerName, $actionName) = explode('::', $controller);
        $guesser = new ControllerNameGuesser($controllerName);
        $name = $guesser->getBundleName().$guesser->getShortName();
        return $name;
    }

    public function getName()
    {
        return 'ExpressionLanguageBridge';
    }
}