#!/usr/bin/env php
<?php
/**
 * 2013-09 sdf
 *
 * CLI ScriptTool to find usage of "lang" in application
 * @author Robert Jamróz <robert.jamroz@velis.pl>
 *
 */
define('BASE_DIR', realpath('../'));

$startMt = microtime(true);

$shortopts = "";
$shortopts .= "h::o::os::ge::vd:t::f"; // Optional value
$longoptions = array(
    'help::', 'output::', 'outputsql', 'group', 'exclude::', 'verbose', 'dirs:', 'langtable'
);

$options = getopt($shortopts, $longoptions);
$excludeDirs = array();
$verbose = false;
$outputFile = 'lang-usage.php';
$outputSqlFile = 'lang-usage.sql';
$grouped = false;
$langTabName = 'app.lang_usage_tab';

if (isset($options['e']) && !empty($options['e'])) {
    $excludeDirs = explode(',', $options['e']);
}

if (isset($options['v'])) {
    $verbose = true;
}

if (isset($options['g'])) {
    $grouped = true;
}

if (isset($options['o']) && !empty($options['o'])) {
    $outputFile = $options['o'];
}

if (isset($options['os']) && !empty($options['os'])) {
    $outputFile = $options['os'];
}

if (isset($options['t']) && !empty($options['t'])) {
    $langTabName = $options['t'];
}

$langMatches = array();
$matchCount = 0;
$fileTypes = '.php,.tpl,.js';
$dirs = array('.');

$fcolors = array();
$bcolors = array();

$fcolors['black'] = '0;30';
$fcolors['dark_gray'] = '1;30';
$fcolors['blue'] = '0;34';
$fcolors['light_blue'] = '1;34';
$fcolors['green'] = '0;32';
$fcolors['light_green'] = '1;32';
$fcolors['cyan'] = '0;36';
$fcolors['light_cyan'] = '1;36';
$fcolors['red'] = '0;31';
$fcolors['light_red'] = '1;31';
$fcolors['purple'] = '0;35';
$fcolors['light_purple'] = '1;35';
$fcolors['brown'] = '0;33';
$fcolors['yellow'] = '1;33';
$fcolors['light_gray'] = '0;37';
$fcolors['white'] = '1;37';

$bcolors['black'] = '40';
$bcolors['red'] = '41';
$bcolors['green'] = '42';
$bcolors['yellow'] = '43';
$bcolors['blue'] = '44';
$bcolors['magenta'] = '45';
$bcolors['cyan'] = '46';
$bcolors['light_gray'] = '47';


/**
 *
 * Message print function
 *
 * @paramstring $msg
 */
function plog($msg)
{

    $startMt = microtime(true);
    $microTime = sprintf("%06d", ($startMt - floor($startMt)) * 1000000);
    $d = new \DateTime(date('Y-m-d H:i:s.' . $microTime, $startMt));
    print $d->format("Y-m-d H:i:s.u") . "\t" . $msg . "\n";
}


/**
 *
 * Main recursive directory walker and filter
 *
 * @global string $fileTypes
 * @global type $excludeDirs
 * @global type $langMatches
 * @global type $verbose
 * @param type $path
 */

function directoryRecursive($path, &$array)
{

    global $fileTypes, $excludeDirs, $langMatches, $verbose, $grouped, $matchCount;

    try {
        if (!file_exists(BASE_DIR . $path) || !is_dir(BASE_DIR . $path)) {
            throw new \Exception('File: "' . BASE_DIR . $path . ' ('.$path.')" not exists or isn\'t a directory!');
        }

        $it = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator(BASE_DIR . $path));
        $content = '';

        while ($it->valid()) {
            if (!$it->isDot()) {
                $currentDir = $it->getSubPath();
                $fileName = $it->key();
                $ext = substr($fileName, 0, -4);

                $isExclude = false;
                foreach ($excludeDirs as $exclude) {
                    if (strlen(trim($exclude)) == 0) {
                        continue;
                    }
                    if (strpos($currentDir, $exclude) !== false) {
                        $isExclude = true;
                        break;
                    }
                }

                if (strpos($fileTypes, $ext) !== false || $isExclude || strpos($fileName, 'lang-extractor.php') !== false) {
                    if ($isExclude && $verbose) {
                        plog("Excluded:\t" . $fileName);
                    }
                    $it->next();
                    continue;
                }

                $content = file_get_contents($fileName);

                $patterns = array(
                    '~\$_LANG\.([0-9A-Z_]+)~',
                    '~Lang::get\(["\']{1}([0-9A-Z_]+)["\']{1}\)~i',
                    'block' => '~\{langs\}([^\{]+)\{/langs\}~'
                );

                $matches = array();
                $customPatterns = array('block');

                foreach ($patterns as $patternKey => $pattern) {
                    $match = preg_match_all($pattern, $content, $matches);

                    if ($match > 0) {
                        //echo 'SubPathName: ' . $it->getSubPathName() . "\n";
                        //echo 'SubPath:     ' . $currentDir . "\n";
                        $added = array();
                        foreach ($matches[1] as $lang) {
                            if (!$grouped) {
                                if (!in_array(trim($lang), $langMatches) && !in_array($patternKey, $customPatterns)) {
                                    $array[] = $added[] = trim($lang);
                                } else {
                                    if ($patternKey == 'block') {
                                        $langBlock = array_filter(preg_split('~[^A-Z0-9_]+~', trim($lang)));
                                        foreach ($langBlock as $block) {
                                            if (!in_array(trim($block), $array)) {
                                                $array[] = $added[] = trim($block);
                                            }
                                        }
                                    }
                                }
                            } else {
                                if (!in_array($patternKey, $customPatterns)) {
                                    groupCalculate($lang, $array);
                                    $added[] = $lang;
                                } else {
                                    if ($patternKey == 'block') {
                                        $langBlock = array_filter(preg_split('~[^A-Z0-9_]+~', trim($lang)));
                                        foreach ($langBlock as $block) {
                                            groupCalculate(trim($block), $array);
                                            $added[] = $block;
                                        }
                                    }
                                }
                            }
                        }

                        if ($verbose) {
                            echo paddingText('' . count($added), 5) . ' ' . str_replace(BASE_DIR, '', $it->key()) . "\n";
                            foreach ($added as $add) {
                                echo paddingText('', 5) . "[ \033[31m" . $add . "\033[0m ] \n";
                            }
                        }

                        $added = array();
                    }

                    $matches = array();
                }
            }
            $it->next();
        }
    } catch (\Exception $e) {
        throw $e;
    }
}


/**
 *
 * Padding of text
 *
 * @param type $msg
 * @param type $langth
 * @return type
 */
function paddingText($msg, $length)
{
    return $msg . str_repeat(' ', $length - (strlen($msg) > $length ? $length : strlen($msg)));
    ;
}


/**
 *
 * Count calculator
 *
 * @param type $array
 * @return int
 */
function calculateCountFound($array)
{
    $count = 0;
    foreach ($array as $key => $value) {
        if (is_array($value)) {
            $count += calculateCountFound($value);
        } else {
            $count +=1;
        }
    }
    return $count;
}


/**
 *
 * Gruped lang executor
 *
 * @global int $matchCount
 * @param type $lang
 * @param type $array
 */
function groupCalculate($lang, &$array)
{

    global $matchCount;

    $groups = explode('_', trim($lang));

    $name = array_shift($groups);

    $value = implode('_', $groups);

    if (!isset($array[$name]) || !is_array($array[$name])) {
        $array[$name] = array();
    }

    if (!in_array($value, $array[$name])) {
        $array[$name][] = $value;
    }
}


/**
 *
 * Sending output files
 *
 * @global type $langMatches
 * @global type $outputFile
 * @global string $outputSqlFile
 * @global type $verbose
 */
function outputFoundData()
{

    global $langMatches, $outputFile, $outputSqlFile, $verbose, $grouped, $langTabName;

    if ($verbose) {
        plog('Sending output file/s');
    }

    if (!$grouped) {
        sort($langMatches);
    } else {
        ksort($langMatches);
        foreach ($langMatches as $key => $value) {
            sort($value);
            $langMatches[$key] = $value;
        }
    }


    $isPhp = file_put_contents($outputFile, "<?php
// Generated: " . date('Y-m-d H::i:s') . "
\n" . '$langUsage = ' . var_export($langMatches, true) . ";\n");


    $sqlInsertContent = array();

    if ($grouped) {
        foreach ($langMatches as $langgroup => $key) {
            foreach ($key as $keyName) {
                $sqlInsertContent[] = "\t" . '(\'' . $langgroup . '\',\'' . $keyName . '\')';
            }
        }
    } else {
        foreach ($langMatches as $key) {
            $groups = explode('_', trim($key));
            $name = array_shift($groups);
            $sqlInsertContent[] = "\t" . '(\'' . $name . '\',\'' . implode('_', $groups) . '\')';
        }
    }

    $isSql = file_put_contents($outputSqlFile, "
// Generated: " . date('Y-m-d H::i:s') . "\nBEGIN;\n" . 'INSERT INTO ' . $langTabName . "\n  " . ' VALUES ' . "\n" . implode(",\n", $sqlInsertContent) . ";\nCOMMIT;\n");

    if ($verbose && ($isPhp || $isSql)) {
        plog('Sending commplete');
    }
}


/**
 *
 * Color string message outpu
 *
 * @global array $fcolors
 * @global array $bcolors
 * @param type $string
 * @param type $foreground_color
 * @param type $background_color
 * @return string
 */
function getColors($string, $foreground_color = null, $background_color = null)
{

    global $fcolors, $bcolors;

    $colored_string = "";

    if (isset($fcolors[$foreground_color])) {
        $colored_string .= "\033[" . $fcolors[$foreground_color] . "m";
    }
    if (isset($bcolors[$background_color])) {
        $colored_string .= "\033[" . $bcolors[$background_color] . "m";
    }
    $colored_string .= $string . "\033[0m";

    return $colored_string;
}

if ($verbose) {
    plog('Start script at: ' . BASE_DIR);
    //print_r($options);
}

/**
 * Main operation statement
 */
if (!isset($options['h'])) {
    try {
        if (!isset($options['f'])) {
            $handle = fopen("php://stdin", "r");
            fwrite(STDOUT, "Do You want to continue? (T/n)\n");
            $line = fgets($handle);
            if (trim($line) !== 'T') {
                echo "ABORTING!\n";
                exit;
            }
            echo "\n";
            echo "Ok, continuing... :)\n";
        }

        if (isset($options['d']) && !empty($options['d'])) {
            $dirs = explode(',', $options['d']);
        }


        foreach ($dirs as $dir) {
            directoryRecursive('/' . trim($dir), $langMatches);
        }

        outputFoundData();

        if ($verbose) {
            plog('Found: ' . calculateCountFound($langMatches));
            plog('Execution interval: ' . (round(microtime(true) - $startMt, 2)) . ' [sec]');
        }
    } catch (\Exception $e) {
        plog('Exception occurred:');
        plog(trim($e->getMessage()));
    }
} else {
    echo "\t==============================================
        ----------------------------------------------
        Robert Jamroz(z krecha) - Options help page :D
        ----------------------------------------------
        ==============================================\n\n
        ----------------------------------------------
        ==============================================\n\n
        \"*\" - is requred
        -h  (-help)      : help - help for this :)
        -o  (-output)    : output file name; default = \"lang-usage.php\"
        -os (-outputsql) : output SQL file name; default = \"lang-usage.sql\"
        -g  (-group)     : group lang found array: \$lang[GROUP][KEY]] else \$lang[GROUP_KEY]
        -v  (-verbose)   : verbose (speak a lot >o_o<)
        -d (-dirs)       : direcories joined ',', e.g.: -d=res,applications; default = ./ of base application dir
        -e (-exclude)    : direcories joined ',' to exclude, e.g.: -e=res,applications; default empty
        -t (-langtable)  : table name for SQL INSERTs; default = app.lang_usage_tab
        -f               : no prompt :D \n\n
        ----------------------------------------------
        ==============================================\n\n";
}
