/******************************************************************************
 *
 *  file:  SwitchArg.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_SWITCH_ARG_H
#define TCLAP_SWITCH_ARG_H

#include <string>
#include <vector>

#include <tclap/Arg.h>

namespace TCLAP
{
	/**
	 * A simple switch argument.  If the switch is set on the command line, then
	 * the getValue method will return the opposite of the default value for the
	 * switch.
	 */
	class SwitchArg : public Arg
	{
		protected:
			/**
			 * The value of the switch.
			 */
			bool _value;

			/**
			 * Used to support the reset() method so that ValueArg can be
			 * reset to their constructed value.
			 */
			bool _default;

		public:
			/**
			* SwitchArg constructor.
			* \param flag - The one character flag that identifies this
			* argument on the command line.
			* \param name - A one word name for the argument.  Can be
			* used as a long flag on the command line.
			* \param desc - A description of what the argument is for or
			* does.
			* \param def - The default value for this Switch.
			* \param v - An optional visitor.  You probably should not
			* use this unless you have a very good reason.
			*/
			SwitchArg(const std::string &flag, const std::string &name, const std::string &desc,
			        bool def = false, Visitor *v = NULL);


			/**
			 * SwitchArg constructor.
			 * \param flag - The one character flag that identifies this
			 * argument on the command line.
			 * \param name - A one word name for the argument.  Can be
			 * used as a long flag on the command line.
			 * \param desc - A description of what the argument is for or
			 * does.
			 * \param parser - A CmdLine parser object to add this Arg to
			 * \param def - The default value for this Switch.
			 * \param v - An optional visitor.  You probably should not
			 * use this unless you have a very good reason.
			 */
			SwitchArg(const std::string &flag, const std::string &name, const std::string &desc,
			        CmdLineInterface &parser, bool def = false, Visitor *v = NULL);


			/**
			* Handles the processing of the argument.
			* This re-implements the Arg version of this method to set the
			* _value of the argument appropriately.
			* \param i - Pointer the the current argument in the list.
			* \param args - Mutable list of strings. Passed
			* in from main().
			*/
			virtual bool processArg(int *i, std::vector<std::string> &args);

			/**
			 * Checks a string to see if any of the chars in the string
			 * match the flag for this Switch.
			 */
			bool combinedSwitchesMatch(std::string &combined);

			/**
			 * Returns bool, whether or not the switch has been set.
			 */
			bool getValue();

			virtual void reset();
	};

	inline SwitchArg::SwitchArg(const std::string &flag, const std::string &name,
	        const std::string &desc, bool default_val, Visitor *v) :
			Arg(flag, name, desc, false, false, v), _value(default_val), _default(default_val)
	{
	}

	inline SwitchArg::SwitchArg(const std::string &flag, const std::string &name,
	        const std::string &desc, CmdLineInterface &parser, bool default_val,
	        Visitor *v) :
			Arg(flag, name, desc, false, false, v), _value(default_val), _default(default_val)
	{
		parser.add(this);
	}

	inline bool SwitchArg::getValue()
	{
		return _value;
	}

	inline bool SwitchArg::combinedSwitchesMatch(std::string &combinedSwitches)
	{
		// make sure this is actually a combined switch
		if (combinedSwitches.length() > 0 &&
		        combinedSwitches[0] != Arg::flagStartString()[0])
		{
			return false;
		}

		// make sure it isn't a long name
		if (combinedSwitches.substr(0, Arg::nameStartString().length()) == Arg::nameStartString())
		{
			return false;
		}

		// make sure the delimiter isn't in the string
		if (combinedSwitches.find_first_of(Arg::delimiter()) != std::string::npos)
		{
			return false;
		}

		// ok, we're not specifying a ValueArg, so we know that we have
		// a combined switch list.
		for (unsigned int i = 1; i < combinedSwitches.length(); i++)
		{
			if (_flag.length() > 0 &&
			        combinedSwitches[i] == _flag[0] &&
			        _flag[0] != Arg::flagStartString()[0])
			{
				// update the combined switches so this one is no longer present
				// this is necessary so that no unlabeled args are matched
				// later in the processing.
				//combinedSwitches.erase(i,1);
				combinedSwitches[i] = Arg::blankChar();
				return true;
			}
		}

		// none of the switches passed in the list match.
		return false;
	}

	inline bool SwitchArg::processArg(int *i, std::vector<std::string> &args)
	{
		if (_ignoreable && Arg::ignoreRest())
		{
			return false;
		}

		if (argMatches(args[*i]) || combinedSwitchesMatch(args[*i]))
		{
			// If we match on a combined switch, then we want to return false
			// so that other switches in the combination will also have a
			// chance to match.
			bool ret = false;
			if (argMatches(args[*i]))
			{
				ret = true;
			}

			if (_alreadySet || (!ret && combinedSwitchesMatch(args[*i])))
			{
				throw (CmdLineParseException("Argument already set!", toString()));
			}

			_alreadySet = true;

			if (_value == true)
			{
				_value = false;
			}
			else
			{
				_value = true;
			}

			_checkWithVisitor();

			return ret;
		}
		else
		{
			return false;
		}
	}

	inline void SwitchArg::reset()
	{
		Arg::reset();
		_value = _default;
	}
}

#endif
