<?php
require_once( dirname( __FILE__ ) . '/add-textdomain.php' );

class GruntAddTextdomain extends AddTextdomain {
	/**
	 * List of text domains to update.
	 *
	 * @type array
	 */
	protected $domains_to_update = array();

	/**
	 * Whether to update all text domains.
	 *
	 * @type boolean
	 */
	protected $update_all_domains = false;

	/**
	 * List of translation functions and the position of the text domain argument.
	 *
	 * @type array
	 */
	protected $domain_positions = array(
		'__'         => 2,
		'_e'         => 2,
		'_x'         => 3,
		'_ex'        => 3,
		'_n'         => 4,
		'_nx'        => 5,
		'_n_noop'    => 3,
		'_nx_noop'   => 4,
		'esc_attr__' => 2,
		'esc_html__' => 2,
		'esc_attr_e' => 2,
		'esc_html_e' => 2,
		'esc_attr_x' => 3,
		'esc_html_x' => 3,
	);

	/**
	 * Add textdomain to a set of PHP tokens.
	 *
	 * @param string $domain Text domain.
	 * @param array  $tokens PHP tokens. An array of token identifiers. Each individual token identifier is either a
	 *                       single character (i.e.: ;, ., >, !, etc.), or a three element array containing the token
	 *                       index in element 0, the string content of the original token in element 1 and the line
	 *                       number in element 2.
	 *
	 * @return string Modified source.
	 */
	public function process_tokens( $domain, $tokens ) {
		$this->modified_contents = '';
		$domain = addslashes( $domain );

		$in_func = false;
		$current_func = null;
		$args_started = false;
		$arg_position = 0;
		$parens_balance = 0;
		$found_domain = false;

		foreach( $tokens as $index => $token ) {
			$string_success = false;
			if ( is_array( $token ) ) {
				list( $id, $text ) = $token;
				if ( T_STRING == $id && in_array( $text, $this->funcs ) ) {
					$in_func        = true;
					$current_func   = $text;
					$parens_balance = 0;
					$args_started   = false;
					$found_domain   = false;
				} elseif ( T_CONSTANT_ENCAPSED_STRING == $id && ( "'$domain'" == $text || "\"$domain\"" == $text ) ) {
					if ( $in_func && $args_started ) {
						$found_domain = true;
					}
				} elseif ( T_CONSTANT_ENCAPSED_STRING == $id && ! empty( $current_func ) && isset( $this->domain_positions[ $current_func ] ) ) {
					$is_domain_match = in_array( trim( $text, '\'"' ), $this->domains_to_update );
					$is_domain_arg   = $this->update_all_domains && $arg_position === $this->domain_positions[ $current_func ] - 1;

					if ( $in_func && $args_started && ( $is_domain_match || $is_domain_arg ) ) {
						$text = preg_replace( '/([\'"]).+[\'"]$/', '$1' . $domain . '$1', $text );
						$found_domain = true;
					}
				}
				$token = $text;
			} elseif ( '(' == $token ) {
				$args_started = true;
				++$parens_balance;
			} elseif ( $in_func && ',' == $token ) {
				++$arg_position;
			} elseif ( ')' == $token ) {
				--$parens_balance;
				if ( $in_func && 0 == $parens_balance ) {
					if ( ! $found_domain ) {
						$token = ", '$domain'";
						if ( T_WHITESPACE == $tokens[ $index - 1 ][0] ) {
							$token .= ' '; // Maintain code standards if previously present
							// Remove previous whitespace token to account for it.
							$this->modified_contents = trim( $this->modified_contents );
						}
						$token .= ')';
					}
					$in_func      = false;
					$current_func = null;
					$args_started = false;
					$arg_position = 0;
					$found_domain = false;
				}
			}
			$this->modified_contents .= $token;
		}

		return $this->modified_contents;
	}

	/**
	 * Normalize and set a list of text domains to update.
	 *
	 * @param string|array $domains Comma-separated string or array of domains to update.
	 */
	public function set_domains_to_update( $domains = array() ) {
		if ( 'all' === $domains ) {
			$this->update_all_domains = true;
			$domains = array();
		} elseif ( is_string( $domains ) ) {
			$domains = explode( ',', $domains );
		}

		// Remove empty items and non-strings.
		$this->domains_to_update = array_filter( array_filter( (array) $domains ), 'is_string' );
	}
}

/**
 * CLI interface.
 *
 * Run the CLI only if the file wasn't included.
 */
$included_files = get_included_files();
if ( __FILE__ == $included_files[0] ) {
	$adddomain = new GruntAddTextdomain;

	$inplace = false;
	if ( '-i' == $argv[1] ) {
		$inplace = true;
	}

	// Set text domains to update.
	if ( isset( $argv[4] ) ) {
		$adddomain->set_domains_to_update( $argv[4] );
	}

	$adddomain->process_file( $argv[2], $argv[3], $inplace );
}
