<?php

/**
 * @package    KO7/Minion
 * @author     Kohana Team
 * @copyright  (c) 2015 Kohana Team
 * @copyright  (c) 2016-2018 Koseven Team
 * @license    https://koseven.ga/LICENSE.md
 */
class KO7_Minion_CLI {

	public static $wait_msg = 'Press any key to continue...';

	protected static $foreground_colors = [
		'black'        => '0;30',
		'dark_gray'    => '1;30',
		'blue'         => '0;34',
		'light_blue'   => '1;34',
		'green'        => '0;32',
		'light_green'  => '1;32',
		'cyan'         => '0;36',
		'light_cyan'   => '1;36',
		'red'          => '0;31',
		'light_red'    => '1;31',
		'purple'       => '0;35',
		'light_purple' => '1;35',
		'brown'        => '0;33',
		'yellow'       => '1;33',
		'light_gray'   => '0;37',
		'white'        => '1;37',
	];
	protected static $background_colors = [
		'black'      => '40',
		'red'        => '41',
		'green'      => '42',
		'yellow'     => '43',
		'blue'       => '44',
		'magenta'    => '45',
		'cyan'       => '46',
		'light_gray' => '47',
	];

	/**
	 * Returns one or more command-line options. Options are specified using
	 * standard CLI syntax:
	 *
	 *     php index.php --username=john.smith --password=secret --var="some value with spaces"
	 *
	 *     // Get the values of "username" and "password"
	 *     $auth = Minion_CLI::options('username', 'password');
	 *
	 * @param   string  $options,...    option name
	 * @return  array
	 */
	public static function options($options = NULL)
	{
		// Get all of the requested options
		$options = func_get_args();

		// Found option values
		$values = [];

		// Skip the first option, it is always the file executed
		for ($i = 1; $i < $_SERVER['argc']; ++$i)
		{
			if ( ! isset($_SERVER['argv'][$i]))
			{
				// No more args left
				break;
			}

			// Get the option
			$opt = $_SERVER['argv'][$i];

			if (substr($opt, 0, 2) !== '--')
			{
				// This is a positional argument
				$values[] = $opt;
				continue;
			}

			// Remove the "--" prefix
			$opt = substr($opt, 2);

			if (strpos($opt, '='))
			{
				// Separate the name and value
				list ($opt, $value) = explode('=', $opt, 2);
			}
			else
			{
				$value = NULL;
			}

			$values[$opt] = $value;
		}

		if ($options)
		{
			foreach ($values as $opt => $value)
			{
				if ( ! in_array($opt, $options))
				{
					// Set the given value
					unset($values[$opt]);
				}
			}
		}

		return count($options) == 1 ? array_pop($values) : $values;
	}

	/**
	 * Reads input from the user. This can have either 1 or 2 arguments.
	 *
	 * Usage:
	 *
	 * // Waits for any key press
	 * Minion_CLI::read();
	 *
	 * // Takes any input
	 * $color = Minion_CLI::read('What is your favorite color?');
	 *
	 * // Will only accept the options in the array
	 * $ready = Minion_CLI::read('Are you ready?', array('y','n'));
	 *
	 * @param  string  $text    text to show user before waiting for input
	 * @param  array   $options array of options the user is shown
	 * @return string  the user input
	 */
	public static function read($text = '', array $options = NULL)
	{
		// If a question has been asked with the read
		$options_output = '';
		if ( ! empty($options))
		{
			$options_output = ' [ '.implode(', ', $options).' ]';
		}

		fwrite(STDOUT, $text.$options_output.': ');

		// Read the input from keyboard.
		$input = trim(fgets(STDIN));

		// If options are provided and the choice is not in the array, tell them to try again
		if ( ! empty($options) && ! in_array($input, $options))
		{
			Minion_CLI::write('This is not a valid option. Please try again.');

			$input = Minion_CLI::read($text, $options);
		}

		// Read the input
		return $input;
	}

	/**
	 * Experimental feature.
	 *
	 * Reads hidden input from the user
	 *
	 * Usage:
	 *
	 * $password = Minion_CLI::password('Enter your password');
	 *
	 * @author Mathew Davies.
	 * @return string
	 */
	public static function password($text = '')
	{
		$text .= ': ';

		if (KO7::$is_windows)
		{
			$vbscript = sys_get_temp_dir().'Minion_CLI_Password.vbs';

			// Create temporary file
			file_put_contents($vbscript, 'wscript.echo(InputBox("'.addslashes($text).'"))');

			$password = shell_exec('cscript //nologo '.escapeshellarg($vbscript));

			// Remove temporary file.
			unlink($vbscript);
		}
		else
		{
			$password = shell_exec('/usr/bin/env bash -c \'read -s -p "'.escapeshellcmd($text).'" var && echo $var\'');
		}

		Minion_CLI::write();

		return trim($password);
	}

	/**
	 * Outputs a string to the cli. If you send an array it will implode them
	 * with a line break.
	 *
	 * @param string|array $text the text to output, or array of lines
	 */
	public static function write($text = '')
	{
		if (is_array($text))
		{
			foreach ($text as $line)
			{
				Minion_CLI::write($line);
			}
		}
		else
		{
			fwrite(STDOUT, $text.PHP_EOL);
		}
	}

	/**
	 * Outputs a replacable line to the cli. You can continue replacing the
	 * line until `TRUE` is passed as the second parameter in order to indicate
	 * you are done modifying the line.
	 *
	 *     // Sample progress indicator
	 *     Minion_CLI::write_replace('0%');
	 *     Minion_CLI::write_replace('25%');
	 *     Minion_CLI::write_replace('50%');
	 *     Minion_CLI::write_replace('75%');
	 *     // Done writing this line
	 *     Minion_CLI::write_replace('100%', TRUE);
	 *
	 * @param string  $text      the text to output
	 * @param boolean $end_line  whether the line is done being replaced
	 */
	public static function write_replace($text = '', $end_line = FALSE)
	{
		// Append a newline if $end_line is TRUE
		$text = $end_line ? $text.PHP_EOL : $text;
		fwrite(STDOUT, "\r\033[K".$text);
	}

	/**
	 * Waits a certain number of seconds, optionally showing a wait message and
	 * waiting for a key press.
	 *
	 * @author     Fuel Development Team
	 * @license    MIT License
	 * @copyright  2010 - 2011 Fuel Development Team
	 * @link       http://fuelphp.com
	 * @param int $seconds number of seconds
	 * @param bool $countdown show a countdown or not
	 */
	public static function wait($seconds = 0, $countdown = false)
	{
		if ($countdown === TRUE)
		{
			$time = $seconds;

			while ($time > 0)
			{
				fwrite(STDOUT, $time.'... ');
				sleep(1);
				--$time;
			}

			Minion_CLI::write();
		}
		else
		{
			if ($seconds > 0)
			{
				sleep($seconds);
			}
			else
			{
				Minion_CLI::write(Minion_CLI::$wait_msg);
				Minion_CLI::read();
			}
		}
	}

	/**
	 * Returns the given text with the correct color codes for a foreground and
	 * optionally a background color.
	 *
	 * @author     Fuel Development Team
	 * @license    MIT License
	 * @copyright  2010 - 2011 Fuel Development Team
	 * @link       http://fuelphp.com
	 * @param string $text the text to color
	 * @param string $foreground the foreground color
	 * @param string $background the background color
	 * @return string the color coded string
	 */
	public static function color($text, $foreground, $background = null)
	{
		if (KO7::$is_windows)
			return $text;

		if ( ! array_key_exists($foreground, Minion_CLI::$foreground_colors))
			throw new KO7_Exception('Invalid CLI foreground color: '.$foreground);

		if ($background !== NULL AND ! array_key_exists($background, Minion_CLI::$background_colors))
			throw new KO7_Exception('Invalid CLI background color: '.$background);

		$string = "\033[".Minion_CLI::$foreground_colors[$foreground]."m";

		if ($background !== NULL)
		{
			$string .= "\033[".Minion_CLI::$background_colors[$background]."m";
		}

		$string .= $text."\033[0m";

		return $string;
	}

}
