/**------------------------------------------------------------------------
 * @file
 *
 * Copyright 2001 by the University of Toronto (UT).
 *
 *                       All Rights Reserved
 * 
 * Permission to copy and modify this software and its documentation 
 * only for internal use in your organization is hereby granted, 
 * provided that this notice is retained thereon and on all copies.  
 * UT makes no representations as to the suitability and operability 
 * of this software for any purpose.  It is provided "as is" without 
 * express or implied warranty.
 *
 * UT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT
 * SHALL UT BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
 * OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 *
 * No other rights, including for example, the right to redistribute 
 * this software and its documentation or the right to prepare 
 * derivative works, are granted unless specifically provided in a 
 * separate license agreement.
 *
 * Author: Diego Macrini
 * 
 * $Log: CmdLineParser.cpp,v $
 * Revision 1.2  2002/05/17 00:26:39  dmac
 * Modification to make the code work with any kind of DAG.
 *
 * Revision 1.1.1.1  2002/03/10 07:38:08  dmac
 * Imported sources
 *
 *
 *-----------------------------------------------------------------------*/

#include <iomanip>

#ifdef WIN32
# include "getopt.h"
#endif

#include "CmdLineParser.h"


/*
          struct option {
              const char *name;
              int has_arg;
              int *flag;
              int val;
          };

       The meanings of the different fields are:

       name   is the name of the long option.

       has_arg
              is:   no_argument   (or   0)  if  the  option  does  not  take  an  argument,
              required_argument  (or  1)  if  the   option   requires   an   argument,   or
              optional_argument (or 2) if the option takes an optional argument.

       flag   specifies  how results are returned for a long option.  If flag is NULL, then
              getopt_long() returns val.  (For example, the calling program may set val  to
              the  equivalent short option character.)  Otherwise, getopt_long() returns 0,
              and flag points to a variable which is set to val if the option is found, but
              left unchanged if the option is not found.

       val    is the value to return, or to load into the variable pointed to by flag.

       The last element of the array has to be filled with zeroes.

       If  longindex  is not NULL, it points to a variable which is set to the index of the
       long option relative to longopts.

*/

bool CmdLineParser::Parse(int argc, char** argv)
{         
	CmdLineArgT* pArg;
	bool bError = false;
	int c, i, j, nOptionIndex;

	SetPrgName(argv[0]);

	InitOptions();
	
	OPTION* longOptions = new OPTION[m_nOptArgs + 1];
	char* szShortOptions = new char[m_nOptArgs + 1];
	
	for (i = 0, j = 0; i < m_nOptArgs; i++)
	{
		pArg = m_pOptArgs[i];
		
		longOptions[i] = *pArg;
		
		if (pArg->HasShortName())
		{
			szShortOptions[j++] = pArg->GetShortName();
			
			if (pArg->HasOptArg())
				szShortOptions[j++] = ':';
		}
	}
	
    longOptions[m_nOptArgs].SetToZero();
	szShortOptions[j] = '\0';
	
	while (true)
	{
		c = getopt_long_only(argc, argv, szShortOptions, longOptions, &nOptionIndex);
		
		if (c == -1)
			break;
		else if (c == 0)
			pArg = m_pOptArgs[nOptionIndex];
		else if (c == '?') {
			bError = true;
			break;
		}
		else {
			// Find the arg that corresponds to the short option returned
			i = GetArgIndex(c);
			
			if (i >= 0)
				pArg = m_pOptArgs[i];
			else {	
				printf ("?? getopt returned character code 0%o ??\n", c);
				bError = true;
				break;
			}
		}
		
		pArg->Select(optarg);
	}

	delete[] longOptions;
	delete[] szShortOptions;
	
	if (bError)
	{
		ShowUsageMsg();
		return false;
	}

	// Process non-option ARGV-elements
	
	if (optind < argc)
	{
		m_pNonOptArgs = argv + optind;
		m_nNonOptArgs = argc - optind;
	}
	
	return true;
}

//! Find the arg that corresponds to the given short option
int CmdLineParser::GetArgIndex(char c)
{
	for (int i = 0; i < m_nOptArgs; i++)
		if (c == m_pOptArgs[i]->GetShortName())
			return i;

	return -1;
}

int CmdLineParser::GetArgIndex(const char* szArgName)
{
	for (int i = 0; i < m_nOptArgs; i++)
		if (!strcmp(szArgName, m_pOptArgs[i]->GetLongName()))
			return i;

	return -1;
}

bool CmdLineParser::IsSelected(char c)
{
	int i = GetArgIndex(c);
	return i >= 0 ? m_pOptArgs[i]->IsSelected():false;
}

bool CmdLineParser::IsSelected(const char* szArgName)
{
	int i = GetArgIndex(szArgName);
	return i >= 0 ? m_pOptArgs[i]->IsSelected():false;
}

void CmdLineParser::Print(std::ostream& os)
{
	os << "\nCurrent parameter values:\n\n";

	for (int i = 0; i < m_nOptArgs; i++)
		if (m_pOptArgs[i]->Print(os, false) && i < m_nOptArgs - 1) 
			os << ", ";

	os << std::endl;
}

void CmdLineParser::ExecActions()
{
	// Call the functions associated with the selected options
	for (int i = 0; i < m_nOptArgs; i++)
		if (m_pOptArgs[i]->IsSelected() && m_pOptArgs[i]->HasAction())
			ExecAction(m_pOptArgs[i]->GetAction());
}

void CmdLineParser::SetPrgName(const char* szProgramName)
{
	int len = strlen(szProgramName);
	int i;
	char c;

	for (i = len - 1; i >= 0; i--) 
	{
		c = szProgramName[i];

		if (c == '/' || c == '\\')
		{
			i++;
			break;
		}
	}

	m_szProgramName = szProgramName + i;
}

void CmdLineParser::ShowUsageMsg()
{
	const int FCS = 30; // first column size
	char szLine[512];
	char* p;

	CmdLineArgT* pArg;

	std::cerr << "\nUsage:\n\t" << m_szProgramName << " [option (val)]* ";
		
	if (m_szOptArgHelp)
		std::cerr << m_szOptArgHelp;
	
	std::cerr << "\n\nOptions:\n";

	for (int i = 0; i < m_nOptArgs; i++)
	{
		pArg = m_pOptArgs[i];
		p = szLine;

		strcpy(szLine, "\t");
		p += strlen(p);

		if (pArg->HasShortName())
		{
			sprintf(p, "-%c ", pArg->GetShortName());
			p += strlen(p);
		}

		if (pArg->HasLongName())
		{
			if (pArg->HasShortName())
			{
				strcpy(p, "or ");
				p += strlen(p);
			}

			sprintf(p, "-%s ", pArg->GetLongName());
			p += strlen(p);
		}

		if (pArg->HasOptArg())
		{
			sprintf(p, "[val]");
			p += strlen(p);
		}
		
		for (; p < szLine + FCS; p++)
			*p = ' ';

		strcpy(p, pArg->GetDesc());

		std::cerr << szLine << std::endl;
	}
	
	Print(std::cerr);

	ShowUsageExamples();
}

void CmdLineParser::ShowErrorMsg(const char* szMsg)
{
	std::cerr << std::endl << "Incorrect command line argument(s): " 
		<< szMsg << std::endl << std::endl;
}

//! ascii to ascii trivial function
LPCSTR asciitoascii(LPCSTR sz) 
{ 
	return sz; 
}
