<?php

$cli_commands=array();
function add_cli_command($command, $handler, $short_desc, $usage_args=false, $long_desc=false, $override=false) {
	global $cli_commands;
	if (array_key_exists($command, $cli_commands) && !$override) {
		logging('ERROR', sprintf(_("The CLI command '%s' already exists.", $command)));
		return False;
	}

	if (!is_callable($handler)) {
		logging('ERROR', sprintf(_("The CLI command '%s' handler is not callable !"), $command));
		return False;
	}

	$cli_commands[$command] = array (
		'handler' => $handler,
		'short_desc' => $short_desc,
		'usage_args' => $usage_args,
		'long_desc' => $long_desc,
	);
	return True;
}

/*
 **************************************************************************************************************
 *                /!\ Code after this message will only be execute on CLI context /!\
 **************************************************************************************************************
 */
if (php_sapi_name() != "cli")
	return true;

// Store current CLI command
$cli_command = null;

/**
 * CLI Helpers
 **/

function usage($error=false) {
	global $cli_commands, $cli_command, $argv;

	if ($error)
		echo "$error\n\n";
	printf(_("Usage: %s [-h] [-qd] command\n"), $argv[0]);
	echo _("  -h        Show this message\n");
	echo _("  -q / -d   Quiet/Debug mode\n");
	echo _("  command   Command to run\n");
	echo "\n";
	echo _("Available commands:\n");

	foreach ($cli_commands as $command => $info) {
		if ($cli_command && $command != $cli_command)
			continue;
		echo "  $command: "._($info['short_desc'])."\n";
		echo "    ".$argv[0]." $command ".($info['usage_args']?_($info['usage_args']):'')."\n";
		if ($info['long_desc']) {
			if (is_array($info['long_desc'])) {
				$lines = array();
				foreach ($info['long_desc'] as $line)
					$lines[] = _($line);
				$info['long_desc'] = implode("\n", $lines);
			}
			else
				$info['long_desc'] = _($info['long_desc']);

			echo "\n    ".str_replace("\n", "\n    ", wordwrap($info['long_desc']))."\n";
		}
		echo "\n";
	}

	exit(($error?1:0));
}

function handle_cli_args() {
	global $log_level, $cli_commands, $cli_command, $argv;
	$log_level = 'INFO';
	$cli_command = false;
	$command_args = array();
	for ($i=1; $i < count($argv); $i++) {
		if (array_key_exists($argv[$i], $cli_commands)) {
			if (!$cli_command)
				$cli_command = $argv[$i];
			else
				usage(_("Only one command could be executed !"));
		}
		else {
			switch($argv[$i]) {
				case '-h':
				case '--help':
					usage();
					break;
				case '-d':
				case '--debug':
					$log_level = 'DEBUG';
					break;
				case '-q':
				case '--quiet':
					$log_level = 'WARNING';
					break;
				default:
					if ($cli_command)
						$command_args[] = $argv[$i];
					else
						usage(
							sprintf(_("Invalid parameter \"%s\".\nNote: Command's parameter/argument must be place after the command."), $argv[$i])
						);
			}
		}
	}

	if (!$cli_command)
		usage();

	logging('DEBUG', 'Run '.basename($argv[0])." command $cli_command with argument(s) '".implode("', '", $command_args)."'");

	try {
		$result = call_user_func($cli_commands[$cli_command]['handler'], $command_args);

		exit($result?0:1);
	}
	catch(Exception $e) {
		log_exception(sprintf(_("An exception occured running command %s"), $cli_command));
		exit(1);
	}
}

function print_item_info($item) {
	printf(_("Item #%s:\n"), $item['id']);
	printf("\t"._("ID: %s")."\n", $item['id']);
	printf("\t"._("Name: '%s'")."\n", $item['name']);
	printf("\t"._("Date: %s")."\n", format_time($item['date']));
	printf("\t"._("Description: %s")."\n", ($item['description']?"'".$item['description']."'":_("Not set")));
	printf("\t"._("Status: %s")."\n", $item['status']);
	return true;
}


/**
 * Common CLI commands
 **/

$orderbys = array('id', 'name', 'date', 'status', 'description');
function cli_list($command_args) {
	global $orderbys;
	$params = array(
		'order' => $orderbys[0],
		'order_direction' => 'ASC',
		'all' => true,
	);
	$patterns = array();
	for($i=0; $i < count($command_args); $i++) {
		switch($command_args[$i]) {
			case '-o':
			case '--orderby':
				$i++;
				if(!in_array($command_args[$i], $orderbys))
					usage('Invalid --orderby clause');
				$params['order'] = $command_args[$i];
				break;
			case '-r':
			case '--reverse':
				$params['order_direction'] = 'DESC';
				break;
			case '-s':
			case '--status':
				$i++;
				if(!check_status($command_args[$i]))
					usage('Invalid -s/--status clause');
				$params['status'] = $command_args[$i];
				break;
			default:
				$patterns[] = $command_args[$i];
		}
	}

	if (!empty($patterns))
		$params['pattern'] = implode(' ', $patterns);

	$items = search_items($params);
	if (!is_array($items)) {
		logging("ERROR", "Invalid DB info return.\n");
		return False;
	}

	if ($items['count'] == 0){
		echo _("No item.\n");
		return True;
	}

	$tbl = new Console_Table();
	$tbl->setHeaders(
		array(
			'ID',
			'Name',
			'Date',
			'Status',
			'Description',
		)
	);
	foreach($items['items'] as $info) {
		$tbl->addRow(
			array(
				$info['id'],
				$info['name'],
				format_time($info['date']),
				$info['status'],
				($info['description']?$info['description']:''),
			)
		);
	}
	echo $tbl->getTable();
	echo "\n".sprintf(_("%d item(s)"), $items['count'])."\n";
	return True;
}
add_cli_command(
	'list',
	'cli_list',
	___("List/search items"),
	___("[patterns]"),
	array(
		___("-o|--orderby       Ordering list criterion. Possible values:"),
		"                     - ".implode("\n                     - ", $orderbys),
		___("-r|--reverse       Reverse order"),
		___("-s|--status        Filter on status. Possible values:"),
		"                     - ".implode("\n                     - ", array_keys($status_list)),
	)
);

function cli_show($command_args) {
	if (count($command_args) != 1 || !check_id($command_args[0]))
		usage(_('You must provide a valid ID.'));

  $item_id = $command_args[0];
	$item = get_item($item_id);

	if (!$item)
		logging('FATAL', sprintf(_("Item #%s not found."), $item_id));

	print_item_info($item);
	return True;
}
add_cli_command(
	'show',
	'cli_show',
	___("Show item"),
	___("[ID]")
);

function cli_delete($command_args) {
	if (count($command_args) != 1)
		usage(_('You must provide item ID.'));

	// Check URI
	if (!check_id($command_args[0]))
		logging('FATAL', _("Invalid item ID"));

	// Check exist
	$item_id = $command_args[0];
	$item = get_item($item_id);
	if (!$item)
		logging('FATAL', sprintf(_("Item #%s not found."), $item_id));

	print_item_info($item);

	// Sure ?
	echo _("Are you sure you want to delete this item?  Type 'yes' to continue: ");
	$handle = fopen ("php://stdin","r");
	$line = fgets($handle);
	if(trim($line) != 'yes'){
		logging('WARNING', _("User cancel"));
		exit;
	}
	echo "\n";

	if (!delete_item($item['id']))
		logging('FATAL', sprintf(_("An error occured deleting item #%d."), $item_id));

	return True;
}
add_cli_command(
	'delete',
	'cli_delete',
	___("Delete item"),
	___("[item ID]")
);

function cli_export($command_args) {
	$fd = fopen((count($command_args) >= 1?$command_args[0]:'php://output'), 'w');
	export_items($fd);
	fclose($fd);
	logging('INFO', "Items export to '".(count($command_args) >= 1?$command_args[0]:'STDOUT')."'.");
}
add_cli_command(
	'export',
	'cli_export',
	___("Export items (as CSV)"),
	___("[output file path]")
);

function cli_restore($command_args) {
	$fd = fopen((count($command_args) >= 1?$command_args[0]:'php://stdin'), 'r');
	restore_items($fd);
	fclose($fd);
	logging('INFO', "Items restored from '".(count($command_args) >= 1?$command_args[0]:'STDIN')."'.");
}
add_cli_command(
	'restore',
	'cli_restore',
	___("Restore items (from CSV)"),
	___("[input file path]")
);

function cli_cron($command_args) {
	global $item_max_age;
	if (!isset($item_max_age))
		$item_max_age = 30;

	$just_try = false;
	for($i=0; $i < count($command_args); $i++) {
		switch($command_args[$i]) {
			case '-m':
			case '--max-age':
				$i++;
				if(!check_id($command_args[$i]))
					usage('Invalid -m|--max-age clause');
				$item_max_age = $command_args[$i];
				break;
			case '-j':
			case '--just-try':
				$just_try = true;
				break;
			default:
				usage('Invalid parameter '.$command_args[$i]);
		}
	}

	if (!is_int($item_max_age) || $item_max_age <= 0)
		logging('FATAL', 'Invalid $item_max_age value set in configuration: it\'s must be a positive integer.');
	logging('DEBUG', "cli_cron(): item max age = $item_max_age day(s)");

	$limit = time() - ($item_max_age * 86400);
	logging('DEBUG', "Handle items expiration with creation date limit ".format_time($limit).".");

	$items = search_items(array('all' => true));
	$error = false;
	foreach($items['items'] as $item) {
		if ($item['date'] < $limit) {
			if ($just_try) {
				logging('DEBUG', 'Just-try mode: do not really delete item #'.$item['id'].' ('.$item['name'].', creation date: '.format_time($item['date']).')');
			}
			else if (delete_item($item['id'])) {
				logging('INFO', 'item #'.$item['id'].' ('.$item['name'].') deleted (creation date: '.format_time($item['date']).')');
				remove_item_attachments($item['id']);
			}
			else {
				logging('ERROR', 'Fail to delete item "'.$item['id'].'" ('.$item['name'].', creation date: '.format_time($item['date']).')');
				$error = true;
			}
		}
		else {
			logging('DEBUG', 'item "'.$item['id'].'" ('.$item['name'].') still valid (creation date: '.format_time($item['date']).')');
		}
	}
	exit($error?1:0);
}
add_cli_command(
	'cron',
	'cli_cron',
	___("Cron to handle item expiration"),
	false,
	array (
		___("-j/--just-try    Just-try mode : do not really removed expired item(s)"),
		___("-m/--max-age     Item expiration limit (in days, optional)"),
	)
);


function cli_extract_messages($command_args) {
  global $root_dir_path, $root_lang_dir, $smarty_templates_dir;

  // List PHP files to parse
  $php_files = run_external_command(
    array('find', escapeshellarg($root_dir_path), '-name', "'*.php'"),
    null,   // no STDIN data
    false   // do not escape command args (already done)
  );
  if (!is_array($php_files) || $php_files[0] != 0) {
    logging('FATAL', _("Fail to list PHP files."));
  }

  // Extract messages from PHP files using xgettext
  $result = run_external_command(
    array(
      "xgettext",
      "--from-code utf-8",
      "--language=PHP",
      "-o", "$root_lang_dir/php-messages.pot",            // Output
      "--omit-header",                                    // No POT header
      "--keyword=___",                                    // Handle custom ___() translation function
      "--files=-"                                         // Read files to parse from STDIN
    ),
    $php_files[1]                                         // Pass PHP files list via STDIN
  );
  if (!is_array($result) || $result[0] != 0)
    logging('FATAL', _("Fail to extract messages from PHP files using xgettext."));

  // Extract messages from templates files using tsmarty2c.php
  $result = run_external_command(
    array (
      "$root_dir_path/vendor/bin/tsmarty2c.php",
      "-o", "$root_lang_dir/templates-messages.pot",
      $smarty_templates_dir,
    )
  );
  if (!is_array($result) || $result[0] != 0)
    logging('FATAL', _("Fail to extract messages from template files using tsmarty2c.php script."));

  // Merge previous results in ldapsaisie.pot file using msgcat
  $result = run_external_command(array(
    'msgcat',
    "$root_lang_dir/php-messages.pot",
    "$root_lang_dir/templates-messages.pot",
    "-o", "$root_lang_dir/messages.pot",
  ));
  if (!is_array($result) || $result[0] != 0)
    logging('FATAL', _("Fail to merge messages using msgcat."));
}
add_cli_command(
	'extract_messages',
	'cli_extract_messages',
	___("Extract messages that need to be translated"),
  null,
	___("This command could be used to generate/update lang/messages.pot file.")
);