/******************************************************************************
 *
 *  file:  Arg.h
 *
 *  Copyright (c) 2003, Michael E. Smoot .
 *  Copyright (c) 2004, Michael E. Smoot, Daniel Aarno .
 *  All rights reverved.
 *
 *  See the file COPYING in the top directory of this distribution for
 *  more information.
 *
 *  THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS
 *  OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 *  THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 *  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 *  DEALINGS IN THE SOFTWARE.
 *
 *****************************************************************************/


#ifndef TCLAP_ARGUMENT_H
#define TCLAP_ARGUMENT_H

#ifdef HAVE_CONFIG_H
 #include <config.h>
#else
 #define HAVE_SSTREAM
#endif

#include <string>
#include <vector>
#include <list>
#include <iostream>
#include <iomanip>
#include <cstdio>

#if defined (HAVE_SSTREAM)
 #include <sstream>
typedef std::istringstream istringstream;
#elif defined (HAVE_STRSTREAM)
 #include <strstream>
typedef std::istrstream istringstream;
#else
 #error "Need a stringstream (sstream or strstream) to compile!"
#endif

#include <tclap/ArgException.h>
#include <tclap/ArgTraits.h>
#include <tclap/CmdLineInterface.h>
#include <tclap/StandardTraits.h>
#include <tclap/Visitor.h>

namespace TCLAP
{
	/**
	 * A virtual base class that defines the essential data for all arguments.
	 * This class, or one of its existing children, must be subclassed to do
	 * anything.
	 */
	class Arg
	{
		private:

			/**
			 * Indicates whether the rest of the arguments should be ignored.
			 */
			static bool& ignoreRestRef()
			{
				static bool ign = false;
				return ign;
			}

			/**
			 * The delimiter that separates an argument flag/name from the
			 * value.
			 */
			static char& delimiterRef()
			{
				static char delim = ' ';
				return delim;
			}

		protected:
			/**
			 * The single char flag used to identify the argument.
			 * This value (preceded by a dash {-}), can be used to identify
			 * an argument on the command line.  The _flag can be blank,
			 * in fact this is how unlabeled args work.  Unlabeled args must
			 * override appropriate functions to get correct handling. Note
			 * that the _flag does NOT include the dash as part of the flag.
			 */
			std::string _flag;

			/**
			 * A single work namd indentifying the argument.
			 * This value (preceded by two dashed {--}) can also be used
			 * to identify an argument on the command line.  Note that the
			 * _name does NOT include the two dashes as part of the _name. The
			 * _name cannot be blank.
			 */
			std::string _name;

			/**
			 * Description of the argument.
			 */
			std::string _description;

			/**
			 * Indicating whether the argument is required.
			 */
			bool _required;

			/**
			 * Label to be used in usage description.  Normally set to
			 * "required", but can be changed when necessary.
			 */
			std::string _requireLabel;

			/**
			 * Indicates whether a value is required for the argument.
			 * Note that the value may be required but the argument/value
			 * combination may not be, as specified by _required.
			 */
			bool _valueRequired;

			/**
			 * Indicates whether the argument has been set.
			 * Indicates that a value on the command line has matched the
			 * name/flag of this argument and the values have been set accordingly.
			 */
			bool _alreadySet;

			/**
			 * A pointer to a vistitor object.
			 * The visitor allows special handling to occur as soon as the
			 * argument is matched.  This defaults to NULL and should not
			 * be used unless absolutely necessary.
			 */
			Visitor *_visitor;

			/**
			 * Whether this argument can be ignored, if desired.
			 */
			bool _ignoreable;

			/**
			 * Indicates that the arg was set as part of an XOR and not on the
			 * command line.
			 */
			bool _xorSet;

			bool _acceptsMultipleValues;

			/**
			 * Performs the special handling described by the Vistitor.
			 */
			void _checkWithVisitor() const;

			/**
			 * Primary constructor. YOU (yes you) should NEVER construct an Arg
			 * directly, this is a base class that is extended by various children
			 * that are meant to be used.  Use SwitchArg, ValueArg, MultiArg,
			 * UnlabeledValueArg, or UnlabeledMultiArg instead.
			 *
			 * \param flag - The flag identifying the argument.
			 * \param name - The name identifying the argument.
			 * \param desc - The description of the argument, used in the usage.
			 * \param req - Whether the argument is required.
			 * \param valreq - Whether the a value is required for the argument.
			 * \param v - The visitor checked by the argument. Defaults to NULL.
			 */
			Arg(const std::string &flag, const std::string &name, const std::string &desc, bool req,
			        bool valreq, Visitor *v = NULL);

		public:
			/**
			 * Destructor.
			 */
			virtual ~Arg();

			/**
			 * Adds this to the specified list of Args.
			 * \param argList - The list to add this to.
			 */
			virtual void addToList(std::list<Arg*> &argList) const;

			/**
			 * Begin ignoring arguments since the "--" argument was specified.
			 */
			static void beginIgnoring()
			{
				ignoreRestRef() = true;
			}

			/**
			 * Whether to ignore the rest.
			 */
			static bool ignoreRest()
			{
				return ignoreRestRef();
			}

			/**
			 * The delimiter that separates an argument flag/name from the
			 * value.
			 */
			static char delimiter()
			{
				return delimiterRef();
			}

			/**
			 * The char used as a place holder when SwitchArgs are combined.
			 * Currently set to the bell char (ASCII 7).
			 */
			static char blankChar()
			{
				return (char)7;
			}

			/**
			 * The char that indicates the beginning of a flag.  Currently '-'.
			 */
			static char flagStartChar()
			{
				return '-';
			}

			/**
			 * The sting that indicates the beginning of a flag.  Currently "-".
			 * Should be identical to flagStartChar.
			 */
			static const std::string flagStartString()
			{
				return "-";
			}

			/**
			 * The sting that indicates the beginning of a name.  Currently "--".
			 * Should be flagStartChar twice.
			 */
			static const std::string nameStartString()
			{
				return "--";
			}

			/**
			 * The name used to identify the ignore rest argument.
			 */
			static const std::string ignoreNameString()
			{
				return "ignore_rest";
			}

			/**
			 * Sets the delimiter for all arguments.
			 * \param c - The character that delimits flags/names from values.
			 */
			static void setDelimiter(char c)
			{
				delimiterRef() = c;
			}

			/**
			 * Pure virtual method meant to handle the parsing and value assignment
			 * of the string on the command line.
			 * \param i - Pointer the the current argument in the list.
			 * \param args - Mutable list of strings. What is
			 * passed in from main.
			 */
			virtual bool processArg(int *i, std::vector<std::string> &args) = 0;

			/**
			 * Operator ==.
			 * Equality operator. Must be virtual to handle unlabeled args.
			 * \param a - The Arg to be compared to this.
			 */
			virtual bool operator==(const Arg &a) const;

			/**
			 * Returns the argument flag.
			 */
			const std::string& getFlag() const;

			/**
			 * Returns the argument name.
			 */
			const std::string& getName() const;

			/**
			 * Returns the argument description.
			 */
			std::string getDescription() const;

			/**
			 * Indicates whether the argument is required.
			 */
			virtual bool isRequired() const;

			/**
			 * Sets _required to true. This is used by the XorHandler.
			 * You really have no reason to ever use it.
			 */
			void forceRequired();

			/**
			 * Sets the _alreadySet value to true.  This is used by the XorHandler.
			 * You really have no reason to ever use it.
			 */
			void xorSet();

			/**
			 * Indicates whether a value must be specified for argument.
			 */
			bool isValueRequired() const;

			/**
			 * Indicates whether the argument has already been set.  Only true
			 * if the arg has been matched on the command line.
			 */
			bool isSet() const;

			/**
			 * Indicates whether the argument can be ignored, if desired.
			 */
			bool isIgnoreable() const;

			/**
			 * A method that tests whether a string matches this argument.
			 * This is generally called by the processArg() method.  This
			 * method could be re-implemented by a child to change how
			 * arguments are specified on the command line.
			 * \param s - The string to be compared to the flag/name to determine
			 * whether the arg matches.
			 */
			virtual bool argMatches(const std::string &s) const;

			/**
			 * Returns a simple string representation of the argument.
			 * Primarily for debugging.
			 */
			virtual std::string toString() const;

			/**
			 * Returns a short ID for the usage.
			 * \param valueId - The value used in the id.
			 */
			virtual std::string shortID(const std::string &valueId = "val") const;

			/**
			 * Returns a long ID for the usage.
			 * \param valueId - The value used in the id.
			 */
			virtual std::string longID(const std::string &valueId = "val") const;

			/**
			 * Trims a value off of the flag.
			 * \param flag - The string from which the flag and value will be
			 * trimmed. Contains the flag once the value has been trimmed.
			 * \param value - Where the value trimmed from the string will
			 * be stored.
			 */
			virtual void trimFlag(std::string &flag, std::string &value) const;

			/**
			 * Checks whether a given string has blank chars, indicating that
			 * it is a combined SwitchArg.  If so, return true, otherwise return
			 * false.
			 * \param s - string to be checked.
			 */
			bool _hasBlanks(const std::string &s) const;

			/**
			 * Sets the requireLabel. Used by XorHandler.  You shouldn't ever
			 * use this.
			 * \param s - Set the requireLabel to this value.
			 */
			void setRequireLabel(const std::string &s);

			/**
			 * Used for MultiArgs and XorHandler to determine whether args
			 * can still be set.
			 */
			virtual bool allowMore();

			/**
			 * Use by output classes to determine whether an Arg accepts
			 * multiple values.
			 */
			virtual bool acceptsMultipleValues();

			/**
			 * Clears the Arg object and allows it to be reused by new
			 * command lines.
			 */
			virtual void reset();
	};

	/**
	 * Typedef of an Arg list iterator.
	 */
	typedef std::list<Arg*>::iterator ArgListIterator;

	/**
	 * Typedef of an Arg vector iterator.
	 */
	typedef std::vector<Arg*>::iterator ArgVectorIterator;

	/**
	 * Typedef of a Visitor list iterator.
	 */
	typedef std::list<Visitor*>::iterator VisitorListIterator;

	/*
	 * Extract a value of type T from it's string representation contained
	 * in strVal. The ValueLike parameter used to select the correct
	 * specialization of ExtractValue depending on the value traits of T.
	 * ValueLike traits use operator>> to assign the value from strVal.
	 */
	template<typename T>
	void ExtractValue(T &destVal, const std::string &strVal, ValueLike vl)
	{
		static_cast<void>(vl); // Avoid warning about unused vl
		istringstream is(strVal);

		int valuesRead = 0;
		while (is.good())
		{
			if (is.peek() != EOF)
			{
#ifdef TCLAP_SETBASE_ZERO
				is >> std::setbase(0);
#endif
				is >> destVal;
			}
			else
			{
				break;
			}

			valuesRead++;
		}

		if (is.fail())
		{
			throw (ArgParseException("Couldn't read argument value "
			                         "from string '" + strVal + "'"));
		}

		if (valuesRead > 1)
		{
			throw (ArgParseException("More than one valid value parsed from "
			                         "string '" + strVal + "'"));
		}
	}

	/*
	 * Extract a value of type T from it's string representation contained
	 * in strVal. The ValueLike parameter used to select the correct
	 * specialization of ExtractValue depending on the value traits of T.
	 * StringLike uses assignment (operator=) to assign from strVal.
	 */
	template<typename T>
	void ExtractValue(T &destVal, const std::string &strVal, StringLike sl)
	{
		static_cast<void>(sl); // Avoid warning about unused sl
		SetString(destVal, strVal);
	}

	inline Arg::Arg(const std::string &flag, const std::string &name, const std::string &desc,
	        bool req, bool valreq, Visitor *v) :
			_flag(flag), _name(name), _description(desc), _required(req), _requireLabel("required"),
			_valueRequired(valreq), _alreadySet(false), _visitor(v), _ignoreable(true),
			_xorSet(false), _acceptsMultipleValues(false)
	{
		if (_flag.length() > 1)
		{
			throw (SpecificationException(
							"Argument flag can only be one character long", toString()));
		}

		if (_name != ignoreNameString() && (_flag == Arg::flagStartString() ||
		        _flag == Arg::nameStartString() || _flag == " "))
		{
			throw (SpecificationException("Argument flag cannot be either '" + Arg::flagStartString() +
			      "' or '" + Arg::nameStartString() + "' or a space.", toString()));
		}

		if ((_name.substr(0, Arg::flagStartString().length()) == Arg::flagStartString()) ||
		        (_name.substr(0, Arg::nameStartString().length()) == Arg::nameStartString()) ||
		        (_name.find(" ", 0) != std::string::npos))
		{
			throw (SpecificationException("Argument name begin with either '" + Arg::flagStartString() +
			      "' or '" + Arg::nameStartString() + "' or space.", toString()));
		}
	}

	inline Arg::~Arg()
	{
	}

	inline std::string Arg::shortID(const std::string &valueId) const
	{
		std::string id = "";

		if (_flag != "")
		{
			id = Arg::flagStartString() + _flag;
		}
		else
		{
			id = Arg::nameStartString() + _name;
		}

		if (_valueRequired)
		{
			id += std::string(1, Arg::delimiter()) + "<" + valueId + ">";
		}

		if (!_required)
		{
			id = "[" + id + "]";
		}

		return id;
	}

	inline std::string Arg::longID(const std::string &valueId) const
	{
		std::string id = "";

		if (_flag != "")
		{
			id += Arg::flagStartString() + _flag;

			if (_valueRequired)
			{
				id += std::string(1, Arg::delimiter()) + "<" + valueId + ">";
			}

			id += ",  ";
		}

		id += Arg::nameStartString() + _name;

		if (_valueRequired)
		{
			id += std::string(1, Arg::delimiter()) + "<" + valueId + ">";
		}

		return id;
	}

	inline bool Arg::operator==(const Arg &a) const
	{
		if ((_flag != "" && _flag == a._flag) || _name == a._name)
		{
			return true;
		}
		else
		{
			return false;
		}
	}

	inline std::string Arg::getDescription() const
	{
		std::string desc = "";
		if (_required)
		{
			desc = "(" + _requireLabel + ")  ";
		}

		desc += _description;
		return desc;
	}

	inline const std::string& Arg::getFlag() const
	{
		return _flag;
	}

	inline const std::string& Arg::getName() const
	{
		return _name;
	}

	inline bool Arg::isRequired() const
	{
		return _required;
	}

	inline bool Arg::isValueRequired() const
	{
		return _valueRequired;
	}

	inline bool Arg::isSet() const
	{
		if (_alreadySet && !_xorSet)
		{
			return true;
		}
		else
		{
			return false;
		}
	}

	inline bool Arg::isIgnoreable() const
	{
		return _ignoreable;
	}

	inline void Arg::setRequireLabel(const std::string &s)
	{
		_requireLabel = s;
	}

	inline bool Arg::argMatches(const std::string &argFlag) const
	{
		if ((argFlag == Arg::flagStartString() + _flag && _flag != "") ||
		        argFlag == Arg::nameStartString() + _name)
		{
			return true;
		}
		else
		{
			return false;
		}
	}

	inline std::string Arg::toString() const
	{
		std::string s = "";

		if (_flag != "")
		{
			s += Arg::flagStartString() + _flag + " ";
		}

		s += "(" + Arg::nameStartString() + _name + ")";

		return s;
	}

	inline void Arg::_checkWithVisitor() const
	{
		if (_visitor != NULL)
		{
			_visitor->visit();
		}
	}

	/**
	 * Implementation of trimFlag.
	 */
	inline void Arg::trimFlag(std::string &flag, std::string &value) const
	{
		int stop = 0;
		for (int i = 0; static_cast<unsigned int>(i) < flag.length(); i++)
		{
			if (flag[i] == Arg::delimiter())
			{
				stop = i;
				break;
			}
		}

		if (stop > 1)
		{
			value = flag.substr(stop + 1);
			flag  = flag.substr(0, stop);
		}
	}

	/**
	 * Implementation of _hasBlanks.
	 */
	inline bool Arg::_hasBlanks(const std::string &s) const
	{
		for (int i = 1; static_cast<unsigned int>(i) < s.length(); i++)
		{
			if (s[i] == Arg::blankChar())
			{
				return true;
			}
		}

		return false;
	}

	inline void Arg::forceRequired()
	{
		_required = true;
	}

	inline void Arg::xorSet()
	{
		_alreadySet = true;
		_xorSet     = true;
	}

	/**
	 * Overridden by Args that need to added to the end of the list.
	 */
	inline void Arg::addToList(std::list<Arg*> &argList) const
	{
		argList.push_front(const_cast<Arg*>(this));
	}

	inline bool Arg::allowMore()
	{
		return false;
	}

	inline bool Arg::acceptsMultipleValues()
	{
		return _acceptsMultipleValues;
	}

	inline void Arg::reset()
	{
		_xorSet     = false;
		_alreadySet = false;
	}
}

#endif
