//
// The developer of the original code and/or files is Tripwire, Inc.
// Portions created by Tripwire, Inc. are copyright (C) 2000 Tripwire,
// Inc. Tripwire is a registered trademark of Tripwire, Inc.  All rights
// reserved.
// 
// This program is free software.  The contents of this file are subject
// to the terms of the GNU General Public License as published by the
// Free Software Foundation; either version 2 of the License, or (at your
// option) any later version.  You may redistribute it and/or modify it
// only in compliance with the GNU General Public License.
// 
// This program is distributed in the hope that it will be useful.
// However, this program is distributed AS-IS WITHOUT ANY
// WARRANTY; INCLUDING THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS
// FOR A PARTICULAR PURPOSE.  Please see the GNU General Public License
// for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
// USA.
// 
// Nothing in the GNU General Public License or any other license to use
// the code or files shall permit you to use Tripwire's trademarks,
// service marks, or other intellectual property without Tripwire's
// prior written consent.
// 
// If you have any questions, please contact Tripwire, Inc. at either
// info@tripwire.org or www.tripwire.org.
//
///////////////////////////////////////////////////////////////////////////////
// cmdlineparser.h
//
// cCmdLineParser.h -- class that tokenizes the command line 
#ifndef __CMDLINEPARSER_H
#define __CMDLINEPARSER_H

#ifndef __HASHTABLE_H
#include "hashtable.h"
#endif
#ifndef __DEBUG_H
#include "debug.h"
#endif
#ifndef __ERROR_H
#include "error.h"
#endif

//=============================================================================
// eCmdLine
//=============================================================================
TSS_EXCEPTION( eCmdLine,			eError )
TSS_EXCEPTION( eCmdLineInvalidArg,	eCmdLine ) // an arg on the command line is not recognized
TSS_EXCEPTION( eCmdLineBadArgParam, eCmdLine ) // wrong number of parameters to an argument
TSS_EXCEPTION( eCmdLineBadParam,	eCmdLine ) // wrong number of paramters to the executable (not associated with any arguments)
TSS_EXCEPTION( eCmdLineBadSwitchPos,eCmdLine ) // a '-' arg appeared after the final parameter list
TSS_EXCEPTION( eCmdLineMutEx,		eCmdLine ) // a mutual exclusion error has occured
TSS_EXCEPTION( eCmdLineDependency,	eCmdLine ) // a dependency error has occurred.
TSS_EXCEPTION( eCmdLineMultiArg,	eCmdLine ) // an arg was found twice in the command line


/*
	// cCmdLineParser owns errors 600-699
	// these can be turned into a string by using cErrorTable
	enum ErrorType
	{
		ERR_NONE					= 601,	// no error
		ERR_INVALID_ARG				= 602,	// an arg on the command line is not recognized
		ERR_BAD_ARG_PARAMS			= 603,	// wrong number of parameters to an argument
		ERR_BAD_PARAMS				= 604,	// wrong number of paramters to the executable (not associated with any arguments)
		ERR_SWITCH_AFTER_FINAL_LIST = 605,	// a '-' arg appeared after the final paramter list
		ERR_MUTUAL_EXCLUSION		= 606,	// a mutual exclusion error has occured
		ERR_MULTIPLE_ARG			= 607,	// an arg was found twice in the command line

		ERR_INVALID			// top of enum
	};

	// for storing error information
	ErrorType						mCurError;
	TSTRING							mCurErrorString;
	void GetErrorInfo(ErrorType& et, TSTRING& errorData) const;
		// returns information on the type of error that occured in a Parse() command. Only 
		// returns valid information if Parse() had just been called and returned false. A 
		// second call to Parse() might alter existing error info
*/

//=============================================================================
// cCmdLineParser
//=============================================================================
class cCmdLineParser
{
public:
	cCmdLineParser();
	~cCmdLineParser();

	enum ParamCount
	{
		PARAM_NONE,		// no parameters to arg
		PARAM_ONE,		// one parameter to arg
		PARAM_MANY,		// zero or more paramters to arg

		PARAM_INVALID	// top of enum
	};

	void AddArg(int argId, const TSTRING& arg, const TSTRING& alias, ParamCount numParams, bool multipleAllowed = false);
		// this method should be called for each argument that can appear on the 
		// command line. 
		// argId -- a number that uniquely identifies the argument; no two arguments
		//		may have the same id (ASSERT-enforced)
		// arg	 -- string that comes after the '-'. can be _T("") if there is only
		//		a string representation
		// alias -- string that comes after '--' which has the same meaning. Can be _T("")
		//		if there is no alias. If both arg and alias are empty strings, then this arg
		//		represents the list of arguments that comes at the end of the command line
		// numParams -- number of parameters that this argument needs

	void AddMutEx(int argId1, int argId2);
		// this adds a mutual exclusion constraint. When Parse() is called, if argId1 and 
		// argId2 both exist on the command line, then the parse will fail and the error 
		// value ERR_MUTUAL_EXCLUSION will be set.

	void AddDependency(int argId1, int argId2, bool mutual = false );
		// This adds a dependency constraint.   When Parse() is called, if argId1 
		// exists on the command line independent from argId2, then the parse will fail.
		// If the default param mutual is true, then the command parser will check for
		// argId1 if argId2 is passed.   We do this, since it is possible for one arg to 
		// depend on another, but have the other arg alone on the command line, legally.

	void Parse(int argc, const TCHAR *const * argv); // throw eCmdLine
		// after AddArg() has been called for every argument that could be processed by the
		// command line, call this to tokenize argv. If the return value is false, then 
		// the input was invalid in some way; the actual error can be determined by calling
		// GetErrorInfo() below.

	void Clear();
		// clear out all information that this class contains

	bool LookupArgInfo(int argId, TSTRING& arg, TSTRING& alias) const;
		// given an argId, fill out the strings with the argument and alias strings. Returns false
		// if the argId cannot be found. This method is not very fast, so don't use it often.

    #ifdef _DEBUG
	void TraceContents(int dl = -1) ;
    #endif
private:
	void TestMutEx();
		// tests for mutual exclusion violations; if it fails, the current error is set and false
		// is returned.
	void TestDependency();
		// tests for all dependency violations.
	bool ArgInList(int argId);
		// returns true if an argument with the specified id already exists in the list; this is used
		// to make sure the same arg doesn't appear >1 time on the command line

	// for storing information on paramers
	struct cArgInfo
	{
		int			mId;
		ParamCount	mNumParams;

		cArgInfo(int i = -1, ParamCount p = PARAM_INVALID) : mId(i), mNumParams(p) {}
	};
	// for storing parsed argv information
	struct cArgData
	{
		int						mId;
		std::vector<TSTRING>	mParams;
		TSTRING					mActualParam;	// a string representation of what was actually on the command line

		cArgData(int id = -1, const TSTRING& actualParam = TSTRING(_T(""))) : mId(id), mActualParam(actualParam) {}
	};

	cHashTable<TSTRING, cArgInfo>	mArgTable;
	cArgInfo						mLastArgInfo;	// info on the argument that comes at the end of the command line (with no associated '-x' or '--x')
	std::list<cArgData>				mArgData;
	std::list<std::pair<int,int> >	mMutExList;		 // all of the mutual exclusions
	std::list< std::pair < std::pair<int,int>, bool > >	mDependencyList; // all of the dependencies
	std::set< int >					mMultipleAllowed;

	friend class cCmdLineIter;
};

///////////////////////////////////////////////////////////////////////////////
// cCmdLineIter -- used to iterate over the tokenized command line parameters; 
//		is only useful after cCmdLineParser::Parse() has been called.
///////////////////////////////////////////////////////////////////////////////
class cCmdLineIter
{
public:
	cCmdLineIter(const cCmdLineParser& parser);

	// iteration
	void	SeekBegin()		const;
	bool	Done()			const;
	bool	IsEmpty()		const;
	void	Next()			const;

	bool	SeekToArg(int argId) const;
		// seeks to the argument with the given argId. returns
		// false and Done() == true if it couldn't find it.

	// access to the argument data
	int				ArgId()				const;
		// returns the id of this arg; ASSERTs if Done() == true
	int				NumParams()			const;
		// returns the number of parameters this argument has
	const TSTRING&	ActualParam()		const;
		// returns exactly what was passed on the command line (ie -- what the user typed)
	const TSTRING&	ParamAt(int index)	const;
		// returns the parameter at the specified index. ASSERTs if
		// the index is out of range.

private:
	const   std::list<cCmdLineParser::cArgData>&				mList;
	mutable std::list<cCmdLineParser::cArgData>::const_iterator mIter;
};

//#############################################################################
// inline implementation
//#############################################################################
inline cCmdLineIter::cCmdLineIter(const cCmdLineParser& parser) :
	mList(parser.mArgData)
{
	SeekBegin();
}
inline void	cCmdLineIter::SeekBegin() const
{
	mIter = mList.begin();
}
inline bool	cCmdLineIter::Done() const
{
	return (mIter == mList.end());
}
inline bool	cCmdLineIter::IsEmpty()	const
{
	return (mList.size() == 0);
}
inline void	cCmdLineIter::Next() const
{
	mIter++;
}
inline int cCmdLineIter::ArgId() const
{
	ASSERT(! Done());
	return mIter->mId;
}
inline int cCmdLineIter::NumParams() const
{
	ASSERT(! Done());
	return mIter->mParams.size();
}

inline const TSTRING&	cCmdLineIter::ActualParam()	const
{
	ASSERT(! Done());
	return mIter->mActualParam;
}

inline const TSTRING& cCmdLineIter::ParamAt(int index)	const
{
	ASSERT((index >= 0) && (index < NumParams()));
	return mIter->mParams[index];
}


#endif

