
/****************************************************************************** 
 * 
 *  file:  SwitchArg.h
 * 
 *  Copyright (c) 2003, Michael E. Smoot .
 *  Copyright (c) 2004, Michael E. Smoot, Daniel Aarno.
 *  All rights reserved.
 * 
 *  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();

	private:
		/**
		 * Checks to see if we've found the last match in
		 * a combined string.
		 */
		bool lastCombined(std::string& combined);

		/**
		 * Does the common processing of processArg.
		 */
		void commonProcessing();
};

//////////////////////////////////////////////////////////////////////
//BEGIN SwitchArg.cpp
//////////////////////////////////////////////////////////////////////
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::lastCombined(std::string& combinedSwitches ) 
{
	for ( unsigned int i = 1; i < combinedSwitches.length(); i++ )
		if ( combinedSwitches[i] != Arg::blankChar() )
			return false;
	
	return true;
}

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 void SwitchArg::commonProcessing()
{
	if ( _xorSet )
		throw(CmdLineParseException(
		      "Mutually exclusive argument already set!", toString()));

	if ( _alreadySet ) 
		throw(CmdLineParseException("Argument already set!", toString()));

	_alreadySet = true;

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

	_checkWithVisitor();
}

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

	// if the whole string matches the flag or name string
	if ( argMatches( args[*i] ) )
	{
		commonProcessing();

		return true;
	}
	// if a substring matches the flag as part of a combination
	else if ( combinedSwitchesMatch( args[*i] ) )
	{
		// check again to ensure we don't misinterpret 
		// this as a MultiSwitchArg 
		if ( combinedSwitchesMatch( args[*i] ) )
			throw(CmdLineParseException("Argument already set!", 
			                            toString()));

		commonProcessing();

		// We only want to return true if we've found the last combined
		// match in the string, otherwise we return true so that other 
		// switches in the combination will have a chance to match.
		return lastCombined( args[*i] );
	}
	else
		return false;
}

inline void SwitchArg::reset()
{
	Arg::reset();
	_value = _default;  
}
//////////////////////////////////////////////////////////////////////
//End SwitchArg.cpp
//////////////////////////////////////////////////////////////////////

} //namespace TCLAP

#endif
