<?php

namespace engine\skin\TwigAddons;

// Based on https://stackoverflow.com/questions/26170727/how-to-create-a-twig-custom-tag-that-executes-a-callback

class JsTagTokenParser extends \Twig\TokenParser\AbstractTokenParser
{
    public function parse(\Twig\Token $token) {
        $lineno = $token->getLine();
        $stream = $this->parser->getStream();

        // recovers all inline parameters close to your tag name
        $params = array_merge([], $this->getInlineParams($token));

        $continue = true;
        while ($continue) {
            // create subtree until the decideJsTagFork() callback returns true
            $body = $this->parser->subparse($this->decideJsTagFork(...));

            // I like to put a switch here, in case you need to add middle tags, such
            // as: {% js %}, {% nextjs %}, {% endjs %}.
            $tag = $stream->next()->getValue();
            switch ($tag) {
                case 'endjs':
                    $continue = false;
                    break;
                default:
                    throw new \Twig\Error\SyntaxError(sprintf(
                        'Unexpected end of template. Twig was looking for the ' .
                        'following tags "endjs" to close the "js" block started ' .
                        'at line %d)',
                        $lineno,
                    ), -1);
            }

            // you want $body at the beginning of your arguments
            array_unshift($params, $body);

            // if your endjs can also contains params, you can uncomment this line:
            // $params = array_merge($params, $this->getInlineParams($token));
            // and comment this one:
            $stream->expect(\Twig\Token::BLOCK_END_TYPE);
        }

        return new JsTagNode(['params' => new JsTagParamsNode($params)], [], $lineno);
    }

    /**
     * Recovers all tag parameters until we find a BLOCK_END_TYPE ( %} )
     *
     * @param \Twig\Token $token
     * @return \Twig\Node\Expression\AbstractExpression[]
     */
    protected function getInlineParams(\Twig\Token $token) {
        $stream = $this->parser->getStream();
        $params = array ();
        while (!$stream->test(\Twig\Token::BLOCK_END_TYPE)) {
            $params[] = $this->parser->getExpressionParser()->parseExpression();
        }
        $stream->expect(\Twig\Token::BLOCK_END_TYPE);
        return $params;
    }

    /**
     * Callback called at each tag name when subparsing, must return
     * true when the expected end tag is reached.
     *
     * @param \Twig\Token $token
     * @return bool
     */
    public function decideJsTagFork(\Twig\Token $token) {
        return $token->test(['endjs']);
    }

    /**
     * Your tag name: if the parsed tag match the one you put here, your parse()
     * method will be called.
     *
     * @return string
     */
    public function getTag() {
        return 'js';
    }
}
