#!/usr/bin/env php
<?php

require_once(__DIR__ . '/../src/bootstrap.php');

// Load the bootstrap from the XML configuration. This will contain any extra
// custom modules that also need to be compiled.
if (!file_exists('phpunit.xml')) {
    echo "You need to have setup phpunit.xml with a bootstrap file.\n\n";
    exit(1);
}

$xml = simplexml_load_file('phpunit.xml');
$bootstrapFile = (string)$xml['bootstrap'];
if (!$bootstrapFile) {
    echo "Your phpunit.xml configuration must have a bootstrap file.\n\n";
    exit(1);
}

require_once($bootstrapFile);

use Concise\Core\IndependentTestCase;
use Concise\Core\ModuleManager;

// Simulate starting a test case which will cause the default ModuleManager
// to load all the modules.
$testCase = new IndependentTestCase();
$testCase->setUpBeforeClass();

updateBuilders();

function getShortName($trait)
{
    static $shortNames = [];
    static $c = 0;
    if (!array_key_exists($trait, $shortNames)) {
        if ($c < 26) {
            $shortNames[$trait] = chr(ord('A') + $c);
        } else {
            $shortNames[$trait] =
                chr(ord('A') + ($c / 26)) .
                chr(ord('a') + ($c % 26));
        }

        if ($shortNames[$trait] == 'Do' || $shortNames[$trait] == 'If') {
            unset($shortNames[$trait]);
            ++$c;
            return getShortName($trait);
        }
    }

    ++$c;
    return $shortNames[$trait];
}

function updateBuilders()
{
    $syntaxTree = array();
    foreach (ModuleManager::getInstance()->getModules() as $module) {
        foreach ($module->getSyntaxes() as $syntax) {
            $parts = explode('?', $syntax->getRawSyntax());
            $temp = &$syntaxTree;
            foreach ($parts as $part) {
                $part = trim($part);
                $temp = &$temp[$part];
            }
            $temp = null;
        }
    }

    $php = array();
    $header = "/**";
    foreach ($syntaxTree as $k => $v) {
        foreach (array(
                     'assert' => false,
                     'verify' => true
                 ) as $method => $verify) {
            $header .= "\n * @method ";

            $types = '';
            if (null !== $v) {
                foreach ($v as $words => $s) {
                    $types .=
                        '|' .
                        getShortName(str_replace(' ', '', ucwords($words)));
                }
            }
            $header .= ltrim($types, '|');

            $header .= " $method" . ucfirst($k) .
                "(\$valueOrFailureMessage, \$value = null)";

            if (null !== $v) {
                $php = a($v, $php);
            }
        }
    }

    $out =
        $header .
        "\n */\nabstract class BaseAssertions extends \\PHPUnit\\Framework\\TestCase\n{" .
        "\n}\n\n";
    ksort($php);
    foreach ($php as $trait => $methods) {
        $out .= "/**$methods */\nclass " . getShortName($trait) . "\n{\n}\n\n";
    }

    // Assume concise has been deployed through composer and we do not want to
    // modify the repository itself; so we load it into a temp folder somewhere
    // on the system. This will be loaded manually when they run concise.
    $assertionsFile = Concise\Core\getBaseAssertionsPath();

    if (strpos(__DIR__, 'vendor/') === false) {
        // Lets also remove the temp file since it has higher precedence than
        // the file we are about to load. Make sure this happens before
        // $assertionsFile gets reassined below.
        if (file_exists($assertionsFile)) {
            unlink($assertionsFile);
        }

        // This means the script has been run when it wasn't deployed through
        // composer. Which means someone is has downloaded the library manually
        // or we are working on the composer package itself (dev). In either
        // case we update the BaseAssertions.php in this repository.
        $assertionsFile = __DIR__ . '/../src/Concise/Core/BaseAssertions.php';
    }

    file_put_contents(
        $assertionsFile,
        "<?php\n\nnamespace Concise\\Core;\n\nuse \\PHPUnit\\Framework\\TestCase;\n\n" .
        str_replace("\t", '    ', $out)
    );
}

/**
 * @return string
 */
function getTempBaseAssertionsPath()
{
    return sys_get_temp_dir() . '/BaseAssertions.php';
}

/**
 * @param $v
 * @param $php
 * @return array
 */
function a($v, $php)
{
    foreach ($v as $words => $s) {
        $trait2 = str_replace(' ', '', ucwords($words));
        $php[$trait2] = "\n * $trait2";
        if (is_array($s)) {
            $php[$trait2] .= "\n * @method null";
            foreach ($s as $words2 => $s2) {
                if ($words2) {
                    $php[$trait2] .=
                        '|' .
                        getShortName(str_replace(' ', '', ucwords($words2)));
                }
            }

            unset($s['']);
            if (count($s) > 0) {
                $php = a($s, $php);
            }
        } else {
            $php[$trait2] = "\n * @property null";
        }
        $php[$trait2] .= " ";
        $php[$trait2] .= lcfirst($trait2);

        if (is_array($s)) {
            $php[$trait2] .= "(\$value)";
        }
        $php[$trait2] .= "\n";
    }
    return $php;
}

function renderSyntax($syntax)
{
    return preg_replace_callback(
        "/\\?:?([a-zA-Z_,]+)?/",
        function ($m) {
            if ($m[0] == '?') {
                return "`mixed`_";
            }
            $types = explode(",", $m[1]);
            $r = array();
            foreach ($types as $type) {
                $r[] = "`$type`_";
            }
            return implode("\\|\\ ", $r);
        },
        $syntax
    );
}
