#!/usr/bin/env php
<?php
/**
 * CSSTidy - Command Line Interface (CLI)
 *
 * This file is part of CSSTidy.
 *
 *   CSSTidy is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU Lesser General Public License as published by
 *   the Free Software Foundation; either version 2.1 of the License, or
 *   (at your option) any later version.
 *
 *   CSSTidy is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Lesser General Public License for more details.
 *
 *   You should have received a copy of the GNU Lesser General Public License
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * @license http://opensource.org/licenses/lgpl-license.php GNU Lesser General Public License
 * @package csstidy
 * @author Florian Schmitz (floele at gmail dot com) 2005-2007
 * @author Brett Zamir (brettz9 at yahoo dot com) 2007
 * @author Nikolay Matsievsky (speed at webo dot name) 2009-2010
 * @author Christopher Finke (cfinke at gmail.com) 2012
 * @author Etienne Desautels (etienne dot desautels at gmail dot com) 2012
 * @author Cedric Morin (cedric at yterium dot com) 2010-2019
 */


error_reporting(E_ALL);

if (version_compare(PHP_VERSION, '5.4')<0){
	die('Requires PHP 5.4 or above');
}

/**
 * Contains the Parser class
 *
 * @version 1.6.5
 */
require dirname(__DIR__) . DIRECTORY_SEPARATOR . 'class.csstidy.php';
$csstidy = new csstidy();
$dumpTree = false;
$default_media = '';

/**
 * Return the usage message to sdterr and exit with return code 1
 * @access private
 * @version 1.0
 */
function _show_usage(){
	$exe = $GLOBALS['argv'][0];
	$version = "Version " . $GLOBALS['csstidy']->version;

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

Options include:

-h, --help                   Show this message
-t=template                  Set the output template [default|filename|low|high|highest]
--case_properties            [1|2|0]
--compress_colors            [true|false]
--compress_font-weight       [true|false]
--default_media              Set the default media rule to apply to the CSS
--css_level                  [CSS3.0|CSS2.1|CSS2.0|CSS1.0]
--discard_invalid_properties [false|true]
--discard_invalid_selectors  [false|true]
--lowercase_s                [false|true]
--merge_selectors            [0|1|2]
--optimise_shorthands        [1|2|0]
--preserve_css               [false|true]
--remove_bslash              [true|false]
--remove_last_semicolon      [false|true]
--reverse_left_and_right     [false|true]
--sort_properties            [false|true]
--sort_selectors             [false|true]
--timestamp                  [false|true]

-T                  Dump formatted parse tree
-v, --version       Print the version

EOT;
	exit($HELP);
}


/**
 * Parse argument
 *
 * @param integer $i
 * @param array $options
 *
 * @return string|null
 */
function parseArgument(&$i, $options){
	global $argc;
	global $argv;

	if (!preg_match('/^(?:' . implode('|', (array)$options) . ')=?(.*)/', $argv[$i], $matches)){
		return;
	}

	if (strlen($matches[1])){
		return $matches[1];
	}

	if ($i+1<$argc){
		$i++;

		return $argv[$i];
	}
}

/**
 * Cast Arguments relative to expected format
 * @param mixed $value
 * @param string $format
 * @param array $expected
 * @return mixed
 */
function castArgument($value, $format, $expected = null) {
	if ($expected and !in_array($value, $expected)) {
		if (in_array('filename', $expected)) {
			$fileName = castArgument($value, $format);
			if (!file_exists($fileName)) {
				_show_usage();
			}
		}
		else {
			_show_usage();
		}
	}
	switch (strtolower($format)) {
		case "int":
			$value = intval($value);
			break;
		case "str":
			$value = trim(strval($value));
			foreach (["'", '"'] as $q) {
				if (substr($value, 0, 1) == $q && substr($value, -1, 1) == $q) {
					$value = substr($value, 1, -1);
					$value = str_replace("\\" . $q, $q, $value);
					break;
				}
			}
			break;
		case "bool":
			$value = strtolower(trim(strval($value)));
			if (!in_array($value, [ '0', '1', 'on', 'off', 'true', 'false'])) {
				_show_usage();
			}
			$value = (in_array(strtolower($value), ['1', 'on', 'true']) ? true : false);
			break;
	}
	return $value;
}

// Check if there's at least one argument
if ($argc<1){
	_show_usage();
}

// Set the settings defined on the CLI
$settings = [
	'case_properties' => ['format' => 'int', 'expected' => [0, 1, 2]],
	'compress_colors' => ['format' => 'bool'],
	'compress_font-weight' => ['format' => 'bool'],
	'css_level' => ['format' => 'str', 'expected' => ['CSS3.0', 'CSS2.1', 'CSS2.0', 'CSS1.0']],
	'discard_invalid_properties' => ['format' => 'bool'],
	'discard_invalid_selectors' => ['format' => 'bool'],
	'merge_selectors' => ['format' => 'int', 'expected' => [0, 1, 2]],
	'lowercase_s' => ['format' => 'bool'],
	'optimise_shorthands' => ['format' => 'int', 'expected' => [0, 1, 2]],
	'preserve_css' => ['format' => 'bool'],
	'remove_bslash' => ['format' => 'bool'],
	'remove_last_semicolon' => ['format' => 'bool', 'setting' => 'remove_last_;'],
	'reverse_left_and_right' => ['format' => 'bool'],
	'sort_properties' => ['format' => 'bool'],
	'sort_selectors' => ['format' => 'bool'],
	'template' => ['format' => 'str', 'short' => 't', 'expected' => ['default', 'filename', 'low', 'high', 'highest']],
	'timestamp' => ['format' => 'bool'],
];

foreach ($settings as $option_name => $desc) {
	$options_list = [ "--$option_name" ];
	if (isset($desc['short'])) {
		$options_list[] = "-" . $desc['short'];
	}
	$settings[$option_name]['options_list'] = $options_list;
}


for ($i = 1; $i<$argc; $i++){
	if ($argv[$i]==='-h' || $argv[$i]==='--help'){
		_show_usage();
	}

	if ($argv[$i]==='-v' || $argv[$i]==='--version'){
		exit($csstidy->version . "\n");
	}
	$value = parseArgument($i, ['--default_media']);
	if (isset($value)) {
		$value = castArgument($value, 'str');
		$default_media = trim($value);
		if (strpos($default_media, '@') === false) {
			$default_media = '@media ' . $default_media;
		}
	}

	foreach ($settings as $option_name => $desc) {
		$value = parseArgument($i, $desc['options_list']);
		if (isset($value)){
			if (isset($desc['format'])) {
				$value = castArgument($value, $desc['format'], isset($desc['expected']) ? $desc['expected'] : null);
			}
			$setting_name = $option_name;
			if (isset($desc['setting'])) {
				$setting_name = $desc['setting'];
			}
			$csstidy->set_cfg($setting_name, $value);
			continue 2;
		}
	}

	if ($argv[$i]==='-T'){
		$dumpTree = true;
		continue;
	}

	if (file_exists($argv[$i])){
		$inputFile = $argv[$i];
		continue;
	}
}

if (!$inputFile) {
	$inputFile = 'php://stdin';
}

// Get the data
$css_code = file_get_contents($inputFile);

// Exit on error when reading the data
if ($css_code===false){
	file_put_contents('php://stderr', "The file \"" . $inputFile . "\" does not exist.\n");
	exit(1);
}

// Parse the CSS
$csstidy->parse($css_code);
if ($dumpTree){
	var_dump($csstidy->css);
} else {
	// Optimize and output the CSS file
	echo $csstidy->print->plain($default_media);
	echo "\n";
}