/*-
 ***********************************************************************
 *
 * $Id: options.c,v 1.6 2012/05/01 14:43:49 mavrik Exp $
 *
 ***********************************************************************
 *
 * Copyright 2006-2012 The WebJob Project, All Rights Reserved.
 *
 ***********************************************************************
 */
#include "all-includes.h"

/*-
 ***********************************************************************
 *
 * OptionsFreeOptionsContext
 *
 ***********************************************************************
 */
void
OptionsFreeOptionsContext(OPTIONS_CONTEXT *psOptionsContext)
{
  if (psOptionsContext != NULL)
  {
    free(psOptionsContext);
  }
}


/*-
 ***********************************************************************
 *
 * OptionsGetArgumentIndex
 *
 ***********************************************************************
 */
int
OptionsGetArgumentIndex(OPTIONS_CONTEXT *psOptionsContext)
{
  return psOptionsContext->iArgumentIndex;
}


/*-
 ***********************************************************************
 *
 * OptionsGetArgumentsLeft
 *
 ***********************************************************************
 */
int
OptionsGetArgumentsLeft(OPTIONS_CONTEXT *psOptionsContext)
{
  return (psOptionsContext->iArgumentCount - (psOptionsContext->iArgumentIndex + 1));
}


/*-
 ***********************************************************************
 *
 * OptionsGetCommand
 *
 ***********************************************************************
 */
TCHAR *
OptionsGetCommand(OPTIONS_CONTEXT *psOptionsContext)
{
  return psOptionsContext->pptcArgumentVector[0];
}


/*-
 ***********************************************************************
 *
 * OptionsGetCurrentArgument
 *
 ***********************************************************************
 */
TCHAR *
OptionsGetCurrentArgument(OPTIONS_CONTEXT *psOptionsContext)
{
  TCHAR              *ptcArgument = NULL;

  if (psOptionsContext->iArgumentIndex < psOptionsContext->iArgumentCount)
  {
    ptcArgument = psOptionsContext->pptcArgumentVector[psOptionsContext->iArgumentIndex];
  }

  return ptcArgument;
}


/*-
 ***********************************************************************
 *
 * OptionsGetFirstArgument
 *
 ***********************************************************************
 */
TCHAR *
OptionsGetFirstArgument(OPTIONS_CONTEXT *psOptionsContext)
{
  TCHAR              *ptcArgument = NULL;

  psOptionsContext->iArgumentIndex = 1;
  if (psOptionsContext->iArgumentIndex < psOptionsContext->iArgumentCount)
  {
    ptcArgument = psOptionsContext->pptcArgumentVector[psOptionsContext->iArgumentIndex];
  }

  return ptcArgument;
}


/*-
 ***********************************************************************
 *
 * OptionsGetNextArgument
 *
 ***********************************************************************
 */
TCHAR *
OptionsGetNextArgument(OPTIONS_CONTEXT *psOptionsContext)
{
  TCHAR              *ptcArgument = NULL;

  if ((psOptionsContext->iArgumentIndex + 1) < psOptionsContext->iArgumentCount)
  {
    ptcArgument = psOptionsContext->pptcArgumentVector[++psOptionsContext->iArgumentIndex];
  }

  return ptcArgument;
}


/*-
 ***********************************************************************
 *
 * OptionsHaveRequiredOptions
 *
 ***********************************************************************
 */
int
OptionsHaveRequiredOptions(OPTIONS_CONTEXT *psOptionsContext)
{
  int                 iIndex = 0;

  for (iIndex = 0; iIndex < psOptionsContext->iNOptions; iIndex++)
  {
    if (psOptionsContext->psOptions[iIndex].iRequired == 1 && psOptionsContext->psOptions[iIndex].iFound == 0)
    {
      return 0;
    }
  }

  return 1;
}


/*-
 ***********************************************************************
 *
 * OptionsHaveSpecifiedOption
 *
 ***********************************************************************
 */
int
OptionsHaveSpecifiedOption(OPTIONS_CONTEXT *psOptionsContext, int iId)
{
  int                 iIndex = 0;

  for (iIndex = 0; iIndex < psOptionsContext->iNOptions; iIndex++)
  {
    if (iId == psOptionsContext->psOptions[iIndex].iId && psOptionsContext->psOptions[iIndex].iFound)
    {
      return 1;
    }
  }

  return 0;
}


/*-
 ***********************************************************************
 *
 * OptionsNewOptionsContext
 *
 ***********************************************************************
 */
OPTIONS_CONTEXT *
OptionsNewOptionsContext(int iArgumentCount, TCHAR *pptcArgumentVector[], TCHAR *ptcError)
{
  OPTIONS_CONTEXT    *psOptionsContext = NULL;

  /*-
   *********************************************************************
   *
   * Allocate and clear memory for the structure.
   *
   *********************************************************************
   */
  psOptionsContext = (OPTIONS_CONTEXT *) calloc(sizeof(OPTIONS_CONTEXT), 1);
  if (psOptionsContext == NULL)
  {
    _sntprintf(ptcError, MESSAGE_SIZE, _T("OptionsNewOptionsContext(): calloc(): %s"), strerror(errno));
    return NULL;
  }

  /*-
   *********************************************************************
   *
   * Initialize members.
   *
   *********************************************************************
   */
  psOptionsContext->pptcArgumentVector = pptcArgumentVector;
  psOptionsContext->iArgumentCount = iArgumentCount;

  return psOptionsContext;
}


/*-
 ***********************************************************************
 *
 * OptionsProcessOptions
 *
 ***********************************************************************
 */
int
OptionsProcessOptions(OPTIONS_CONTEXT *psOptionsContext, void *pvProperties, TCHAR *ptcError)
{
  TCHAR               atcLocalError[MESSAGE_SIZE] = { 0 };
  TCHAR              *ptcArgument = NULL;
  int                 iError = 0;
  int                 iIndex = 0;
  OPTIONS_TABLE      *psOptions = NULL;

  /*-
   *********************************************************************
   *
   * Define local variables to make the code easier to read.
   *
   *********************************************************************
   */
  psOptions = psOptionsContext->psOptions;

  /*-
   *********************************************************************
   *
   * Walk the argument list.
   *
   *********************************************************************
   */
  while ((ptcArgument = OptionsGetNextArgument(psOptionsContext)) != NULL)
  {
    /*-
     *******************************************************************
     *
     * Check for the end of options token ("--"), and break if found.
     *
     *******************************************************************
     */
    if (_tcscmp(ptcArgument, _T("--")) == 0)
    {
      break;
    }

    /*-
     *******************************************************************
     *
     * Scan the options table looking for a match on the nick name or
     * the full name. If a match is found, pass the option and its
     * argument, if any, to the designated handler.
     *
     *******************************************************************
     */
    for (iIndex = 0; iIndex < psOptionsContext->iNOptions; iIndex++)
    {
      if (
           (psOptions[iIndex].atcNickName[0] && _tcscmp(ptcArgument, psOptions[iIndex].atcNickName) == 0) ||
           (psOptions[iIndex].atcFullName[0] && _tcscmp(ptcArgument, psOptions[iIndex].atcFullName) == 0)
         )
      {
        if (psOptions[iIndex].iAllowRepeats == 0 && psOptions[iIndex].iFound == 1)
        {
          _sntprintf(ptcError, MESSAGE_SIZE, _T("OptionsProcessOptions(): The %s option may not be specified more than once."), ptcArgument);
          return OPTIONS_ER;
        }
/* TODO: Modify this section (down to the break) to support multiple arguments per option. */
        if (psOptions[iIndex].iNArguments > 0)
        {
          if (OptionsGetArgumentsLeft(psOptionsContext) < 1)
          {
            return OPTIONS_USAGE;
          }
          ptcArgument = OptionsGetNextArgument(psOptionsContext);
        }
        else
        {
          ptcArgument = NULL;
        }
        psOptions[iIndex].iFound = 1;
        iError = psOptions[iIndex].piHandler(&psOptions[iIndex], ptcArgument, pvProperties, atcLocalError);
        if (iError != OPTIONS_OK)
        {
          _sntprintf(ptcError, MESSAGE_SIZE, _T("OptionsProcessOptions(): %s"), atcLocalError);
          return OPTIONS_ER;
        }
        break;
      }
    }

    /*-
     *******************************************************************
     *
     * Reject unmatched arguments that look like options. Treat single
     * hyphens ("-") as arguments/operands, rather than options. Stop
     * searching for options at the first argument that looks like an
     * operand.
     *
     *******************************************************************
     */
    if (iIndex == psOptionsContext->iNOptions)
    {
      if (ptcArgument[0] == '-' && ptcArgument[1] != 0)
      {
        _sntprintf(ptcError, MESSAGE_SIZE, _T("OptionsProcessOptions(): option=[%s]: Unknown option."), ptcArgument);
        return OPTIONS_ER;
      }
      else
      {
        psOptionsContext->iArgumentIndex--; /* Decrement the index so the next OptionsGetNextArgument() will get this argument. */
        break;
      }
    }
  }

  return OPTIONS_OK;
}


/*-
 ***********************************************************************
 *
 * OptionsSetOptions
 *
 ***********************************************************************
 */
void
OptionsSetOptions(OPTIONS_CONTEXT *psOptionsContext, OPTIONS_TABLE *psOptions, int iNOptions)
{
  psOptionsContext->psOptions = psOptions;
  psOptionsContext->iNOptions = iNOptions;
}
