
#include "commandline.h"

CommandLine::CommandLine() {

	cerr << "ERROR: CommandLine constructed without argc and argv" << endl;
	exit(-1);
}

CommandLine::CommandLine(int argc, char **argv) {

	_argc = argc;
	_argv = argv;
	_numoptions = 0;
	_index = 0;
	_tokenless_count = 0;
	_finish_add_option_flag = 0;
}

CommandLine::CommandLine(const CommandLine&) {

	cerr << "ERROR: CommandLine copy constructor called!" << endl;
	exit(-1);
}

CommandLine::~CommandLine() {

	for (int i=0; i<_numoptions; i++)
		delete _options[i];
}

CommandLine& CommandLine::AddOption() {

	if (_numoptions >= MAX_OPTIONS-1)
		cerr << "ERROR: Cannot add another command line option" << endl;
	else {
		_options[_numoptions] = new CLOption;
		_options[_numoptions]->toggle = NULL;
		_options[_numoptions]->var_int = NULL;
		_options[_numoptions]->var_float = NULL;
		_options[_numoptions]->var_char = NULL;
		_options[_numoptions]->var_string = NULL;
		_numoptions++;
	}

	return *this;
}

CommandLine& CommandLine::AddOption(const JLString &token, int *i) {

	AddOption();
	_options[_numoptions-1]->token = token;
	_options[_numoptions-1]->var_int = i;

	return *this;
}

CommandLine& CommandLine::AddOption(const JLString &token, float *f) {

	AddOption();
	_options[_numoptions-1]->token = token;
	_options[_numoptions-1]->var_float = f;

	return *this;
}

CommandLine& CommandLine::AddOption(const JLString &token, char **c) {

	AddOption();
	_options[_numoptions-1]->token = token;
	_options[_numoptions-1]->var_char = c;

	return *this;
}

CommandLine& CommandLine::AddOption(const JLString &token, JLString *s) {

	AddOption();
	_options[_numoptions-1]->token = token;
	_options[_numoptions-1]->var_string = s;

	return *this;
}

CommandLine& CommandLine::AddToggle(const JLString &token, int *t) {

	AddOption();
	_options[_numoptions-1]->token = token;
	_options[_numoptions-1]->toggle = t;

	return *this;
}

CommandLine& operator<<(CommandLine &CL, const char *token) {

	if (CL._finish_add_option_flag) {
		cerr << "Error defining CommandLine option: "
			 << CL._options[CL._numoptions-1]->token << endl;
		exit(-1);
	}

	CL.AddOption();
	CL._options[CL._numoptions-1]->token.SetString(token);
	CL._finish_add_option_flag = 1;

	return CL;
}

CommandLine& operator<<(CommandLine &CL, int &i) {

	if (!CL._finish_add_option_flag) {
		cerr << "Error defining CommandLine option: Unexpected integer" << endl;
		exit(-1);
	}

	CL._options[CL._numoptions-1]->var_int = &i;
	CL._finish_add_option_flag = 0;

	return CL;
}

CommandLine& operator<<(CommandLine &CL, float &f) {

	if (!CL._finish_add_option_flag) {
		cerr << "Error defining CommandLine option: Unexpected float" << endl;
		exit(-1);
	}

	CL._options[CL._numoptions-1]->var_float = &f;
	CL._finish_add_option_flag = 0;

	return CL;
}

CommandLine& operator<<(CommandLine &CL, JLString &s) {

	if (!CL._finish_add_option_flag) {
		cerr << "Error defining CommandLine option: Unexpected string" << endl;
		exit(-1);
	}

	CL._options[CL._numoptions-1]->var_string = &s;
	CL._finish_add_option_flag = 0;

	return CL;
}

CommandLine& CommandLine::SetVariable(int o) {

	if (_index >= _argc) {
		cerr << "Error: Argument expected for: " << _argv[_index-1] << endl;
		exit(-1);
	}

	if (_options[o]->var_int) {
		*(_options[o]->var_int) = atoi(_argv[_index]);
		_index++;
		return *this;
	}

	if (_options[o]->var_float) {
		*(_options[o]->var_float) = atof(_argv[_index]);
		_index++;
		return *this;
	}

	if (_options[o]->var_char) {
		strcpy(*(_options[o]->var_char), _argv[_index]);
		_index++;
		return *this;
	}

	if (_options[o]->var_string) {
		_options[o]->var_string->SetString(_argv[_index]);
		_index++;
		return *this;
	}

	cerr << "ERROR: Check CheckOptions()" << endl;
	exit(-1);

	return *this;
}

CommandLine& CommandLine::CheckOption() {

	int i, o=-1;

	for (i=0; i<_numoptions; i++) {
		if (_options[i]->token == _argv[_index])
			o = i;
	}

	if (o == -1) {
		CheckTokenless();
		return *this;
	}

	_index++;

	if (_options[o]->toggle) {
		*(_options[o]->toggle) = 1;
		return *this;
	}

	SetVariable(o);

	return *this;
}

CommandLine& CommandLine::CheckTokenless() {

	int count=0;
	int i, o=-1;

	for (i=0; i<_numoptions; i++)
		if (_options[i]->token.GetLength() == 0) {
			if (count == _tokenless_count) {
				o = i;
				i = _numoptions;
			}
			count++;
		}

	if (o == -1) {
		cerr << "Error: Unrecognized command line option: "
			 << _argv[_index] << endl;
		exit(-1);
	}

	_tokenless_count++;

	SetVariable(o);

	return *this;
}

CommandLine& CommandLine::ParseCommandLine() {

	_index = 1;

	while (_index < _argc)
		CheckOption();

	return *this;
}
