/**
 * @file GetOpt.hpp
 * @author Schlund + Partner AG
 * @brief Abstraction of GNU C "getopt_long(3)".
 * @deprecated Leagcy - don't use this for new code -- use boost::program_options instead.
 *
 * Synopsis:
 *
 * @code
 * #include <ui-utilcpp/GetOpt.hpp>
 * @endcode
 *
 *	Notes:
 *	- A "command line option" has the form "--<nameLong>=<arg>" or "-<nameShort> <arg>".
 *	- Arg can be optional.
 *	- Parser used is GNU getoptAlong.
 *
 *	Limitations / todos:
 *	 - Currently, all c.l.options MUST have a short _and_ a long option.
 *
 *	Usage (informal):
 *	- GetOpt getOpt(argc, argv)
 *	- { getOpt.set(...) }
 *
 *  - CLOption * opt0(getOpt.get("version" | 'v'))
 *
 *	- getOpt.isValid(), opt0->isGiven(), ...
 */
#ifndef UI_UTIL_GETOPT_HPP
#define UI_UTIL_GETOPT_HPP

/**
 * @example GetOpt.cpp
 * Simple test program for GetOpt.
 * Binary should be installed as ui-utilcpp-getopt.
 */

// STDC++
#include <string>
#include <vector>

// SYSTEM C
#include <getopt.h>  // GNU

namespace UI {
namespace Util {

/** @brief Most general class for a command line option. Use this class for a option without argument.
 * @deprecated Leagcy - don't use this for new code -- use boost::program_options instead.
 */
class CLOption
{
public:
	/** @brief Standard constructor. Normally, you would not use this directly, but rather use GetOpt::set.
	 *
	 * @param nameLong Long name of the option; e.g. "version".
	 * @param nameShort Short name (one character) of the option; e.g. 'v'.
	 * @param doc Documentation string for this option; e.g. "Show version information".
	 * @param defaultArg Default value for that option.
	 */
	CLOption(std::string const & nameLong, char nameShort, std::string const & doc = "No documentation for this option.", std::string const & defaultArg="");

	/** */
	virtual ~CLOption();

	/** @brief Get the long name.
	 *
	 * @return Long name of this option.
	 */
	std::string getNameLong() const;

	/** @brief Get the short name.
	 *
	 * @return Short name of this option.
	 */
	char getNameShort() const;

	/** @brief Get the documentation.
	 *
	 * @return Documentation string of this option.
	 */
	std::string getDoc() const;

	/** @brief Check if this option was given.
	 *
	 * @result true, if argument was given, else false. This must be set manually via setGiven() (GetOpt automates this).
	 */
	bool isGiven() const;

	/** @brief Get argument given with this option.
	 *
	 * @result Argument, if any; zero-length string for none. This must be set manually via setGiven() (GetOpt automates this).
	 */
	std::string getArg() const;

	/** @brief Print usage information for this option. */
	virtual void printUsage() const;

	/** @brief Get GNU getopt_long(3) represention of this option.
	 *
	 * @return Corresponding option struct for GNU getopt_long(3).
	 */
	virtual option * getCOptLong(option * opt) const;

	/** @brief Get GNU getopt(3) represention of this option.
	 *
	 * @return Corresponding "optstr" for getopt(3).
	 */
	virtual std::string getCOptShort() const;

	/** @brief Mark this option as "given", and optionally set the argument.
	 *
	 * @param arg Argument string to set this option to.
	 */
	virtual void setGiven(std::string const & arg="");

protected:

private:
	// Attributes set when initialized
	std::string const nameLong_; /** Long option */
	char const nameShort_;  /** Short option */
	std::string const doc_;      /** Option documentation */

	// Attributes set when parsed
	bool isGiven_;    /** Option given */
	std::string arg_;      /** Option argument */

protected:
	std::string const defaultArg_;      /** Default option argument */
};


/** @brief Class representing a command line option with mandatory argument. */
class CLOptionArg: public CLOption
{
public:
	/** @brief Standard constructor.
	 *
	 * @see CLOption::CLOption.
	 */
	CLOptionArg(std::string const & nameLong, char nameShort, std::string const & doc = "No documentation for this option.", std::string const & defaultArg="");
	virtual void printUsage() const;
	virtual option * getCOptLong(option * opt) const;
	virtual std::string getCOptShort() const;
};

/** @brief Class representing a command line option with optional argument. */
class CLOptionOptArg : public CLOption
{
public:
	/** @brief Standard constructor.
	 *
	 * @see CLOption::CLOption.
	 */
	CLOptionOptArg(std::string const & nameLong, char nameShort, std::string const & doc = "No documentation for this option.", std::string const & defaultArg="");

	virtual void printUsage() const;
	virtual option * getCOptLong(option * opt) const;
	virtual std::string getCOptShort() const;
};


/** @brief Abstraction class for GNU getopt_long(3).
 *
 * Use this rather than the CLOption*-Classes directly.
 *
 * @deprecated Leagcy - don't use this for new code -- use boost::program_options instead.
 */
class GetOpt
{
public:
	/** @brief All three possible types of CLOption* Classes.
	 *
	 * In normal use, you would use this in set(), who then choses what type of object
	 * to create. Use "NOARG" for CLOption, "ARG" for CLOptionArg and "OPTARG" for CLOptionOptArg.
	 * @see set()
	 */
	enum Type {NoArg_, Arg_, OptArg_};

	/** @brief Standard constructor.
	 *
	 * @param argc Number of arguments (first argument of main() function).
	 * @param argv Array of arguments  (second argument of main() function).
	 */
	GetOpt(int argc, char * const * const argv);

	/** @brief Standard destructor. */
	~GetOpt();

	/** @brief Introduce a new option.
	 *
	 * @param nameLong Long name of the option; e.g. "version".
	 * @param nameShort Short name (one character) of the option; e.g. 'v'.
	 * @param type Type of the option (NOARG, ARG, OPTARG); e.g. "NOARG".
	 * @param doc Documentaion for this option; e.g. "Show version information".
	 * @param defaultArg Default value for that option.
	 */
	GetOpt & set(std::string const & nameLong, char nameShort, Type type,
	             std::string const & doc = "No documentation for this option.", std::string const & defaultArg="");

	/** @brief Get an option object from short name.
	 *
	 * The pointer is valid as long as the GetOpt object stays alive.
	 * This will implicitely parse the command line options, if not already done.
	 *
	 * @param nameShort Short name (one character) of the option; e.g. 'v'.
	 */
	CLOption * get(char nameShort);

	/** @brief  Get an option object from long name.
	 *
	 * @param nameLong Long name of the option; e.g. "version".
	 */
	CLOption * get(std::string const & nameLong);

	/** @brief Prints out Usage information to cout (stdout).
	 *
	 * @param title Print this text before usage information.
	 */
	void printUsage(std::string const & title="\nUsage:\n\n") const;

	/** @brief Helper.
	 *
	 * Prints out an error text to cerr, prints usage to cout
	 * and always returns 1 (suitable as program exit code).
	 * @param reason Explanatory error text.
	 * @param title Print this text before usage information.
	 */
	int wrongUsage(std::string const & reason, std::string const & title="\nUsage:\n\n") const;

	/** @brief Check if given command line arguments are "well-formed".
	 *
	 * @note GNU getopt_long(3) will print error messages to stderr, if
	 * the given arguments are not valid.
	 *
	 * @return true if valid, else false.
	 */
	bool isValid();

private:
	int const argc_;
	char * const * const argv_;

	std::vector<CLOption *> opts_;

	void parse();

	bool isValid_;
	bool isParsed_;
};

}}
#endif
