#!/usr/bin/env php
<?php
/**
 * EGroupware - modify our old JS inheritance to TypeScript
 *
 * @link http://www.egroupware.org
 * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
 * @author Ralf Becker <rb@egroupware.org>
 * @copyright 2020 by Ralf Becker <rb@egroupware.org>
 */

if (PHP_SAPI !== 'cli')	// security precaution: forbit calling as web-page
{
	die('<h1>fix_api.php must NOT be called as web-page --> exiting !!!</h1>');
}

// raw replacements
$replace = array(
	'/^app.classes.([a-z0-9_]+)(\s*=\s*AppJS.extend)\(.*^}\);/misU' =>
		function($matches) {
			return strtr($matches[0], [
				'app.classes.'.$matches[1].$matches[2].'(' => 'class '.ucfirst($matches[1]).'App extends EgwApp',
				"\n});" => "\n}\n\napp.classes.$matches[1] = ".ucfirst($matches[1])."App;"
			]);
		},
	"/^\tappname:\s*'([^']+)',/m" => "\treadonly appname = '$1';",
	"/^\t([^: ,;(\t]+?):\s*([^()]+?),/m" => "\t\$1 : any = $2;",
	"/^\t([^:\n]+):\s*function\s*\(.*this._super.(apply|call)\(/msU" =>
		function($matches) {
	        return str_replace('this._super',
				$matches[1] === 'init' ? 'super' : 'super.'.$matches[1], $matches[0]);
	    },
	"/^\t([^:\n]+):\s*function\s*\(/m" => function($matches) {
		return "\t".($matches[1] === 'init' ? 'constructor' : $matches[1]).'(';
	},
    // TS does not like to call parent constructor with super.apply(this, arguments) and we dont have arguments ...
    '/\tsuper.apply\(this, *arguments\)/' => "\tsuper()",
	"/^\t},$/m" => "\t}",
	'/^ \* @version \$Id[^$]*\$\n/m' => '',
	'#^ \* @link http://www.egroupware.org#m' => ' * @link: https://www.egroupware.org',
);

/**
 * Add boilerplate for app.js files after header
 *
 * @param $app
 * @param $content
 * @return string
 */
function app_js_add($app, $content)
{
	return preg_replace('#^(/\*\*.*\n\ \*/)#Us', <<<EOF
$1

/*egw:uses
	/api/js/jsapi/egw_app.js
 */

import { EgwApp } from '../../api/js/jsapi/egw_app';
EOF
		, $content);
}

/**
 * Convert JavaScript to TypeScript
 *
 * @param string $file filename
 * @param boolean $dry_run =false true: only echo fixed file, not fix it
 * @return boolean false on error
 */
function convert($file, $dry_run=false)
{
	global $prog, $replace;
	if (basename($file) == $prog) return true;	// dont fix ourself ;-)

	if (($content = $content_in = file_get_contents($file)) === false) return false;

	$replace_callbacks = array_filter($replace, 'is_callable');
	$replace_strings = array_filter($replace, 'is_string');

	$content = preg_replace(array_keys($replace_strings), array_values($replace_strings),
		preg_replace_callback_array($replace_callbacks, $content));

	// add app.js spezific boilerplate
	if (preg_match('#/([^/]+)/js/app(\.old)?\.js$#', realpath($file), $matches))
	{
		$content = app_js_add($matches[1], $content);
	}

	if ($content == $content_in) return true;	// nothing changed

	if ($dry_run)
	{
		echo $content;
	}
	else
	{
		$ret = file_put_contents($new_file=preg_replace('/\.js$/', '.ts', $file), $content) === false ? -1 : 0;
		//system('/usr/bin/php -l '.$file.'.new', $ret);
		system('/usr/bin/diff -u '.$file.' '.$new_file);
		return !$ret;
	}

	return true;
}

/**
 * Loop recursive through directory and call fix_api for each php file
 *
 * @param string $dir
 * @param boolean $dry_run =false true: only echo fixed file, not fix it
 * @return boolean false on error
 */
function convert_recursive($dir, $dry_run=false)
{
	if (!is_dir($dir)) return false;

	foreach(scandir($dir) as $file)
	{
		if ($file == '.' || $file == '..') continue;

		if (is_dir($dir.'/'.$file))
		{
			convert_recursive($dir.'/'.$file, $dry_run);
		}
		elseif(substr($file,-3) == '.js')
		{
			echo "\r".str_repeat(' ',100)."\r".$dir.'/'.$file.': ';
			convert($dir.'/'.$file, $dry_run);
		}
	}
	echo "\r".str_repeat(' ',100)."\r";
	return true;
}

/**
 * Give usage
 *
 * @param string $error =null
 */
function usage($error=null)
{
	global $prog;
	echo "Usage: $prog [-h|--help] [-d|--dry-run] file(s) or dir(s)\n\n";
	if ($error) echo $error."\n\n";
	exit($error ? 1 : 0);
}

$args = $_SERVER['argv'];
$prog = basename(array_shift($args));

if (!$args) usage();

$dry_run = false;
while(($arg = array_shift($args)) && $arg[0] == '-')
{
	switch($arg)
	{
		case '-h':
		case '--help':
			usage();
			break;

		case '-d':
		case '--dry-run':
			$dry_run = true;
			break;

		default:
			if ($args)	// not last argument
			{
				usage("Unknown argument '$arg'!");
			}
			break 2;
	}
}

do {
	if (!file_exists($arg)) usage("Error: $arg not found!");

	if (!is_dir($arg))
	{
		convert($arg, $dry_run);
	}
	else
	{
		convert_recursive($arg, $dry_run);
	}
}
while(($arg = array_shift($args)));
