#!/usr/bin/env php
<?php
// Command line utility to compile LESS to STDOUT
// Leaf Corcoran <leafot@gmail.com>, 2013

require_once __DIR__ . '/vendor/autoload.php';

$exe = array_shift($argv); // remove filename

$HELP = <<<EOT
Usage: $exe [options] input-file [output-file]

Options include:

    -h, --help  Show this message
    -v          Print the version
    -f=format   Set the output format, includes "default", "compressed"
    -c          Keep /* */ comments in output
    -r          Read from STDIN instead of input-file
    -w          Watch input-file, and compile to output-file if it is changed
    -T          Dump formatted parse tree
    -X          Dump raw parse tree


EOT;

$opts = getopt('hvrwncXTf:', ['help']);
while (count($argv) > 0 && preg_match('/^-([-hvrwncXT]$|[f]=)/', $argv[0])) {
    array_shift($argv);
}

function has()
{
    global $opts;
    foreach (func_get_args() as $arg) {
        if (isset($opts[$arg])) {
            return true;
        }
    }

    return false;
}

if (has("h", "help")) {
    exit($HELP);
}

error_reporting(E_ALL);
$path = realpath(dirname(__FILE__)) . '/';

$VERSION = \LesserPhp\Compiler::VERSION;

$fa = "Fatal Error: ";
function err($msg)
{
    fwrite(STDERR, $msg . "\n");
}

if (php_sapi_name() != "cli") {
    err($fa . $argv[0] . " must be run in the command line.");
    exit(1);
}

function make_less()
{
    global $opts;
    $l = new \LesserPhp\Compiler();

    if (has("f")) {
        $format = $opts["f"];
        if ($format != "default") {
            $l->setFormatter($format);
        }
    }

    if (has("c")) {
        $l->setPreserveComments(true);
    }

    return $l;
}

/**
 * @param $data
 */
function process($data)
{
    global $fa;

    $l = make_less();

    try {
        echo $l->parse($data);
        exit(0);
    } catch (\Exception $ex) {
        err($fa . "\n" . str_repeat('=', 20) . "\n" .
            $ex->getMessage());
        exit(1);
    }
}

if (has("v")) {
    exit($VERSION . "\n");
}

if (has("r")) {
    if (!empty($argv)) {
        $data = $argv[0];
    } else {
        $data = "";
        while (!feof(STDIN)) {
            $data .= fread(STDIN, 8192);
        }
    }
    exit(process($data));
}

if (has("w")) {
    // need two files
    if (!is_file($in = array_shift($argv)) ||
        null == $out = array_shift($argv)
    ) {
        err($fa . $exe . " -w infile outfile");
        exit(1);
    }

    echo "Watching " . $in .
        (has("n") ? ' with notifications' : '') .
        ", press Ctrl + c to exit.\n";

    $cache = $in;
    $last_action = 0;
    while (true) {
        clearstatcache();

        // check if anything has changed since last fail
        $updated = false;
        if (is_array($cache)) {
            foreach ($cache['files'] as $fname => $_) {
                if (filemtime($fname) > $last_action) {
                    $updated = true;
                    break;
                }
            }
        } else {
            $updated = true;
        }

        // try to compile it
        if ($updated) {
            $last_action = time();

            try {
                $cache = \LesserPhp\Compiler::cexecute($cache);
                echo "Writing updated file: " . $out . "\n";
                if (!file_put_contents($out, $cache['compiled'])) {
                    err($fa . "Could not write to file " . $out);
                    exit(1);
                }
            } catch (\Exception $ex) {
                echo "\nFatal Error:\n" . str_repeat('=', 20) . "\n" .
                    $ex->getMessage() . "\n\n";

                if (has("n")) {
                    `notify-send -u critical "compile failed" "{$ex->getMessage()}"`;
                }
            }
        }

        sleep(1);
    }
    exit(0);
}

if (!$fname = array_shift($argv)) {
    echo $HELP;
    exit(1);
}

function dumpValue($node, $depth = 0)
{
    if (is_object($node)) {
        $indent = str_repeat("  ", $depth);
        $out = [];
        foreach ($node->props as $prop) {
            $out[] = $indent . dumpValue($prop, $depth + 1);
        }
        $out = implode("\n", $out);
        if (!empty($node->tags)) {
            $out = "+ " . implode(", ", $node->tags) . "\n" . $out;
        }

        return $out;
    } elseif (is_array($node)) {
        if (empty($node)) {
            return "[]";
        }
        $type = $node[0];
        if ($type == "block") {
            return dumpValue($node[1], $depth);
        }

        $out = [];
        foreach ($node as $value) {
            $out[] = dumpValue($value, $depth);
        }

        return "{ " . implode(", ", $out) . " }";
    } else {
        if (is_string($node) && preg_match("/[\s,]/", $node)) {
            return '"' . $node . '"';
        }

        return $node; // normal value
    }
}


function stripValue($o, $toStrip)
{
    if (is_array($o) || is_object($o)) {
        $isObject = is_object($o);
        $o = (array)$o;
        foreach ($toStrip as $removeKey) {
            if (!empty($o[$removeKey])) {
                $o[$removeKey] = "*stripped*";
            }
        }

        foreach ($o as $k => $v) {
            $o[$k] = stripValue($v, $toStrip);
        }

        if ($isObject) {
            $o = (object)$o;
        }
    }

    return $o;
}

function dumpWithoutParent($o, $alsoStrip = [])
{
    $toStrip = array_merge(["parent"], $alsoStrip);
    print_r(stripValue($o, $toStrip));
}

try {
    $less = make_less();
    if (has("T", "X")) {
        $parser = new \LesserPhp\Parser($less, $fname);
        $tree = $parser->parse(file_get_contents($fname));
        if (has("X")) {
            $out = print_r($tree, 1);
        } else {
            $out = dumpValue($tree) . "\n";
        }
    } else {
        $out = $less->parse();
    }

    if (!$fout = array_shift($argv)) {
        echo $out;
    } else {
        file_put_contents($fout, $out);
    }

} catch (\Exception $ex) {
    err($fa . $ex->getMessage());
    exit(1);
}
