/******************************************************************************
 *
 *  file:  XorHandler.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_XORHANDLER_H
#define TCLAP_XORHANDLER_H

#include <tclap/Arg.h>
#include <string>
#include <vector>
#include <algorithm>
#include <iostream>

namespace TCLAP
{

	/**
	 * This class handles lists of Arg's that are to be XOR'd on the command
	 * line.  This is used by CmdLine and you shouldn't ever use it.
	 */
	class XorHandler
	{
		protected:
			/**
			 * The list of of lists of Arg's to be or'd together.
			 */
			std::vector<std::vector<Arg*>> _orList;

		public:
			/**
			 * Constructor.  Does nothing.
			 */
			XorHandler()
			{
			}

			/**
			 * Add a list of Arg*'s that will be orred together.
			 * \param ors - list of Arg* that will be xor'd.
			 */
			void add(std::vector<Arg*> &ors);

			/**
			 * Checks whether the specified Arg is in one of the xor lists and
			 * if it does match one, returns the size of the xor list that the
			 * Arg matched.  If the Arg matches, then it also sets the rest of
			 * the Arg's in the list. You shouldn't use this.
			 * \param a - The Arg to be checked.
			 */
			int check(const Arg *a);

			/**
			 * Returns the XOR specific short usage.
			 */
			std::string shortUsage();

			/**
			 * Prints the XOR specific long usage.
			 * \param os - Stream to print to.
			 */
			void printLongUsage(std::ostream &os);

			/**
			 * Simply checks whether the Arg is contained in one of the arg
			 * lists.
			 * \param a - The Arg to be checked.
			 */
			bool contains(const Arg *a);

			std::vector<std::vector<Arg*>>& getXorList();
	};

	inline void XorHandler::add(std::vector<Arg*> &ors)
	{
		_orList.push_back(ors);
	}

	inline int XorHandler::check(const Arg *a)
	{
		// iterate over each XOR list
		for (int i = 0; static_cast<unsigned int>(i) < _orList.size(); i++)
		{
			// if the XOR list contains the arg..
			ArgVectorIterator ait = std::find(_orList[i].begin(), _orList[i].end(), a);
			if (ait != _orList[i].end())
			{
				// go through and set each arg that is not a
				for (ArgVectorIterator it = _orList[i].begin();
				        it != _orList[i].end();
				        it++)
				{
					if (a != (*it))
					{
						(*it)->xorSet();
					}
				}

				// return the number of required args that have now been set
				if ((*ait)->allowMore())
				{
					return 0;
				}
				else
				{
					return static_cast<int>(_orList[i].size());
				}
			}
		}

		if (a->isRequired())
		{
			return 1;
		}
		else
		{
			return 0;
		}
	}

	inline bool XorHandler::contains(const Arg *a)
	{
		for (int i = 0; static_cast<unsigned int>(i) < _orList.size(); i++)
		{
			for (ArgVectorIterator it = _orList[i].begin();
			        it != _orList[i].end();
			        it++)
			{
				if (a == (*it))
				{
					return true;
				}
			}
		}

		return false;
	}

	inline std::vector<std::vector<Arg*>>& XorHandler::getXorList()
	{
		return _orList;
	}
}

#endif
