//
// CRAM: CREATE Real-time Applications Manager
// See the copyright and acknowledgment of authors in the file COPYRIGHT
//
//// Options.cpp: the CRAM class for parsing input arguments, based on the MICOGetOpt class ////
//

#include "Options.h"
#include <fstream>
#include <string>
#include <iostream>

using namespace cram; 

Options::Options (const OptMap &opts) : mInOpts (opts) {
}

Options::~Options () { }

bool Options::parse (int &argc, char *argv[], bool ignore) {
	vector<int> erase;
	vector<string> args;

	int i;
	for (i = 1; i < argc; ++i)
		args.push_back (argv[i]);
	if (!parse (args, erase, ignore))
		return false;
	int nargc = 0;
	for (i = 0; i < argc; ++i) {
		if (erase.size() > 0 && i == erase[0]+1) {
			erase.erase (erase.begin());
		} else {
			argv[nargc++] = argv[i];
		}
	}
	if (nargc < argc)
		argv[nargc] = 0;
	argc = nargc;
	return true;
}

bool Options::parse (const vector<string> &argv, vector<int> &erase,bool ignore) {
	for (unsigned int i = 0; i < argv.size(); ++i) {
		string arg = argv[i];
		if (arg == "--") {
			erase.push_back (i);
			break;
		}
		if (!ignore && (arg.size() == 0 || arg[0] != '-')) {
			break;
		}
		OptMap::const_iterator it = mInOpts.find (arg);
		if (it == mInOpts.end()) {
			int pos = 0;
			if (arg.length() > 2) {
				// -Oval ??
				arg = arg.substr (0, 2);
				pos = 2;
				it = mInOpts.find (arg);
			}
			if (it == mInOpts.end() && (pos = argv[i].find ("=")) > 0) {
				// --long-opt=something ??
				arg = argv[i].substr (0, pos);
				++pos;
				it = mInOpts.find (arg);
			}
			if (it != mInOpts.end()) {
				if ((*it).second.length() == 0) {
					cerr << "unexpected argument for option " << arg << endl;
					return false;
				}
				mOutOpts.push_back (make_pair (arg, argv[i].substr (pos)));
				erase.push_back (i);
			} else if (!ignore) {
				cerr << "unknown option: " << argv[i] << endl;
				return false;
			}
		} else {
			erase.push_back (i);
			if ((*it).second.length() > 0) {
				// -O val
				// --long-opt val
				if (++i == argv.size()) {
					cerr << "missing argument for option " << arg << endl;
					return false;
				}
				mOutOpts.push_back (make_pair (arg, argv[i]));
				erase.push_back (i);
			} else {
				// -O
				// --long-opt
				mOutOpts.push_back (make_pair (arg, string("")));
			}
		}
	}

	for (OptVec::const_iterator it = mOutOpts.begin();it!=mOutOpts.end();++it){
		OptMap::iterator mp = mInOpts.find((*it).first);
		if (mp == mInOpts.end()) { //either last or nonexistant
			if((*mp).first==(*it).first){ // Was the last item
				mInOpts.erase(mp);
			}
		} else {					// existed, so we delete it
			mInOpts.erase(mp);
		}
	}

/*	for(OptMap::const_iterator mp = _in_opts.begin();mp!=_in_opts.end();++mp){ // Add all remaining options
		_out_opts.push_back (make_pair ((*mp).first, (*mp).second));
	}
*/
	
	return true;
}

bool Options::parse (char *argv, bool ignore) {
	vector<int> erase;
	vector<string> args;

	int i,j,len=strlen(argv);
	char *walker=argv,*source=argv;
	
	
	for(i=0,j=0;i<len;++i){
		if(source[i]==' '){
			source[i]='\0';
			args.push_back(walker);
			++j;
			walker=(char*)(source+i+1);
		}
	}

	args.push_back(walker); 
	
	return parse (args, erase, ignore);
	
}

bool Options::parse (const vector<string> &argv, bool ignore) {
	vector<int> erase;
	return parse (argv, erase, ignore);
}

bool Options::parse (const string &_filename, bool ignore) {
	string fn = _filename;
	if (fn[0] == '~') {
		char *s = getenv ("HOME");
		if (!s) {
			return true;
		}
		string home = s;
		fn.replace (0, 1, home);
	}
	ifstream in (fn.c_str());
	if (!in)
		return true;

	char line[10000], *lptr, *tok;
	vector<string> argv;

	while (true) {
		if (in.getline(line, sizeof(line)).eof())
			break;
		if (!line[0])
			continue;
		lptr = line;
		while ((tok = ::strtok (lptr, " \t"))) {
			if (lptr && *tok == '#')
				break;
			argv.push_back (tok);
			lptr = 0;
		}
	}
	return parse (argv, ignore);
}

Options::OptVec::iterator Options::find (string option, string &result) {
	Options::OptVec &o = mOutOpts;
	Options::OptVec::iterator i;
	for ( i = o.begin(); i != o.end(); ++i) {

		if((*i).first==option){
			result=(*i).second;
			return i;
		}
	}
	return i;
}	

const Options::OptVec & Options::opts () const {
	return mOutOpts;
}
