// Copyright (C) 2002, stephan@wanderinghorse.net
// Released under the GNU Lesser General Public License

#include <iostream>
#include <stdlib.h>

#include "EStringTokenizer.h"
#include "ESimpleCLParser.h"


ESimpleCLParser::ESimpleCLParser ():EPropertyList ()
{
}
ESimpleCLParser::ESimpleCLParser (int argc, char *argv[], int startAt):
EPropertyList ()
{
	setArgs (argc, argv, startAt);
}

ESimpleCLParser::~ESimpleCLParser ()
{
}

bool
ESimpleCLParser::isHelpSet()
{
        return this->isSet( "help" ) || this->isSet( "-?" ) ;
}


void
ESimpleCLParser::setHelp (const std::string & key, const std::string & text)
{
	this->helpmap.setString (key, text);
}

const std::string
ESimpleCLParser::getHelp (const std::string & key) const
{
     std::string help = helpmap.getString (key);
     return help;
}


std::string
ESimpleCLParser::getString (const std::string & key, const std::string & defaultVal) const
{
	     // DO NOT call LIBE_{DEBUG,VERBOSE} from here!
	     std::string check = EPropertyList::getString (key, defaultVal);

	     if (check != defaultVal)
		     return check;

	     if (key.find ("-") != 0)	// non-dashed argument
	     {
		     // now try -key, --key
		     check = key;
		     std::string foo;
		     for (int i = 0; i < 2; i++)
		     {
			     check.insert (check.begin (), '-');
			     foo = EPropertyList::getString (check,
							     defaultVal);
			     //CERR << "dash test: " << check << " = " << foo << endl;
			     if (foo != defaultVal)
				     return foo;
		     }
	     }
	     return defaultVal;
     }



int
ESimpleCLParser::setArgs (int argc, char *argv[], int startAt,
			  const char *argpre)
{
	using namespace std;
	if (startAt >= argc)
		return -1;
	if (!argv[startAt])
		return -1;
	std::string argprefix = argpre ? argpre : "-";
	int acount = 0;
	std::string v;
	std::string a;
	EKeyValueParser kvp;
	std::string nextarg;
	string numbers = "0123456789";
	bool skipnext = false;
	for (int i = startAt; i < argc; i++)
	{
		if (skipnext)
		{
			skipnext = false;
			continue;
		}
		a = std::string ((const char *) argv[i]);
		//CERR << "arg="<<a<<endl;
		if (a.find (argprefix) != 0)
		{
			continue;
		}
		//      if( a.find_first_of( numbers ) == 1 )
		//      { // negative number?
		//          CERR << "setArgs() skipping possible negative number arg: "<<a<<endl;
		//          continue;
		//         }

		v = std::string ();
		++acount;
		if (kvp.parse (a))	// check for: --foo=bar
		{
			a = kvp.key ();
			v = kvp.value ();
		}
		else		// else it's space-separated or a boolean flag
		{
			if (i < argc - 1)
			{
				nextarg = std::string (argv[i + 1]);
				if (nextarg.find (argprefix) == 0)
				{
					if (nextarg.find_first_of (numbers) ==
					    1)
					{	// let's assume it's a negative number, not a flag
						// todo: actual atol() or atod() check
						skipnext = true;
						v = nextarg;
					}
					else
					{	// nextarg is argprefix'd, so treat this as a boolean flag
						v = "true";
					}
				}
				else
				{	// it was space-separated: --foo bar
					v = nextarg;
					skipnext = true;
				}
			}
			else
			{
				v = "true";	// the final item is an argument, toggling it to true.
			}
		}
		this->set (a, v);
		//CERR << "["<<a<<"] = ["<<v<<"]"<<endl;
	}
	return acount;
}

int
ESimpleCLParser::setArgs (std::string args, std::string separators)
{
	//    if( args.empty() || args.find( "#" ) == 0 ) return 0; // arguable
	if (args.empty ())
		return 0;
	EStringTokenizer toker;
	const static int maxargs = 256;
	char *cargs[maxargs];	// max number of arguments. This size is completely arbitrary.

	int count = 0;
	toker.tokenize (args.c_str (), separators.c_str ());
	int argbufsize = 1024;
	while (toker.hasMoreTokens () && count < maxargs)
	{
		cargs[count] = (char *) malloc (argbufsize);	// if this isn't enough... there's a problem.
		strncpy (cargs[count], toker.nextToken (), argbufsize);
		//        CERR << "parse(): token= [" << cargs[count] << "]" << endl;
		++count;
	}
	int ret = count ? this->setArgs (count, cargs, 0, 0) : 0;
	for (int i = 0; i < count; i++)
		free (cargs[i]);
	return ret;
}


const std::string
ESimpleCLParser::dumpHelp (bool scv /* show current values? */ ) const
{
        EPropertyList::const_iterator iter = this->helpmap.begin ();
        std::string outs;
        std::string key, val, realkey;
        //     outs += "==========Format:\noption_name:";
        //     if( scv ) outs += " [current value]";
        //     outs += "\n\thelp text for option.\n==========\n";
        while (iter != this->helpmap.end ())
        {
                key = (*iter).first;
                outs += key;
                outs += ':';
                if (scv)
                {
                        val = this->getString (key);	// handles dashes
                        if (!val.empty ())
                        {
                                outs += " [";
                                outs += val;
                                outs += "]";
                        }
                }
                outs += "\n\t";
                outs += this->getHelp (key);
                outs += '\n';
                ++iter;
        }
        outs += "\nAll arguments must start with either - or --, like -help or --help, when passed in from the command line.";
        return outs;
}
