/*
 * \brief  Utility for command-line parsing
 * \author Norman Feske
 * \date   2013-03-18
 */

/*
 * Copyright (C) 2013 Genode Labs GmbH
 *
 * This file is part of the Genode OS framework, which is distributed
 * under the terms of the GNU General Public License version 2.
 */

#ifndef _COMMAND_LINE_H_
#define _COMMAND_LINE_H_

#include <line_editor.h>

class Command_line
{
	private:

		char const *_cmd_line;
		Command    &_command;

		bool _parameter_is_known(Token token)
		{
			return Argument_tracker::lookup(token, _command.parameters()) != 0;
		}

		Token _tag_token(char const *tag)
		{
			for (Token token(_cmd_line); token; token = token.next())
				if (strcmp(token.start(), tag, token.len()) == 0
				 && strlen(tag) == token.len()
				 && _parameter_is_known(token))
					return token;

			return Token();
		}

		Token _value_token(char const *tag)
		{
			return _tag_token(tag).next().next();
		}

		static bool _is_parameter(Token token)
		{
			return token[0] == '-' && token[1] == '-';
		}


	public:

		/**
		 * Constructor
		 *
		 * \param cmd_line  null-terminated command line string
		 * \param command   meta data about the command
		 */
		Command_line(char const *cmd_line, Command &command)
		: _cmd_line(cmd_line), _command(command) { }

		/**
		 * Return true if tag is specified at the command line
		 */
		bool parameter_exists(char const *tag)
		{
			return _tag_token(tag);
		}

		/**
		 * Return number argument specified for the given tag
		 */
		template <typename T>
		bool parameter(char const *tag, T &result)
		{
			Token value = _value_token(tag);
			return value && Genode::ascii_to(value.start(), result) != 0;
		}

		/**
		 * Return string argument specified for the given tag
		 */
		bool parameter(char const *tag, char *result, size_t result_len)
		{
			Token value = _value_token(tag);
			if (!value)
				return false;

			value.string(result, result_len);
			return true;
		}

		bool argument(unsigned index, char *result, size_t result_len)
		{
			Argument_tracker argument_tracker(_command);

			/* argument counter */
			unsigned cnt = 0;

			for (Token token(_cmd_line); token; token = token.next()) {

				argument_tracker.supply_token(token);

				if (!argument_tracker.valid())
					return false;

				if (!argument_tracker.expect_arg())
					continue;

				Token arg = token.next();
				if (!arg)
					return false;

				/*
				 * The 'arg' token could either the tag of a parameter or
				 * an argument. We only want to count the arguments. So
				 * we skip tokens that have the usual form a parameter tag.
				 */
				if (_is_parameter(arg))
					continue;

				if (cnt == index) {
					arg.string(result, result_len);
					return true;
				}
				cnt++;
			}
			return false;
		}

		/**
		 * Validate parameter tags
		 *
		 * \return tag token of first unexpected parameter, or
		 *         invalid token if no unexpected parameter was found
		 */
		Token unexpected_parameter()
		{
			Argument_tracker argument_tracker(_command);

			for (Token token(_cmd_line); token; token = token.next()) {

				argument_tracker.supply_token(token);

				if (!argument_tracker.valid())
					return token;

				if (!argument_tracker.expect_arg())
					continue;

				Token arg = token.next();

				/* ignore non-parameter tokens (i.e., normal arguments) */
				if (!_is_parameter(arg))
					continue;

				/* if parameter with the given tag exists, we are fine */
				if (_parameter_is_known(arg))
					continue;

				/* we hit an unknown parameter tag */
				return arg;
			}
			return Token();
		}
};

#endif /* _COMMAND_LINE_H_ */
