<?php
/**
 * core.framework
 *
 * @category   Core
 * @package    Core_Router
 * @subpackage UnitTests
 * @copyright  Copyright (c) 2011. Burza d.o.o. (http://web.burza.hr/en/)
 * @license    proprietary
 */

/**
 * @category   Core
 * @package    Core_Router
 * @subpackage UnitTests
 * @copyright  Copyright (c) 2011. Burza d.o.o. (http://web.burza.hr/en/)
 * @license    proprietary
 * @group      Core_Router
 * @group      Core_Router_Route
 */
class Core_Router_Route_TokenTest extends PHPUnit_Framework_TestCase
{
    /**
     * @var Core_Router_Route_Token
     */
    protected $_object;

    protected function setUp()
    {
        $this->_object = new Core_Router_Route_Token;
    }

    /**
     * @covers Core_Router_Route_Token::getRegex
     * @covers Core_Router_Route_Token::<protected>
     */
    public function testEmptyPatternWillProduceAnEmptyRegex()
    {
        $this->_object->setPattern('');
        $this->assertEquals('^/$', $this->_object->getRegex());
    }

    /**
     * @covers Core_Router_Route_Token::getRegex
     */
    public function testWillCreateRegexFromPattern()
    {
        $this->_object->setPattern('/site');
        $this->assertEquals('^/site/?$', $this->_object->getRegex());
    }

    /**
     * @covers Core_Router_Route_Token::getRegex
     * @covers Core_Router_Route_Token::<protected>
     */
    public function testCanProcessPatternsWithVariables()
    {
        $this->_object->setPattern('/site/{id}');
        $this->assertEquals('^/site/(?P<id>[^/]+)/?$', $this->_object->getRegex());
    }

    /**
     * @covers Core_Router_Route_Token::path
     */
    public function testCanGenerateUrl()
    {
        $this->_object->setPattern('/site');
        $this->assertEquals('/site/', $this->_object->path('/'));
    }

    /**
     * @covers Core_Router_Route_Token::path
     */
    public function testCanGenerateUrlWithVariables()
    {
        $this->_object->setPattern('/site/{id}');
        $this->assertEquals('/site/23/', $this->_object->path('/', array('id' => 23)));
    }

    /**
     * @covers Core_Router_Route_Token::path
     * @covers Core_Router_Route_Token::<protected>
     */
    public function testCanGenerateUrlWithOptionalVariables()
    {
        $this->_object->setPattern('/site/{+id}');
        $this->assertEquals('/site/', $this->_object->path('/'));
    }

    /**
     * @covers Core_Router_Route_Token::match
     */
    public function testWillDisregardCaseWhenMatchingRoute()
    {
        $this->_object->setPattern('/info/{+page}');
        $this->assertTrue($this->_object->match('/INFO/IMPRESSUM/', '/'));
    }

    /**
     * @covers Core_Router_Route_Token::match
     */
    public function testCanSpecifyVariableType()
    {
        $this->_object->setPattern('/page/{+id:\d+}');
        $this->assertTrue($this->_object->match('/page/23/', '/'));
    }

    /**
     * @covers Core_Router_Route_Token::match
     */
    public function testWillMatchIfSpecifiedVariableTypeAndNoLeadingSlash()
    {
        $this->_object->setPattern('/page/{id:\d}');
        $this->assertTrue($this->_object->match('/page/23', '/'));
    }

    /**
     * @covers Core_Router_Route_Token::match
     */
    public function testWillMatchWithoutLastOptionalVariableSpecifiedAndNoTrailingSlash()
    {
        $this->_object->setPattern('/page/{+id}/');
        $this->assertTrue($this->_object->match('/page/23', '/'));
    }

    /**
     * @covers Core_Router_Route_Token::match
     * @covers Core_Router_Route_Token::<protected>
     */
    public function testCanHaveStaticHostname()
    {
        $this->_object
            ->setPattern('/abc')
            ->setHostname('http://dkarlovi.example.com');

        $this->assertTrue($this->_object->match('/abc', '/', null, 'http://dkarlovi.example.com'));
    }

    /**
     * @covers Core_Router_Route_Token::match
     * @covers Core_Router_Route_Token::<protected>
     */
    public function testCanIncludeVariableInHostname()
    {
        $this->_object
            ->setPattern('/abc')
            ->setHostname('http://{username}.example.com');

        $this->assertTrue($this->_object->match('/abc', '/', null, 'http://dkarlovi.example.com'));
    }

    /**
     * @covers Core_Router_Route_Token::match
     * @covers Core_Router_Route_Token::<protected>
     */
    public function testWillExtractVariableFromHostname()
    {
        $this->_object
            ->setPattern('/abc')
            ->setHostname('http://{username}.example.com');
        $this->_object->match('/abc', '/', null, 'http://dkarlovi.example.com');
        $values = $this->_object->getValues();

        $this->assertArrayHasKey('username', $values);
        $this->assertEquals('dkarlovi',      $values['username']);
    }

    /**
     * @covers Core_Router_Route_Token::match
     * @covers Core_Router_Route_Token::<protected>
     */
    public function testWillExtractVariableFromHostnameAndPath()
    {
        $this->_object
            ->setPattern('/abc/{param}')
            ->setHostname('http://{username}.example.com');
        $this->_object->match('/abc/value1', '/', null, 'http://dkarlovi.example.com');
        $values = $this->_object->getValues();

        $this->assertEquals('dkarlovi', $values['username']);
        $this->assertEquals('value1',   $values['param']);
    }

    /**
     * @covers Core_Router_Route_Regex::url
     * @covers Core_Router_Route_Regex::<protected>
     */
    public function testWillProcessHostnameVariablesWhenBuilingUrl()
    {
        $this->_object
            ->setPattern('/abc/{param}')
            ->setHostname('http://{username}.example.com');

        $this->assertEquals(
            'http://dkarlovi.example.com/abc/value1/',
            $this->_object->url('/', array('username' => 'dkarlovi', 'param' => 'value1'))
        );
    }

    /**
     * @param string $hostname    Route hostname
     * @param string $pattern     Route pattern
     * @param string $expectedUrl URL expected from the route to produce
     * @param array  $params      Route params, used for generating the path and expected to pull from the path
     *
     * @covers Core_Router_Route_Token::url
     * @covers Core_Router_Route_Token::match
     * @covers Core_Router_Route_Token::<protected>
     * @dataProvider routeToPath
     */
    public function testRouteWillMatchThePathItProduces($hostname, $pattern, $expectedUrl, $params = array())
    {
        $this->_object
            ->setHostname($hostname)
            ->setPattern($pattern);

        $url = $this->_object->url('/', $params);

        // path should match the expected path
        $this->assertEquals($expectedUrl, $url);

        // the route matches the path it created
        $tokens = parse_url($url);
        $this->assertTrue($this->_object->match($tokens['path'], '/', null, $tokens['scheme'] .'://'. $tokens['host']));

        // params are retained
        $this->assertEquals($params, $this->_object->getValues());
    }

    public function routeToPath()
    {
        // null, pattern, expected url, params = array()
        return array(
            array(
                'http://example.com',
                '/',
                'http://example.com/'
            ),
            array(
                'http://example.com',
                '/a/',
                'http://example.com/a/'
            ),
            array(
                'http://example.com',
                '/a/{+id}',
                'http://example.com/a/'
            ),
            array(
                'http://example.com',
                '/a/{id}',
                'http://example.com/a/23/',
                array('id' => 23)
            ),
            array(
                'http://example.com',
                '/a/{id}',
                'http://example.com/a/neki-alias/',
                array('id' => 'neki-alias')
            ),
            array(
                'http://example.com',
                '/{+preview}/a/{id}',
                'http://example.com/a/23/',
                array('id' => 23, 'preview' => false)
            ),
            array(
                'http://example.com',
                '/{+preview}/a/{id}',
                'http://example.com/preview/a/23/',
                array('id' => 23, 'preview' => 'preview')
            ),
            array(
                'http://example.com',
                '/{category}/{id}/',
                'http://example.com/clanak/foo/',
                array('category' => 'clanak', 'id' => 'foo')
            ),

            array(
                'http://example.com',
                '/{+category:.}',
                'http://example.com/'
            ),
            array(
                'http://example.com',
                '/{category:.}',
                'http://example.com/vijesti/sport/',
                array('category' => 'vijesti/sport')
            ),
            array(
                'http://example.com',
                '/{category:.}/',
                'http://example.com/vijesti/sport/',
                array('category' => 'vijesti/sport')
            ),
            array(
                'http://example.com',
                '/{category:.}/{+id}',
                'http://example.com/vijesti/sport/',
                array('category' => 'vijesti/sport')
            ),
            array(
                'http://example.com',
                '/{category:.}/{+id}/',
                'http://example.com/vijesti/sport/',
                array('category' => 'vijesti/sport')
            ),
            array(
                'http://example.com',
                '/{category:.}/{+id:\d}',
                'http://example.com/vijesti/sport/',
                array('category' => 'vijesti/sport')
            ),
            array(
                'http://example.com',
                '/{category:.}/{+id:\d}/',
                'http://example.com/vijesti/sport/',
                array('category' => 'vijesti/sport')
            ),
            array(
                'http://example.com',
                '/{category:.}/{id}',
                'http://example.com/vijesti/sport/23/',
                array('category' => 'vijesti/sport', 'id' => 23)
            ),
            array(
                'http://example.com',
                '/{category:.}/{id}/',
                'http://example.com/vijesti/sport/23/',
                array('category' => 'vijesti/sport', 'id' => 23)
            ),
            array(
                'http://example.com',
                '/{category:.}/{id:\d}',
                'http://example.com/vijesti/sport/23/',
                array('category' => 'vijesti/sport', 'id' => 23)
            ),
            array(
                'http://example.com',
                '/{category:.}/{id:\d}/',
                'http://example.com/vijesti/sport/23/',
                array('category' => 'vijesti/sport', 'id' => 23)
            ),
            array(
                'http://example.com',
                '/{category:.}/{id:.}/',
                'http://example.com/vijesti/sport/23/',
                array('category' => 'vijesti/sport', 'id' => 23)
            ),

            // hostname with params
            array(
                'http://{username}.example.com',
                '/abc',
                'http://dkarlovi.example.com/abc/',
                array('username' => 'dkarlovi')
            ),
            array(
                'http://{username}.{name}.com',
                '/abc',
                'http://dkarlovi.foo.com/abc/',
                array('username' => 'dkarlovi', 'name' => 'foo')
            ),
            array(
                'http://{username}.{name}.com',
                '/{param}',
                'http://dkarlovi.foo.com/abc/',
                array('username' => 'dkarlovi', 'name' => 'foo', 'param' => 'abc')
            ),
            array(
                'http://{username}.{+name}.com',
                '/{param}',
                'http://dkarlovi.com/abc/',
                array('username' => 'dkarlovi', 'param' => 'abc')
            ),
            array(
                'http://{username}.{+name}.com',
                '/{param}',
                'http://dkarlovi.foo.com/abc/',
                array('username' => 'dkarlovi', 'name' => 'foo', 'param' => 'abc')
            ),
            array(
                'http://{domain:.}',
                '/{param}',
                'http://dkarlovi.foo.com/abc/',
                array('domain' => 'dkarlovi.foo.com', 'param' => 'abc')
            ),
            // TODO
            /*
            array(
                'http://{code:(?!www|api).}.example.com',
                '/{param}',
                'http://foo.example.com/abc/',
                array('code' => 'foo', 'param' => 'abc')
            ),
            */
        );
    }
}
