/*
 * This file is part of IPAACA, the
 *  "Incremental Processing Architecture
 *   for Artificial Conversational Agents".  
 *
 * Copyright (c) 2009-2013 Sociable Agents Group
 *                         CITEC, Bielefeld University   
 *
 * http://opensource.cit-ec.de/projects/ipaaca/
 * http://purl.org/net/ipaaca
 *
 * This file may be licensed under the terms of of the
 * GNU Lesser General Public License Version 3 (the ``LGPL''),
 * or (at your option) any later version.
 *
 * Software distributed under the License is distributed
 * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either
 * express or implied. See the LGPL for the specific language
 * governing rights and limitations.
 *
 * You should have received a copy of the LGPL along with this
 * program. If not, go to http://www.gnu.org/licenses/lgpl.html
 * or write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  
 *
 * The development of this software was supported by the
 * Excellence Cluster EXC 277 Cognitive Interaction Technology.
 * The Excellence Cluster EXC 277 is a grant of the Deutsche
 * Forschungsgemeinschaft (DFG) in the context of the German
 * Excellence Initiative.
 */

#include <ipaaca/ipaaca.h>

#ifndef WIN32
#include <getopt.h>
#endif

namespace ipaaca {

//
// Command line options implementation
//


void CommandLineOptions::set_option(const std::string& name, bool expect, const char* optarg) {
	param_set[name] = true;
	if (expect) {
		param_opts[name] = optarg;
	}
}
std::string CommandLineOptions::get_param(const std::string& o) {
	std::map<std::string, std::string>::iterator it = param_opts.find(o);
	if (it==param_opts.end()) return "";
	return it->second;
}
bool CommandLineOptions::is_set(const std::string& o) {
	std::map<std::string, bool>::iterator it = param_set.find(o);
	if (it==param_set.end()) return false;
	return it->second;
}
void CommandLineOptions::dump() {
	for (std::map<std::string, bool>::iterator it=param_set.begin(); it!=param_set.end(); ++it) {
		std::map<std::string, std::string>::iterator it2 = param_opts.find( it->first );
		if (it2 == param_opts.end()) {
			std::cout << it->first << "\t<true>" << std::endl;
		} else {
			std::cout << it->first << "\t" << it2->second << std::endl;
		}
	}
}

//
// Command line parser implementation
//

CommandLineParser::CommandLineParser()
{
	initialize_parser_defaults();
}

void CommandLineParser::initialize_parser_defaults()
{
	add_option("help",           0  , false, "");
	add_option("verbose",        'v', false, "");
	add_option("character-name", 'c', true, "UnknownCharacter");
	add_option("component-name", 'n', true, "UnknownComponent");
}

void CommandLineParser::dump_options()
{
	for (std::map<std::string, bool>::const_iterator it = options.begin(); it!=options.end(); ++it) {
		const std::string& optn = it->first;
		bool expect = it->second;
		char shortn = shortopt[optn];
		std::string shortns; shortns += shortn;
		if (optn != "help") {
			if (shortn) std::cout << "    -" << shortns << " | --" << optn << " ";
			else std::cout << "    --" << optn << " ";
			if (expect) {
				std::cout << "<param>";
				std::cout << " (default: '" << defaults[optn] << "')";
			}
			std::cout << std::endl;
		}
	}
}

void CommandLineParser::add_option(const std::string& optname, char shortoptn, bool expect_param, const std::string& defaultv)
{
	longopt[shortoptn] = optname;
	shortopt[optname] = shortoptn;
	options[optname] = expect_param;
	defaults[optname] = defaultv;
	set_flag[optname] = 0;
}

CommandLineOptions::ptr CommandLineParser::parse(int argc, char* const* argv)
{
#ifdef WIN32
	LOG_IPAACA_CONSOLE("IMPLEMENT ME: command line parsing for Windows. (req'd: getopt)")
	throw NotImplementedError();
#else
	int len = options.size();
	struct option long_options[len+1];
	int i=0;
	std::string short_options_str = "";
	for (std::map<std::string, bool>::const_iterator it = options.begin(); it!=options.end(); ++it) {
		const std::string& optn = it->first;
		bool expect = it->second;
		char shortn = shortopt[optn];
		int* write_to = &(set_flag[optn]);
		if (shortn > ' ') {
			short_options_str += shortn;
			if (expect) short_options_str += ':';
		}
		long_options[i].name = optn.c_str();
		long_options[i].has_arg = (expect?required_argument:no_argument);
		long_options[i].flag = (expect?write_to:0);
		long_options[i].val = shortn;
		i++;
	}
	long_options[i].name = 0;
	long_options[i].has_arg = 0;
	long_options[i].flag = 0;
	long_options[i].val = 0;
	CommandLineOptions::ptr clo = CommandLineOptions::ptr(new CommandLineOptions());
	int c;
	bool keep_going = true;
	while (keep_going)
	{
		// getopt_long stores the option index here.
		int option_index = 0;
		c = getopt_long (argc, argv, short_options_str.c_str(), long_options, &option_index);
		// Detect the end of the options. 
		if (c == -1) break;
		
		switch (c)
		{
			case 0:
				{
				std::string longname = long_options[option_index].name;
				if (longname == "help") {
					std::cout << "Options:" << std::endl;
					dump_options();
					exit(0);
				}
				std::string longoption = long_options[option_index].name;
				bool expect = options[longoption];
				clo->set_option(longoption, expect, optarg);
				}
				break;

			case '?':
				break;

			default:
				std::string s;
				s += c;
				std::string longoption = longopt[c];
				bool expect = options[longoption];
				clo->set_option(longoption, expect, optarg);
		}
	}
	ensure_defaults_in( clo );
	return clo;
#endif
}

void CommandLineParser::ensure_defaults_in( CommandLineOptions::ptr clo )
{
	for (std::map<std::string, bool>::const_iterator it = options.begin(); it!=options.end(); ++it) {
		const std::string& optn = it->first;
		bool expect = it->second;
		char shortn = shortopt[optn];
		if (expect && (! clo->is_set(optn))) {
			clo->set_option(optn, true, defaults[optn].c_str());
		}
	}
}

} // namespace ipaaca