// Copyright (C) 2001-2008 Gianni Mariani
//
// 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.
//
// As a special exception, you may use this file as part of a free software
// library without restriction.  Specifically, if other files instantiate
// templates or use macros or inline functions from this file, or you compile
// this file and link it with other files to produce an executable, this
// file does not by itself cause the resulting executable to be covered by
// the GNU General Public License.  This exception does not however
// invalidate any other reasons why the executable file might be covered by
// the GNU General Public License.
//
// This exception applies only to the code released under the name GNU
// Common C++.  If you copy code from other releases into a copy of GNU
// Common C++, as the General Public License permits, the exception does
// not apply to the code that you add in this way.  To avoid misleading
// anyone as to the status of such modified files, you must delete
// this exception notice from them.
//
// If you write modifications of your own for GNU Common C++, it is your choice
// whether to permit this exception to apply to your modifications.
// If you do not wish that, delete this exception notice.
//

/**
 * @file cmdoptns.h
 * @short Command line option parsing interface.
 **/

#ifndef	CCXX_CMDOPTNS_H_
#define	CCXX_CMDOPTNS_H_

#ifndef CCXX_STRING_H_
#include <cc++/string.h>
#endif

#ifdef	CCXX_NAMESPACES
namespace ost {
#endif

class CommandOption;
class CommandOptionParse;

/**
 * This defines a linked list head pointer for all the command line
 * options that use the default list.  It will most likely
 * be used in most cases without being explicitly referenced in application
 * code.  It is a default value of various method's parameters.
 *
 */
extern __EXPORT CommandOption * defaultCommandOptionList;

/**
 * CommandOption is the base class for all command line options.  Command
 * line options can be defined statically and used when constructing
 * a command line parser onject using makeCommandOptionParse.  This serves
 * only as a base class to CommandOptionWithArg, CommandOptionRest or
 * CommandOptionNoArg which can also be used to derive more complex
 * classes or even entire applications.
 *
 * @author Gianni Mariani <gianni@mariani.ws>
 */
class __EXPORT CommandOption {
public:

	/**
	 * Long option name, these will be preceded with "--" on the command line.
	 * e.g. --file foo.x
	 */
	const char	* optionName;

	/**
	 * option letter, these will be preceded with "-" on the command line.
	 * e.g. -f foo.x
	 */
	const char	* optionLetter;

	/**
	 * A short description of the option for Usage messages.
	 * e.g. Usage: mycommand : blah
	 *			-f, --file \<DESCRIPTION here\>
	 */
	const char	* description;

	/**
	 * OptionType is for denoting what type of option this is, with an
	 * arg, without an arg or the trailing args.
	 * @short Option type
	 */
	enum OptionType {
		/**
		 * This option is associated with a value
		 */
		hasArg,
		/**
		 * This option is a flag only
		 */
		noArg,
		/**
		 * Remaining of the command line arguments
		 */
		trailing,
		/**
		 * Collect values that are not a value to an option
		 */
		collect
	};

	/**
	 * This command option's OptionType.
	 */
	OptionType	  optionType;	// HasArg, NoArg or Trailing

	/**
	 * True if this parameter is required.  If the parameter is not supplied
	 * and required is true, an error will be flagged in the option processor.
	 */
	bool		  required;		// Option is required - fail without it

	/**
	 * This next CommandOption in this list of options or nil if no more
	 * options exist.
	 */
	CommandOption	* next;

	/**
	 * A virtual destructor just in case.
	 */
	virtual ~CommandOption();

	/**
	 * CommandOption contructor.  Note the default values for required and
	 * ppNext.
	 *
	 * @param inOptionName	long option name
	 * @param inOptionLetter	short letter name
	 * @param inDescription	short description of the option
	 * @param inOptionType	the type of this option
	 * @param inRequired		true if option is required
	 * @param ppNext			the linked list header
	 */
	CommandOption(
		const char		* inOptionName,
		const char		* inOptionLetter,
		const char		* inDescription,
		OptionType		  inOptionType,
		bool			  inRequired = false,
		CommandOption  ** ppNext = & defaultCommandOptionList
	);

	/**
	 * foundOption is called by the CommandOptionParse object during the parsing
	 * of the command line options.
	 *
	 * @param cop		pointer to the command option parser
	 * @param value		the value of this option
	 */
	virtual	void foundOption( CommandOptionParse * cop, const char * value = 0 );

	/**
	 * foundOption is called by the CommandOptionParse object during the parsing
	 * of the command line options.
	 *
	 * @param cop		pointer to the command option parser
	 * @param value		an array of values of this option
	 * @param num		number of values in the array
	 */
	virtual	void foundOption( CommandOptionParse * cop, const char ** value, int num );

	/**
	 * Once parsing of command line options is complete, this method is called.
	 * This can be used to perform last minute checks on the options collected.
	 *
	 * @param cop		pointer to the command option parser
	 */
	virtual	void parseDone( CommandOptionParse * cop );

	/**
	 * Once CommandOption objects have completed parsing and there are no
	 * errors they may have some specific tasks to perform.  PerformTask
	 * must return.
	 *
	 * @param cop		pointer to the command option parser
	 */
	virtual	void performTask( CommandOptionParse * cop );

	/**
	 * For fields with the required flag set, this method is used to determine
	 * if the Option has satisfied it's required status.  The default methods
	 * simply returns true if any values have been found.  This could be specialized
	 * to return true based on some other criteria.
	 */
	virtual bool hasValue();

};

/**
 * Derived class of CommandOption for options that have a value associated with them.
 * Classes CommandOptionRest and CommandOptionArg derive from this class.
 */
class __EXPORT CommandOptionWithArg : public CommandOption {
public:

	/**
	 * Array of list of values collected for this option.
	 */
	const char ** values;

	/**
	 * Number of values in the values array.
	 */
	int			  numValue;

	/**
	 * CommandOptionWithArg contructor.  Note the default values for required and
	 * ppNext.
	 *
	 * @param inOptionName	long option name
	 * @param inOptionLetter	short letter name
	 * @param inDescription	short description of the option
	 * @param inOptionType	the type of this option
	 * @param inRequired		true if option is required
	 * @param ppNext			the linked list header
	 */
	CommandOptionWithArg(
		const char		* inOptionName,
		const char		* inOptionLetter,
		const char		* inDescription,
		OptionType		  inOptionType,
		bool			  inRequired = false,
		CommandOption  ** ppNext = & defaultCommandOptionList
	);

	virtual ~CommandOptionWithArg();

	virtual	void foundOption( CommandOptionParse * cop, const char * value = 0 );
	virtual	void foundOption( CommandOptionParse * cop, const char ** value, int num );
	virtual bool hasValue();
};

/**
 * Class for options with an argument e.g. --option value .
 */
class __EXPORT CommandOptionArg : public CommandOptionWithArg {
public:

	/**
	 * CommandOptionArg contructor.  This sets the optionType for this
	 * object to HasArg.
	 *
	 * @param inOptionName	long option name
	 * @param inOptionLetter	short letter name
	 * @param inDescription	short description of the option
	 * @param inRequired		true if option is required
	 * @param ppNext			the linked list header
	 */
	CommandOptionArg(
		const char		* inOptionName,
		const char		* inOptionLetter,
		const char		* inDescription,
		bool			  inRequired = false,
		CommandOption  ** ppNext = & defaultCommandOptionList
	);

	virtual ~CommandOptionArg();


};

/**
 * It only makes sense to have a single one of these set and it is
 * exclusive with CommandOptionCollect. It is the option that takes the rest
 * of the command line options that are not part of any other options.
 * e.g. "strace -ofile command arg1 arg2". The "command arg1 arg2" part is
 * placed in objects of this class.
 *
 * @short CommandOption to take the rest of the command line
 */
class __EXPORT CommandOptionRest : public CommandOptionWithArg {
public:

	/**
	 * CommandOptionRest contructor.  This sets the optionType for this
	 * object to Trailing.
	 *
	 * @param inOptionName	long option name
	 * @param inOptionLetter	short letter name
	 * @param inDescription	short description of the option
	 * @param inRequired		true if option is required
	 * @param ppNext			the linked list header
	 */
	CommandOptionRest(
		const char		* inOptionName,
		const char		* inOptionLetter,
		const char		* inDescription,
		bool			  inRequired = false,
		CommandOption  ** ppNext = & defaultCommandOptionList
	);

};

/**
 * It only makes sense to have a single one of these set and it is also
 * exclusive with CommandOptionRest.  This makes parameter collecting
 * behave line the Unix "cat" command.
 *
 * @short CommandOption to collect parameters that are not options.
 */
class __EXPORT CommandOptionCollect : public CommandOptionWithArg {
public:

	/**
	 * CommandOptionRest contructor.  This sets the optionType for this
	 * object to Collect.
	 *
	 * @param inOptionName	long option name
	 * @param inOptionLetter	short letter name
	 * @param inDescription	short description of the option
	 * @param inRequired		true if option is required
	 * @param ppNext			the linked list header
	 */
	CommandOptionCollect(
		const char		* inOptionName,
		const char		* inOptionLetter,
		const char		* inDescription,
		bool			  inRequired = false,
		CommandOption  ** ppNext = & defaultCommandOptionList
	);

};

/**
 * CommandOption type for flags.
 */
class __EXPORT CommandOptionNoArg : public CommandOption {
public:

	/**
	 * The number of times this value has been set.
	 */
	int			  numSet;	// The number of times this argument is set

	/**
	 * CommandOptionArg contructor.  This sets the optionType for this
	 * object to NoArg.
	 *
	 * @param inOptionName	long option name
	 * @param inOptionLetter	short letter name
	 * @param inDescription	short description of the option
	 * @param inRequired		true if option is required
	 * @param ppNext			the linked list header
	 */
	CommandOptionNoArg(
		const char		* inOptionName,
		const char		* inOptionLetter,
		const char		* inDescription,
		bool			  inRequired = false,
		CommandOption  ** ppNext = & defaultCommandOptionList
	);

	/**
	 * CommandOptionNoArg::foundOption will evpect a nil "value" passed in.
	 */
	virtual	void foundOption( CommandOptionParse * cop, const char * value = 0 );

};

/**
 * This is the CommandOptionParse interface class.  To implement this object you can
 * call makeCommandOptionParse();  This will instantiate a dynamically allocated
 * version of this class and parse the command line for the list of command options
 * that are passed in.
 *
 * @author Gianni Mariani <gianni@mariani.ws>
 */

class __EXPORT CommandOptionParse {
public:

	/**
	 * Virtual destructor needed so that the object may be correctly deleted.
	 */
	virtual ~CommandOptionParse() = 0;

	/**
	 * Get the value of the error flag set if the parser encountered errors.
	 */
	virtual bool argsHaveError() = 0;

	/**
	 * Return a string of text describing the list of errors encountered.
	 */
	virtual const char * printErrors() = 0;

	/**
	 * Return a string that contains the usage description of this list of paramaters.
	 */
	virtual const char * printUsage() = 0;

	/**
	 * Register an error with this parser.  This string will be appended to the
	 * errors already buffered in this object.
	 */
	virtual void registerError( const char * errMsg ) = 0;

	/**
	 * The method should be invoked by the main code once it has determined that
	 * the application should be started.
	 */
	virtual void performTask() = 0;

};

/**
 * makeCommandOptionParse will create an implementation of a CommandOptionParse
 * object.  This particular implementation is a wrapper around getopt_long(3).
 * That interface unfortunatly does not provide enough information to give
 * the best error messages with malformed input.  If the implementation changes
 * there is a good chance that the binary interface will remain the same.
 *
 */
__EXPORT CommandOptionParse * makeCommandOptionParse(
	int				   argc,
	char			** argv,
	const char * comment,
	CommandOption	 * options = defaultCommandOptionList
);

#ifdef	CCXX_NAMESPACES
}
#endif

#endif

/** EMACS **
 * Local variables:
 * mode: c++
 * c-basic-offset: 8
 * End:
 */

