text: /***************************************************************************
   *
   * 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"
  #include <algorithm>
  
  // 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, bool hidden)
  {
  	if (hidden)
  		hiddenOptions.push_back(option);
  	else
  	{
  		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)
  {
  	for (int ii = 0; ii < options.size(); ii++)
  		if (strcmp((options[ii]).c_str(), option.c_str()) == 0)
  			return true;
  
  	for (int ii = 0; ii < hiddenOptions.size(); ii++)
  		if (strcmp((hiddenOptions[ii]).c_str(), option.c_str()) == 0)
  			return true;
  
  	return false;
  
  }
  
  std::string IOParser::getOption(std::string option, std::string usage, std::string defaultvalue, bool hidden)
  {
  
  	// If this option did not exist yet, add it to the list
  	if (!optionExists(option))
  		addOption(option, usage, defaultvalue, hidden);
  
      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, bool hidden)
  {
  	// If this option did not exist yet, add it to the list
  	if (!optionExists(option))
  		addOption(option, usage, defaultvalue, hidden);
  
  	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)
  {
  
      if(argc==1 || (argc==2 && checkParameter(argc, argv, "--continue")) || checkParameter(argc, argv, "--help") || checkParameter(argc, argv, "-h"))
  	{
  		writeUsage(std::cout);
  	 	exit(0);
  	}
  	// 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
  		// or whether this is perhaps
  		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] + "\tis not a valid RELION 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);
  	}
  
  }
  
  void untangleDeviceIDs(std::string &tangled, std::vector < std::vector < std::string > > &untangled)
  {
  	// Handle GPU (device) assignments for each rank, if speficied
  	size_t pos = 0;
  	std::string delim = ":";
  	std::vector < std::string > allRankIDs;
  	std::string thisRankIDs, thisThreadID;
  	while ((pos = tangled.find(delim)) != std::string::npos)
  	{
  		thisRankIDs = tangled.substr(0, pos);
  //		    std::cout << "in loop " << thisRankIDs << std::endl;
  		tangled.erase(0, pos + delim.length());
  		allRankIDs.push_back(thisRankIDs);
  	}
  	allRankIDs.push_back(tangled);
  
  	untangled.resize(allRankIDs.size());
  	//Now handle the thread assignements in each rank
  	for (int i = 0; i < allRankIDs.size(); i++)
  	{
  		pos=0;
  		delim = ",";
  //			std::cout  << "in 2nd loop "<< allRankIDs[i] << std::endl;
  		while ((pos = allRankIDs[i].find(delim)) != std::string::npos)
  		{
  			thisThreadID = allRankIDs[i].substr(0, pos);
  //				std::cout << "in 3rd loop " << thisThreadID << std::endl;
  			allRankIDs[i].erase(0, pos + delim.length());
  			untangled[i].push_back(thisThreadID);
  		}
  		untangled[i].push_back(allRankIDs[i]);
  	}
  }
  
  