/***************************************************************************
 *   Copyright (C) 2005 by Christof Donat   *
 *   cdonat@gmx.de   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under 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.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  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.             *
 ***************************************************************************/


#include <vector>
#include <string>
#include "logger.h"
#include <boost/lexical_cast.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/scoped_ptr.hpp>
#include <boost/algorithm/string.hpp>

/** abstractcommandlineparser holds some basic stuff for making commandline-Parsers easier. */

namespace abstractcommandlineparser {

#ifndef HSADMIN_ABSTRACTCMDLINEPARSER
#define HSADMIN_ABSTRACTCMDLINEPARSER

using std::vector;
using std::string;
using boost::shared_ptr;
using boost::scoped_ptr;
using boost::starts_with;
using boost::erase_first;

/** \brief template for Commandline-Parser
 * 
 *  Has two Parameters:
 *  - class Options: This class must provide a method 'parseThis()'. - see template CmdLineOption for an example
 *  - class parseResult: a shared pointer to an Object of this class is passed to the Options.
 */
template<class Options,class parserResult> class CmdLineParser {
	public:
	/** \brief this actually not only instatiates the parser but also parses Parameters.
	 * 
	 *  takes the Options in a vector<string> 
	 */
	CmdLineParser(vector<string> options) : m_parsed(new parserResult), m_parseSuccessful(false) {
		Options o;
		while( options.size() > 0 )
			if( !(this->m_parseSuccessful = o.parseThis(options,m_parsed)) )
				return;
	};

	//! has the parser been successful?
	inline bool operator!() { return !this->m_parseSuccessful; }

	protected:
	shared_ptr<parserResult> m_parsed;
	bool m_parseSuccessful;
};


/** \brief base template for Option classes
 *
 * usually you would use one of the many specialisations
 * has four parameters:
 * - bool hasLongName: the Parameter has a long name (e.g. --verbose)
 * - bool hasShortName: the Parameter has a short name (e.g. -v)
 * - bool hasParameter: the Parameter is followed by another Parameter that belongs to it
 * - class parseResult: takes the Parse results
 * 
 * You should implement a protected virtual method 'bool handle(string &parameter, shared_ptr<parseResult> result)'
 * if you have 'hasParameter' set to true or 'bool handle(shared_ptr<parseResult> result)' otherwise. There you 
 * can actually store your values in your parseResult.
 */
template <bool hasLongName, bool hasShortName, bool hasParameter,class parseResult> class CmdLineOption {
	public:
	//! the actual parser function.
	bool parseThis(vector<string>& options, shared_ptr<parseResult> result) {};
};

/** \brief base template for Option classes - specialisation
 *
 * this is the specialisation for Parameters that do have long and short names as well as a value.
 *
 * You should implement a protected virtual method 
 * 'bool handle(string &parameter, shared_ptr<parseResult> result)'.
 * There you can actually store your values in your parseResult.
 */
template <class parseResult> class CmdLineOption<true, true, true,parseResult> {
	public:
	//! the actual parser function.
	bool parseThis(vector<string>& options, shared_ptr<parseResult> result) {
		bool rval = false;
		if( starts_with(options[0], "-"+m_shortName) ) {
			if( rval = this->handle(options[1],result) ) {
				if( options[0] == "-"+m_shortName)
					options.erase(options.begin(),options.begin()+2);
				else {
					options.erase(options.begin()+1);
					erase_first(options[0], m_shortName);
				}
			}
		} else {
			if( starts_with(options[0], "--"+m_longName+m_parameterSeparator) ) {
				string p = options[0].substr(m_longName.size()+m_parameterSeparator.size()+2);
				if( rval = this->handle(p,result) )
					options.erase(options.begin());
			}
		}
		return rval;
	}

	protected:
	string m_longName;
	string m_shortName;
	string m_parameterSeparator;

	//! the handle function.
	virtual bool handle(string &parameter, shared_ptr<parseResult> result) { return true; };
};

/** \brief base template for Option classes - specialisation
 *
 * this is the specialisation for Parameters that do have long and short names but no value.
 *
 * You should implement a protected virtual method 
 * 'bool handle(shared_ptr<parseResult> result)'.
 * There you can actually store your values in your parseResult.
 */
template <class parseResult> class CmdLineOption<true, true, false,parseResult> {
	public:
	//! the actual parser function.
	bool parseThis(vector<string>& options, shared_ptr<parseResult> result) {
		bool rval = false;
		if( options[0] == "-"+m_shortName || options[0] == "--"+m_longName ) {
			if( rval = this->handle(result) )
				options.erase(options.begin());
		} else if( starts_with(options[0], "-"+m_shortName) && (rval = this->handle(result)) )
			erase_first(options[0], m_shortName);
		return rval;
	}

	protected:
	string m_longName;
	string m_shortName;

	//! the handle function.
	virtual bool handle(shared_ptr<parseResult> result) { return true; };
};

/** \brief base template for Option classes - specialisation
 *
 * this is the specialisation for Parameters that only have a long name and a value but no short name.
 *
 * You should implement a protected virtual method 
 * 'bool handle(string &parameter, shared_ptr<parseResult> result)'.
 * There you can actually store your values in your parseResult.
 */
template <class parseResult> class CmdLineOption<true, false, true,parseResult> {
	public:
	//! the actual parser function.
	bool parseThis(vector<string>& options, shared_ptr<parseResult> result) {
		bool rval = false;
		if( starts_with(options[0], "--"+m_longName+m_parameterSeparator) ) {
			string p = options[0].substr(m_longName.size()+m_parameterSeparator.size()+2);
			if ( rval = this->handle(p, result) )
				options.erase(options.begin());
		}
		return rval;
	}

	protected:
	string m_longName;
	string m_parameterSeparator;

	//! the handle function.
	virtual bool handle(string &parameter, shared_ptr<parseResult> result) { return true; };
};

/** \brief base template for Option classes - specialisation
 *
 * this is the specialisation for Parameters that only have a long name but no value or short name.
 *
 * You should implement a protected virtual method 
 * 'bool handle(shared_ptr<parseResult> result)'.
 * There you can actually store your values in your parseResult.
 */
template <class parseResult> class CmdLineOption<true, false, false,parseResult> {
	public:
	//! the actual parser function.
	bool parseThis(vector<string>& options, shared_ptr<parseResult> result) {
		bool rval = false;
		if( options[0] == "--"+m_longName && (rval = this->handle(result)) )
			options.erase(options.begin());
		return rval;
	}

	protected:
	string m_longName;

	//! the handle function.
	virtual bool handle(shared_ptr<parseResult> result) { return true; };
};


/** \brief base template for Option classes - specialisation
 *
 * this is the specialisation for Parameters that only have short name and a value but no long name.
 *
 * You should implement a protected virtual method 
 * 'bool handle(string &parameter, shared_ptr<parseResult> result)'.
 * There you can actually store your values in your parseResult.
 */
template <class parseResult> class CmdLineOption<false, true, true,parseResult> {
	public:
	//! the actual parser function.
	bool parseThis(vector<string>& options, shared_ptr<parseResult> result) {
		bool rval = false;
		if( starts_with(options[0], "-"+m_shortName) && (rval = this->handle(options[1],result)) ) {
			if( options[0] == "-"+m_shortName)
				options.erase(options.begin(),options.begin()+2);
			else {
				options.erase(options.begin()+1);
				erase_first(options[0], m_shortName);
			}
		}
		return rval;
	}

	protected:
	string m_shortName;

	//! the handle function.
	virtual bool handle(string &parameter, shared_ptr<parseResult> result) { return true; };
};

/** \brief base template for Option classes - specialisation
 *
 * this is the specialisation for Parameters that only have short name bot no value or long name.
 *
 * You should implement a protected virtual method 
 * 'bool handle(shared_ptr<parseResult> result)'.
 * There you can actually store your values in your parseResult.
 */
template <class parseResult> class CmdLineOption<false, true, false,parseResult> {
	public:
	//! the actual parser function.
	bool parseThis(vector<string>& options, shared_ptr<parseResult> result) {
		bool rval = false;
		if( starts_with(options[0], "-"+m_shortName) && (rval = this->handle(result)) ) {
			if( options[0] == "-"+m_shortName )
				options.erase(options.begin());
			else
				erase_first(options[0], m_shortName);
		}
		return rval;
	}

	protected:
	string m_shortName;

	//! the handle function.
	virtual bool handle(shared_ptr<parseResult> result) { return true; };
};

/** \brief base template for Option classes - specialisation
 *
 * this is the specialisation for Parameters that only have  a value but neither a long or short name.
 *
 * You should implement a protected virtual method 
 * 'bool handle(string &parameter, shared_ptr<parseResult> result)'.
 * There you can actually store your values in your parseResult.
 */
template <class parseResult> class CmdLineOption<false, false, true,parseResult> {
	public:
	//! the actual parser function.
	bool parseThis(vector<string>& options, shared_ptr<parseResult> result) {
		bool rval = false;
		if( !starts_with(options[0], "-") && (rval = this->handle(options[0],result)) )
			options.erase(options.begin());
		return rval;
	}

	protected:
	//! the handle function.
	virtual bool handle(string &parameter, shared_ptr<parseResult> result) { return true; };
};


/** \brief base template for Option classes - specialisation
 *
 * this is the specialisation for Parameters that neither have a value, a long or short name.
 *
 * You should implement a protected virtual method 
 * 'bool handle(shared_ptr<parseResult> result)'.
 * There you can actually store your values in your parseResult.
 */
template <class parseResult> class CmdLineOption<false, false, false,parseResult> {
	public:
	//! the actual parser function.
	bool parseThis(vector<string>& options, shared_ptr<parseResult> result) {
		bool rval = false;
		if( !starts_with(options[0], "-") && (rval = this->handle(result)) )
			options.erase(options.begin());
		return rval;
	}

	protected:
	//! the handle function.
	virtual bool handle(shared_ptr<parseResult> result) { return true; };
};

//! just needed for typelists
class nullType {};

//! typelist for multiple Options parser
template<class CAR, class CDR> class typelist {
	public:
	typedef CAR car;
	typedef CDR cdr;
};

/** \brief A Commandline-Option for CmdLineParser that works with a typelist of Options.
 *
 * this is meant to combine multiple Parameters in a typelist to build a complex Command Line parser
 */
template<class tl, class parseResult> class CmdLineOptionList : public CmdLineOption<false,false,false,parseResult> {
	public:
	//! the actual parser function.
	bool parseThis(vector<string>& options, shared_ptr<parseResult> result) {
		return m_first.parseThis(options,result) || m_second.parseThis(options,result);
	}

	private:
	typename tl::car                                 m_first;
	CmdLineOptionList<typename tl::cdr,parseResult>  m_second;
};

/** \brief specialisation for empty type list
 */
template<class parseResult> class CmdLineOptionList<nullType,parseResult> : public CmdLineOption<false,false,false,parseResult> {
	public:
	//! the actual parser function; always returns false.
	bool parseThis(vector<string>& options, shared_ptr<parseResult> result) {
		return false;
	}
};

#define Parameterlist1(t1) \
	abstractcommandlineparser::typelist<t1,abstractcommandlineparser::nullType>
#define Parameterlist2(t1,t2) \
	abstractcommandlineparser::typelist<t1,Parameterlist1(t2) >
#define Parameterlist3(t1,t2,t3) \
	abstractcommandlineparser::typelist<t1,Parameterlist2(t2,t3) >
#define Parameterlist4(t1,t2,t3,t4) \
	abstractcommandlineparser::typelist<t1,Parameterlist3(t2,t3,t4) >
#define Parameterlist5(t1,t2,t3,t4,t5) \
	abstractcommandlineparser::typelist<t1,Parameterlist4(t2,t3,t4,t5) >
#define Parameterlist6(t1,t2,t3,t4,t5,t6) \
	abstractcommandlineparser::typelist<t1,Parameterlist5(t2,t3,t4,t5,t6) >
#define Parameterlist7(t1,t2,t3,t4,t5,t6,t7) \
	abstractcommandlineparser::typelist<t1,Parameterlist6(t2,t3,t4,t5,t6,t7) >
#define Parameterlist8(t1,t2,t3,t4,t5,t6,t7,t8) \
	abstractcommandlineparser::typelist<t1,Parameterlist7(t2,t3,t4,t5,t6,t7,t8) >
#define Parameterlist9(t1,t2,t3,t4,t5,t6,t7,t8,t9) \
	abstractcommandlineparser::typelist<t1,Parameterlist8(t2,t3,t4,t5,t6,t7,t8,t9) >
#define Parameterlist10(t1,t2,t3,t4,t5,t6,t7,t8,t9,t10) \
	abstractcommandlineparser::typelist<t1,Parameterlist9(t2,t3,t4,t5,t6,t7,t8,t9,t10) >
#define Parameterlist11(t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11) \
	abstractcommandlineparser::typelist<t1,Parameterlist10(t2,t3,t4,t5,t6,t7,t8,t9,t10,t11) >
#define Parameterlist12(t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12) \
	abstractcommandlineparser::typelist<t1,Parameterlist11(t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12) >
#define Parameterlist13(t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13) \
	abstractcommandlineparser::typelist<t1,Parameterlist12(t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13) >
#define Parameterlist14(t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13,t14) \
	abstractcommandlineparser::typelist<t1,Parameterlist13(t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13,t14) >
#define Parameterlist15(t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13,t14,t15) \
	abstractcommandlineparser::typelist<t1,Parameterlist14(t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13,t14,t15) >
#define Parameterlist16(t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13,t14,t15,t16) \
	abstractcommandlineparser::typelist<t1,Parameterlist15(t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13,t14,t15,t16) >
#define Parameterlist17(t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13,t14,t15,t16,t17) \
	abstractcommandlineparser::typelist<t1,Parameterlist16(t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13,t14,t15,t16,t17) >
#define Parameterlist18(t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13,t14,t15,t16,t17,t18) \
	abstractcommandlineparser::typelist<t1,Parameterlist17(t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13,t14,t15,t16,t17,t18) >
#define Parameterlist19(t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13,t14,t15,t16,t17,t18,t19) \
	abstractcommandlineparser::typelist<t1,Parameterlist18(t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13,t14,t15,t16,t17,t18,t19) >
#define Parameterlist20(t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13,t14,t15,t16,t17,t18,t19,t20) \
	abstractcommandlineparser::typelist<t1,Parameterlist19(t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13,t14,t15,t16,t17,t18,t19,t20) >
#define Parameterlist21(t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13,t14,t15,t16,t17,t18,t19,t20,t21) \
	abstractcommandlineparser::typelist<t1,Parameterlist20(t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13,t14,t15,t16,t17,t18,t19,t20,t21) >
#define Parameterlist22(t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13,t14,t15,t16,t17,t18,t19,t20,t21,t22) \
	abstractcommandlineparser::typelist<t1,Parameterlist21(t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13,t14,t15,t16,t17,t18,t19,t20,t21,t22) >
#define Parameterlist23(t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13,t14,t15,t16,t17,t18,t19,t20,t21,t22,t23) \
	abstractcommandlineparser::typelist<t1,Parameterlist22(t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13,t14,t15,t16,t17,t18,t19,t20,t21,t22,t23) >
#define Parameterlist24(t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13,t14,t15,t16,t17,t18,t19,t20,t21,t22,t23,t24) \
	abstractcommandlineparser::typelist<t1,Parameterlist23(t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13,t14,t15,t16,t17,t18,t19,t20,t21,t22,t23,t24) >

#else

template<class Options,class parseResult> class CmdLineParser;
template <bool hasLongName, bool hasShortName, bool hasParameter,class parseResult> class CmdLineOption;
class nullType;
template<class CAR, class CDR> class typelist;
template<class tl, class parseResult> class CmdLineOptionList;

#endif /* HSADMIN_ABSTRACTCMDLINEPARSER */
};
