/*
 * settings.cpp
 *
 *  Created on: 22.11.2012
 *      Author: Gereon Kremer
 */

#include "src/utility/settings.h"

//#include <iostream>
//#include <boost/program_options.hpp>
//#include "src/exceptions/InvalidSettings.h"

namespace mrmc {
namespace settings {

namespace bpo = boost::program_options;

bpo::options_description mrmc::settings::Settings::configfile("Config Options");
bpo::options_description mrmc::settings::Settings::generic("Generic Options");
bpo::options_description mrmc::settings::Settings::commandline("Commandline Options");
bpo::positional_options_description mrmc::settings::Settings::positional;

bpo::options_description mrmc::settings::Settings::cli;
bpo::options_description mrmc::settings::Settings::conf;

mrmc::settings::Settings* mrmc::settings::Settings::inst = NULL;

/*!
 *	The constructor fills the option descriptions, parses the
 *	command line and the config file and puts the option values to
 *	our option mapping.
 *
 *	If a configfile is set in the commandline, we load this one.
 *	Otherwise, if filename is not NULL, we load this one. Otherwise,
 *	we load no config file.
 *
 *	@param argc should be argc passed to main function
 *	@param argv should be argv passed to main function
 *	@param filename	either NULL or name of config file
 */
Settings::Settings(const int argc, const char* argv[], const char* filename)
{
	try
	{
		/*
		 *	load command line
		 */
		bpo::store(bpo::command_line_parser(argc, argv).options(this->cli).positional(this->positional).run(), this->vm);
		/*
		 *	load config file if specified
		 */
		if (this->vm.count("configfile"))
		{
			bpo::store(bpo::parse_config_file<char>(this->vm["configfile"].as<std::string>().c_str(), this->conf), this->vm, true);
		}
		else if (filename != NULL)
		{
			bpo::store(bpo::parse_config_file<char>(filename, this->conf), this->vm, true);
		}
		bpo::notify(this->vm);
	}
	/*
	 *	catch errors...
	 */
	catch (bpo::reading_file e)
	{
		std::cout << "Could not read config file " << filename << std::endl;
	}
	catch (bpo::required_option e)
	{
		if (! (this->vm.count("help") || this->vm.count("help-config")))
		{
			std::cout << e.what() << std::endl;
			throw mrmc::exceptions::InvalidSettings();
		}
	}
	catch (bpo::error e)
	{
		std::cout << "Some error occurred: " << e.what() << std::endl;
	}
}

/*!
 *	Creates a new instance if necessary. When the instance is actually
 *	created for the first time, the internal options_description objects are
 *	initialized.
 *	
 *	If this function was already called and another instance is   
 *	already present, the existing instance will be returned. In this
 *	case, better use the routine mrmc::settings::instance().
 *
 *	The constructor fills the option descriptions, parses the
 *	command line and the config file and puts the option values to
 *	our option mapping.
 *
 *	If a configfile is set in the commandline, we load this one.
 *	Otherwise, if filename is not NULL, we load this one. Otherwise,
 *	we load no config file.
 *
 *	@param argc should be argc passed to main function
 *	@param argv should be argv passed to main function
 *	@param filename	either NULL or name of config file
 *	@return instance of Settings
 */
Settings* Settings::instance(const int argc, const char* argv[], const char* filename)
{
	if (Settings::inst == NULL) {
		/*
		 *	fill option descriptions
		 */
		Settings::commandline.add_options()
			("help,h", "produce help message")
			("verbose,v", "be verbose")
			("help-config", "produce help message about config file")
			("configfile,c", bpo::value<std::string>(), "name of config file")
		;
		Settings::generic.add_options()
			("trafile", bpo::value<std::string>()->required(), "name of the .tra file")
			("labfile", bpo::value<std::string>()->required(), "name of the .lab file")
		;
		Settings::configfile.add_options()
		;

		/*
		 *	construct option descriptions for commandline and config file
		 */
		Settings::cli.add(Settings::commandline).add(generic);
		Settings::conf.add(Settings::configfile).add(generic);
	
		/*
		 *	Take care of positional arguments
		 */
		Settings::positional.add("trafile", 1);
		Settings::positional.add("labfile", 1);
		
		/*
		 *	Actually create new instance
		 */
		Settings::inst = new Settings(argc, argv, filename);	        
	}
	return Settings::inst;
}

/*!
 *	Print a short general usage information consisting of the positional
 *	options and the list of available command line options.
 *
 *	Use it like this:
 *	@code std::cout << mrmc::settings::help; @endcode
 */
std::ostream& help(std::ostream& os)
{
	os << "Usage: <binary> [options] <transition file> <label file>" << std::endl;
	os << Settings::cli << std::endl;;
	return os;
}

/*!
 *	Print a list of available options for the config file.
 *
 *	Use it like this:
 *	@code std::cout << mrmc::settings::helpConfigfile; @endcode
 */
std::ostream& helpConfigfile(std::ostream& os)
{
	os << Settings::conf << std::endl;;
	return os;
}

/*!
 *	@return current instance.
 */
Settings* instance()
{
	return Settings::inst;
}
    
} // namespace settings

} // namespace mrmc