/***************************************************************************
 *
 * Author: "Sjors H.W. Scheres"
 * MRC Laboratory of Molecular Biology
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * This complete copyright notice must be included in any revised version of the
 * source code. Additional authorship citations may be added, but existing
 * author citations must be preserved.
 ***************************************************************************/
/***************************************************************************
 *
 * Authors:     Carlos Oscar S. Sorzano (coss@cnb.csic.es)
 *
 * Unidad de  Bioinformatica of Centro Nacional de Biotecnologia , CSIC
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
 * 02111-1307  USA
 *
 *  All comments concerning this program package may be sent to the
 *  e-mail address 'xmipp@cnb.csic.es'
 ***************************************************************************/

#include "src/args.h"
#include "src/gcc_version.h"
#include "src/matrix1d.h"

// Get parameters from the command line ====================================
std::string getParameter(int argc, char **argv, const std::string param, const std::string option)
{
    int i = 0;

    while ((i < argc) && (strcmp(param.c_str(), argv[i])))
        i++;
    if (i < argc - 1)
    {
        return(argv[i+1]);
    }
    else
    {
    	if (option != "NULL")
    	{
    		return (std::string)option;
    	}
    	else
        {
            std::string auxstr;
            auxstr = (std::string)"Argument " + param + " not found or invalid argument";
            REPORT_ERROR(auxstr);
        }
    }
}

// Checks if a boolean parameter was included the command line =============
bool checkParameter(int argc, char **argv, std::string param)
{
    int i = 0;

    while ((i < argc) && (strcmp(param.c_str(), argv[i]) != 0))
        i++;

    if (i < argc)
        return(true);
    else
        return(false);
}


IOParser::IOParser()
{
	clear();

}

IOParser::IOParser(const IOParser &in)
{
	copy(in);
}

IOParser& IOParser::operator= (const IOParser &in)
{
	copy(in);
}

IOParser::~IOParser()
{
	clear();
}


void IOParser::copy(const IOParser &in)
{
	options = in.options;
	usages = in.usages;
	optionals = in.optionals;
	defaultvalues = in.defaultvalues;
	argc = in.argc;
	argv = in.argv;
	error_messages = in.error_messages;
	warning_messages = in.warning_messages;
	current_section = in.current_section;
	section_names = in.section_names;
	section_numbers = in.section_numbers;
}

void IOParser::clear()
{
	argc = 0;
	argv = NULL;
	options.clear();
	usages.clear();
	optionals.clear();
	defaultvalues.clear();
	error_messages.clear();
	warning_messages.clear();
	section_names.clear();
	section_numbers.clear();
	current_section = 0;
}

void IOParser::setCommandLine(int _argc, char** _argv)
{
	argc = _argc;
	argv = _argv;
}

void IOParser::addOption(std::string option, std::string usage, std::string defaultvalue)
{
	if (section_names.size() == 0)
		REPORT_ERROR("IOParser::addOption: ERROR First add a section to the parser, then the options!");
	options.push_back(option);
	usages.push_back(usage);
	section_numbers.push_back(current_section);
	if (defaultvalue == "NULL")
	{
		optionals.push_back(false);
		defaultvalues.push_back(" ");
	}
	else
	{
		optionals.push_back(true);
		defaultvalues.push_back((std::string)defaultvalue);
	}
}

int IOParser::addSection(std::string name)
{
	current_section = section_names.size();
	section_names.push_back(name);
	return current_section;
}

/** Set the current section to this number */
void IOParser::setSection(int number)
{
	current_section = number;
}

bool IOParser::optionExists(std::string option)
{
	int ii;
	for (ii = 0; ii < options.size(); ii++)
		if (strcmp((options[ii]).c_str(), option.c_str()) == 0)
			return true;

	return false;

}

std::string IOParser::getOption(std::string option, std::string usage, std::string defaultvalue)
{

	// If this option did not exist yet, add it to the list
	if (!optionExists(option))
		addOption(option, usage, defaultvalue);

    int i = 0;

    while ((i < argc) && (strcmp(option.c_str(), argv[i])))
        i++;
    if (i < argc - 1)
    {
        return(argv[i+1]);
    }
    else
    {
    	if (defaultvalue != "NULL")
    	{
    		return (std::string)defaultvalue;
    	}
    	else
        {
            std::string auxstr;
            auxstr = (std::string)"ERROR: Argument " + option + " not found or invalid argument";
            error_messages.push_back(auxstr);
            return "";
        }
    }

}
// Checks if a boolean parameter was included the command line =============
bool IOParser::checkOption(std::string option, std::string usage, std::string defaultvalue)
{
	// If this option did not exist yet, add it to the list
	if (!optionExists(option))
		addOption(option, usage, defaultvalue);

	return checkParameter(argc, argv, option);
}

void IOParser::writeCommandLine(std::ostream &out)
{
	for (int i = 1; i < argc; i++)
		out << argv[i] << " ";
	out << std::endl;

}

bool IOParser::checkForErrors(int verb)
{

	// First check the command line for unknown arguments
	checkForUnknownArguments();

	// First print warning messages
	if (warning_messages.size() > 0)
	{
		if (verb > 0)
		{
			std::cerr << "The following warnings were encountered upon command-line parsing: " << std::endl;
			for (unsigned int i = 0; i < warning_messages.size(); ++i)
				std::cerr << warning_messages[i] << std::endl;
		}
	}

	// Then check for error messages
	if (error_messages.size() > 0)
	{
		if (verb > 0)
		{
			std::cerr << "The following errors were encountered upon command-line parsing: " << std::endl;
			for (unsigned int i = 0; i < error_messages.size(); ++i)
				std::cerr << error_messages[i] << std::endl;
		}
		return true;
	}
	else
		return false;

}

void IOParser::checkForUnknownArguments()
{
	for (int i = 1; i < argc; i++)
	{
		// Valid options should start with "--"
		bool is_ok = true;
		if (strncmp("--", argv[i], 2) == 0)
		{
			if (!optionExists((std::string)argv[i]))
			{
				is_ok = false;
			}
		}
		// If argv[i] starts with one "-": check it is a number and argv[i-1] is a valid option
		else if (strncmp("--", argv[i], 1) == 0)
		{
			float testval;
			// test whether this is a number
			int is_a_number = sscanf(argv[i], "%f", &testval);
		    if (is_a_number)
		    {
		    	// check whether  argv[i-1] is a valid option
		    	if (!optionExists(argv[i-1]))
		    		is_ok = false;
		    }
		    else
		    	is_ok = false;
		}

		if (!is_ok)
		{
			std::string auxstr;
			auxstr = (std::string)"WARNING: Option " + argv[i] + " is not a valid argument";
			warning_messages.push_back(auxstr);
		}


	}
}

void IOParser::writeUsageOneLine(int i, std::ostream &out)
{
	std::string aux = "  ";
	aux += options[i];

	if (optionals[i])
	{
		aux += " (";
		aux += defaultvalues[i];
		aux += ")";
	}

	out << std::setw(35) << aux;
	out << " : ";
	out << usages[i];
	out << std::endl;

}

void IOParser::writeUsageOneSection(int section, std::ostream &out)
{
	// First write all compulsory options
	//out << "+++ Compulsory:" << std::endl;
	for (int i = 0; i < options.size(); i++)
	{
		if (!optionals[i] && section_numbers[i] == section)
			writeUsageOneLine(i, out);
	}

	// Then write optional ones
	//out << "+++ Optional (defaults between parentheses):" << std::endl;
	for (int i = 0; i < options.size(); i++)
	{
		if (optionals[i] && section_numbers[i] == section)
			writeUsageOneLine(i, out);
	}

}

void IOParser::writeUsage(std::ostream &out)
{

	out << "+++ RELION: command line arguments (with defaults for optional ones between parantheses) +++"<<std::endl;

	for (int section = 0; section < section_names.size(); section++)
	{
		out << "====== " << section_names[section] << " ===== " << std::endl;
		writeUsageOneSection(section, out);
	}

}

