<?php

class Wact_Php_WriterTestCase extends Wact_Test_Case {

    function testEmpty() {
        $code = new Wact_Php_Writer();
        $this->assertEqual($code->getCode(), '');
    }

    function testSwitchTo() {
        $code = new Wact_Php_Writer();
        $code->switchToHTML();
        $code->switchToPHP();
        $code->switchToPHP();
        $code->switchToHTML();
        $code->switchToHTML();
        $this->assertEqual($code->getCode(), '<' . '?php  ?' . '>');
    }

    function testRegisterFunction() {
        $code = new Wact_Php_Writer();

        $function = new Wact_Php_Writer_Function('render');
        $code->registerFunction($function);

        try {
            $code->registerFunction($function);
            $this->fail('Expected exception not thrown');
        } catch (Wact_Php_Writer_Exception $e) {
            $this->pass();
        }

        $function2 = $code->findFunction('render');
        $this->assertIdentical($function, $function2);

        $this->assertEqual($code->getCode(), '<' . '?php' . "\n" . 'function render() {' . "\n\n" . '}' . "\n\n" . '?' . '>');
    }

    function testRegisterClass() {
        $code = new Wact_Php_Writer();

        $class = new Wact_Php_Writer_Class('MyClass');
        $code->registerClass($class);

        try {
            $code->registerClass($class);
            $this->fail('Expected exception not thrown');
        } catch (Wact_Php_Writer_Exception $e) {
            $this->pass();
        }

        $class2 = $code->findClass('MyClass');
        $this->assertIdentical($class, $class2);

        $this->assertEqual($code->getCode(), '<' . '?php' . "\n" . 'class MyClass {' . "\n\n" . '}'. "\n\n\n" . '?' . '>');
    }

    function testScopeWrites() {
        $code = new Wact_Php_Writer();

        $class = new Wact_Php_Writer_Class('MyView');
        $code->registerClass($class);

        $method = new Wact_Php_Writer_Method('render');
        $class->registerMethod($method);

        $code->writePHP('if ($x = ');
        $code->writePHPLiteral('f\'o\'o');
        $code->writePHP(') { ');
        $code->writeHTML('bar');
        $code->writePHP('}');

        $body = 'if ($x = \'f\\\'o\\\'o\') {  ?' . '>bar<' . '?php }';
        $this->assertEqual($code->getCode(), '<' . '?php' . "\n" . 'class MyView {' . "\n\n" . 'function render() {' . "\n" . $body . "\n" . '}' . "\n\n" . '}' . "\n\n\n" . '?>');
    }

    function testSwitchScope() {
        $function = new Wact_Php_Writer_Function('init');

        $class = new Wact_Php_Writer_Class('MyView');

        $classMethod = new Wact_Php_Writer_Method('render');
        $class->registerMethod($classMethod);

        $code = new Wact_Php_Writer();
        $code->registerFunction($function);
        $code->registerClass($class);

        $code->switchToGlobalScope();
        $code->writeHTML('Global Scope');

        try {
            $code->switchToMethodScope('render');
            $this->fail('Expected exception not thrown');
        } catch (Wact_Php_Writer_Exception $e) {
            $this->pass();
        }

        $code->writeHTML('Still in Global Scope');

        $code->switchToClassMethodScope('MyView', 'render');
        $code->writePHP('echo 1;');

        $code->switchToFunctionScope('init');
        $code->writePHP('echo 2;');

        $code->switchToGlobalScope();
        $code->writeHTML('Global Scope Again');

        $st_global = 'Global Scope' . 'Still in Global Scope' . 'Global Scope Again';
        $st_initfunc = 'function init() {' . "\n" . 'echo 2;' . "\n" . '}';
        $st_myclass = 'class MyView {' . "\n\n" . 'function render() {' . "\n" . 'echo 1;' . "\n" . '}' . "\n\n" . '}';
        $this->assertEqual($code->getCode(), '<' . '?php' . "\n" . $st_myclass . "\n\n" . $st_initfunc . "\n\n" . '?' . '>' . $st_global);
    }

    function testSwitchScopeInvalidFunction() {
        $class = new Wact_Php_Writer_Class('MyView');

        $code = new Wact_Php_Writer();
        $code->registerClass($class);

        // this is a valid operation and shouldn't throw an exception
        $code->switchToClassScope('MyView');
        $this->pass();

        try {
            $code->switchToClassMethodScope('MyView2', 'render');
            $this->fail('Expected exception not thrown');
        } catch (Wact_Php_Writer_Exception $e) {
            $this->pass();
        }
    }

    function testSetScope() {
        $code = new Wact_Php_Writer();

        $code->createClass('MyView1');
        $v1 = $code->findClass('MyView1');

        $code->createClass('MyView2');
        $v2 = $code->findClass('MyView2');

        $scope = $code->setScope($v1);
        $this->assertIdentical($scope, $v2);

        $scope = $code->setScope($v2);
        $this->assertIdentical($scope, $v1);
    }

    function testSwitchScopeReturns() {
        $code = new Wact_Php_Writer();

        $code->createClass('MyClass');
        $class = $code->findClass('MyClass');

        $code->switchToGlobalScope(); // back to global scope
        $code->createFunction('MyFunc');
        $func = $code->findFunction('MyFunc');

        $code->switchToGlobalScope(); // back to global scope
        $globalScope1 = $code->switchToClassScope('MyClass');
        $classScope = $code->switchToFunctionScope('MyFunc');
        $funcScope = $code->switchToGlobalScope();
        $globalScope2 = $code->switchToFunctionScope('MyFunc');

        $this->assertIdentical($globalScope1, $globalScope2);
        $this->assertIdentical($classScope, $class);
        $this->assertIdentical($funcScope, $func);
    }

    function testCreateFunction() {
        $code = new Wact_Php_Writer();

        $name = 'init';
        $params = '$p1';

        $code->createFunction($name, $params);
        $function = $code->findFunction($name);

        $this->assertEqual($function->getName(), $name);
        $this->assertEqual($function->getParams(), $params);
        $this->assertIdentical($function, $code->findFunction($name));

        try {
            $code->createFunction($name, $params);
            $this->fail('Expected exception not thrown');
        } catch (Wact_Php_Writer_Exception $e) {
            $this->pass();
        }

        $function->writePHP('echo 1;');
        $code->writePHP('echo 2;');

        $this->assertEqual($code->getCode(), '<' . '?php' . "\n" . 'function ' . $name . '(' . $params . ') {' . "\n" . 'echo 1;echo 2;' . "\n" . '}' . "\n\n" . '?' . '>');
    }

    function testCreateClass() {
        $code = new Wact_Php_Writer();

        $name = 'MyView';
        $extends = 'Wact_View_Root';
        $implements = 'FooInterface';

        $code->createClass($name, $extends, $implements);

        $class = $code->findClass($name);
        $this->assertEqual($class->getName(), $name);
        $this->assertIdentical($class, $code->findClass($name));

        try {
            $code->createClass($name, $extends, $implements);
            $this->fail('Expected exception not thrown');
        } catch (Wact_Php_Writer_Exception $e) {
            $this->pass();
        }

        try {
            $code->writePHP('echo 2;');
            $this->fail('Expected exception not thrown');
        } catch (Wact_Php_Writer_Exception $e) {
            $this->pass();
        }

        $this->assertEqual($code->getCode(), '<' . '?php' . "\n" . 'class ' . $name . ' extends ' . $extends . ' implements ' . $implements . ' {' . "\n\n" . '}' . "\n\n\n" . '?' . '>');
    }

    function testRegisterInclude() {
        $code = new Wact_Php_Writer();
        $code->registerInclude('test.php');

        $html = 'test';
        $code->writeHTML($html);

        $inc = '<' . '?php' . "\n" . "require_once 'test.php';\n" . '?' . '>';
        $this->assertEqual($code->getIncludeCode(), $inc);
        $this->assertEqual($code->getBlockCode(), $html);
        $this->assertEqual($code->getCode(), $inc . $html);
    }

    function testRegisterIncludeClass() {
        $code = new Wact_Php_Writer();
        $code->registerInclude('test1.php');

        $code->createClass('test');
        $code->registerInclude('test2.php');

        $code->switchToGlobalScope();
        $code->registerInclude('test3.php');

        $html = 'test';
        $code->writeHTML($html);

        $inc = '<' . '?php' . "\n" . "require_once 'test1.php';\nrequire_once 'test2.php';\nrequire_once 'test3.php';\n" . '?' . '>';
        $php = '<' . '?php' . "\n" . 'class test {' . "\n\n" . '}' . "\n\n\n" . '?' . '>';
        $this->assertEqual($code->getIncludeCode(), $inc);
        $this->assertEqual($code->getBlockCode(), $php . $html);
        $this->assertEqual($code->getCode(), $inc . $php . $html);
    }

    function testRegisterIncludeFunc() {
        $code = new Wact_Php_Writer();
        $code->registerInclude('test1.php');

        $code->createFunction('test');
        $code->registerInclude('test2.php');

        $code->switchToGlobalScope();
        $code->registerInclude('test3.php');

        $html = 'test';
        $code->writeHTML($html);

        $inc = '<' . '?php' . "\n" . "require_once 'test1.php';\nrequire_once 'test2.php';\nrequire_once 'test3.php';\n" . '?' . '>';
        $php = '<' . '?php' . "\n" . 'function test() {' . "\n\n" . '}' . "\n\n" . '?' . '>';
        $this->assertEqual($code->getIncludeCode(), $inc);
        $this->assertEqual($code->getBlockCode(), $php . $html);
        $this->assertEqual($code->getCode(), $inc . $php . $html);
    }

    function testPropertyFunctions() {
        $code = new Wact_Php_Writer();

        $name = 'myVar';

        try {
            $code->createProperty($name);
            $this->fail('Expected exception not thrown');
        } catch (Wact_Php_Writer_Exception $e) {
            $this->pass();
        }

        try {
            $code->createTempProperty();
            $this->fail('Expected exception not thrown');
        } catch (Wact_Php_Writer_Exception $e) {
            $this->pass();
        }

        try {
            $code->findProperty($name);
            $this->fail('Expected exception not thrown');
        } catch (Wact_Php_Writer_Exception $e) {
            $this->pass();
        }

        try {
            $code->propertyExists($name);
            $this->fail('Expected exception not thrown');
        } catch (Wact_Php_Writer_Exception $e) {
            $this->pass();
        }

        $code->createClass('MyClass');
        $sr0 = $code->createProperty($name);
        $r0 = $code->findProperty($name);

        try {
            $code->createProperty($name);
            $this->fail('Expected exception not thrown');
        } catch (Wact_Php_Writer_Exception $e) {
            $this->pass();
        }

        $code->createClass('MyOtherClass');
        $sr2 = $code->createProperty($name);
        $r2 = $code->findProperty($name);
        $this->assertEqual($sr2, $r2->getName());
        $this->assertTrue($code->propertyExists($name));

        $code->switchToGlobalScope();

        try {
            $code->createProperty($name);
            $this->fail('Expected exception not thrown');
        } catch (Wact_Php_Writer_Exception $e) {
            $this->pass();
        }

        $code->switchToClassScope('MyClass');

        $r1 = new Wact_Php_Writer_Property('foo');
        $code->registerProperty($r1);

        $php = 'class MyClass {' . "\n\n" . $r0->getDeclaration() . "\n\n" . $r1->getDeclaration() . "\n\n" . '}' . "\n\n";
        $php .= 'class MyOtherClass {' . "\n\n" . $r2->getDeclaration() . "\n\n" . '}' . "\n\n";
        $this->assertEqual($code->getCode(), '<' . '?php' . "\n" . $php . "\n" . '?' . '>');
    }

    function testFunctionFunctionsWithScope() {
        $code = new Wact_Php_Writer();

        $name = 'MyView';
        $code->createClass($name);
        $class = $code->findClass($name);

        $code->createMethod('render');
        $this->assertTrue($class->methodExists('render'));

        $method = new Wact_Php_Writer_Method('init');
        $code->registerMethod($method);
        $this->assertTrue($class->methodExists('init'));
        $this->assertTrue($code->methodExists('init'));

        $method2 = $code->findMethod('init');
        $method3 = $class->findMethod('init');
        $this->assertIdentical($method, $method2);
        $this->assertIdentical($method2, $method3);

        $code->switchToGlobalScope();

        $this->assertFalse($code->findFunction('init'));
        $this->assertFalse($code->findFunction('render'));
    }

    function testCreateTempProperty() {
        $code = new Wact_Php_Writer();
        $code->createClass('MyView');
        $propertyName = $code->createTempProperty();
        $this->assertWantedPattern('/[a-z][a-z0-9]*/i', $propertyName);
        $php = 'class MyView {' . "\n\n" . 'public $A;' . "\n\n" . '}' . "\n\n";
        $this->assertEqual($code->getCode(), '<' . '?php' . "\n" . $php . "\n" . '?' . '>');
    }

    function testCreateTempVariable() {
        $code = new Wact_Php_Writer();

        $var1 = $code->createTempVariable();
        $this->assertWantedPattern('/[a-z][a-z0-9]*/i', $var1);

        $var2 = $code->createTempVariable();
        $this->assertWantedPattern('/[a-z][a-z0-9]*/i', $var2);

        $code->createFunction('init');

        $var3 = $code->createTempVariable();
        $this->assertWantedPattern('/[a-z][a-z0-9]*/i', $var3);

        $this->assertNotEqual($var1, $var2);
        $this->assertEqual($var1, $var3);
    }

}

?>