/**
 * @file config.cc
 *
 * Config - Configuration data handling for the capi tracer.
 *
 * Copyright: 2002-2003 Thomas Wintergerst. All rights reserved.
 *
 * $FreeBSD$
 * $Id: config.cc,v 1.1.2.1 2006/02/28 21:35:40 thomas Exp $
 * $Project:    CAPI for BSD $
 * $Target:     capitest - Test tool for the functionality of CAPI for BSD $
 * @date        19.08.2003
 * @author      "Thomas Wintergerst" <twinterg@gmx.de>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");

// System includes
#include <stdexcept>
#include <iostream>

// Import includes

#define __CONFIG__

// Local includes
#ifndef __CONFIG_H
#  include "config.h"
#endif

using namespace thwutil;





// === Private definitions ===============================================





/// The enum definition for the call type command line option.
CEnumStringParamCmdOpt::EnumDef_t g_aCallTypeEnumDefs [] =
   {
      { (int) CCapiStateMachine::CT_AUTO,      "a"     },
      { (int) CCapiStateMachine::CT_AUTO,      "auto"  },
      { (int) CCapiStateMachine::CT_VOICE,     "v"     },
      { (int) CCapiStateMachine::CT_VOICE,     "voice" },
      { (int) CCapiStateMachine::CT_DATA_HDLC, "h"     },
      { (int) CCapiStateMachine::CT_DATA_HDLC, "hdlc"  },
      { (int) CCapiStateMachine::CT_DATA_X75,  "x"     },
      { (int) CCapiStateMachine::CT_DATA_X75,  "x75"   },
      { (int) CCapiStateMachine::CT_FAXG3,     "f"     },
      { (int) CCapiStateMachine::CT_FAXG3,     "fax"   }
   };





// === Definitions for public declarations ===============================





/// The global config object.
CConfig e_oConfig;





// === Prototypes of private functions ===================================





// === Definitions for class members =====================================





// --- Class for configuration data handling -----------------------------

/**
 * Constructor.
 */

CConfig::CConfig (void):
   m_cmdLineEvaluator (),
   m_cmdOptListCtlr
      (m_cmdLineEvaluator, "l",
       CCmdOptBase::UNIQUE_OPTION, NULL),
   m_cmdOptInCalls
      (m_cmdLineEvaluator, "i",
       CCmdOptBase::UNIQUE_OPTION, NULL),
   m_cmdOptOutCalls
      (m_cmdLineEvaluator, "o",
       CCmdOptBase::HAS_PARAMETER |
          CCmdOptBase::UNIQUE_OPTION,
       &m_strOutputFileName),
   m_cmdOptRepOutCalls
      (m_cmdLineEvaluator, "O",
       CCmdOptBase::HAS_PARAMETER |
          CCmdOptBase::UNIQUE_OPTION,
       &m_strOutputFileName),
   m_cmdOptBatchCalls
      (m_cmdLineEvaluator, "b",
       CCmdOptBase::HAS_PARAMETER |
          CCmdOptBase::UNIQUE_OPTION,
       &m_strBatchFileName),
   m_cmdOptRepBatchCalls
      (m_cmdLineEvaluator, "B",
       CCmdOptBase::HAS_PARAMETER |
          CCmdOptBase::UNIQUE_OPTION,
       &m_strBatchFileName),
   m_cmdOptSelfConnect
      (m_cmdLineEvaluator, "s",
       CCmdOptBase::UNIQUE_OPTION, NULL),
   m_cmdOptRepSelfConnect
      (m_cmdLineEvaluator, "S",
       CCmdOptBase::UNIQUE_OPTION, NULL),
   m_cmdOptVerbose
      (m_cmdLineEvaluator, "v",
       0, &m_fVerboseOutput),
   m_cmdOptCtlr
      (m_cmdLineEvaluator, "c",
       CCmdOptBase::HAS_PARAMETER |
          CCmdOptBase::UNIQUE_OPTION,
       &m_uController,
       1, 1, 127),
   m_cmdOptAllControllers
      (m_cmdLineEvaluator, "a",
       0,
       &m_fUseAllControllers),
   m_cmdOptCallType
      (m_cmdLineEvaluator, "t",
       CCmdOptBase::HAS_PARAMETER |
          CCmdOptBase::IGNORE_PARAM_CASE |
          CCmdOptBase::UNIQUE_OPTION,
       &m_iCallType,
       g_aCallTypeEnumDefs, ARRAY_COUNT (g_aCallTypeEnumDefs)),
   m_cmdOptNumCalls
      (m_cmdLineEvaluator, "n",
       CCmdOptBase::HAS_PARAMETER,
       &m_uNumCalls,
       1, 1, UINT_MAX),
   m_cmdOptSendChecksumData
      (m_cmdLineEvaluator, "u",
       0,
       &m_fSendUUData),
   m_cmdOptEchoData
      (m_cmdLineEvaluator, "e",
       0,
       &m_fEchoData),
   m_cmdOptInputFileNamePattern
      (m_cmdLineEvaluator, "f",
       CCmdOptBase::HAS_PARAMETER |
          CCmdOptBase::UNIQUE_OPTION,
       &m_strInputFileNamePattern),
   m_cmdOptHelp1
      (m_cmdLineEvaluator, "h",
       CCmdOptBase::TERMINATE_EVALUATION, NULL),
   m_cmdOptHelp2
      (m_cmdLineEvaluator, "?",
       CCmdOptBase::TERMINATE_EVALUATION, NULL),
   m_mainTask (TASK_UNKNOWN),
   m_fVerboseOutput (false),
   m_uController (1),
   m_fUseAllControllers (false),
   m_iCallType ((int) CCapiStateMachine::CT_AUTO),
   m_uNumCalls (1),
   m_fSendUUData (false),
   m_fEchoData (false)
{
   // set the default program name, will be overwritten later in initialization
   m_strProgName = "capitest";
   
} // CConfig::CConfig





/**
 * Destructor.
 */

CConfig::~CConfig (void)
{
   // nothing to do
   
} // CConfig::~CConfig





/**
 * Perform general initialization.
 *
 * As this class is designed to create a static object, the constructor
 * will not get enough information for initializing the object. This will
 * thus be done by calling this member function.
 *
 * The main task is to set all internal member variables to reasonable
 * defaults. In addition the short program name is determined from the
 * argument specified. It is expected to be a file name, possibly
 * including some path specification. The basename of this file name will
 * be set as the program name.
 *
 * @param pszProgFileName       I: The file name of the current program, maybe
 *                                 including a path specification.
 *
 * @retval None.
 *
 * @throw std::invalid_argument
 *                              The argument is the NULL pointer or the empty
 *                              string.
 */

void CConfig::Init
   (const char *pszProgFileName)
{
   const char *p;
   
   // check for valid parameters
   if (! pszProgFileName || ! *pszProgFileName)
   {
      throw std::invalid_argument
               ("CConfig::Init(): Invalid program file name");
   }
   
   // find the basename in the file name
   p = strrchr (pszProgFileName, '/');
   if (p)
   {
      ++p;
   }
   else
   {
      p = pszProgFileName;
   }
   
   // set the internal short program name to the basename found
   m_strProgName = p;

} // CConfig::Init





/**
 * Evalutate the command line.
 *
 * The command line is evaluated in runs up to a non-option argument. This
 * ending argument will specify the first of two phone numbers. Where
 * supported this is the destination phone number, the second is the
 * optional source phone number.
 *
 * The options for the main tasks are mutually exclusive. Only one of them
 * may be specified on the command line. If none or more than one are
 * given, an error will be returned.
 *
 * @param iArgc                 I: The number of command line arguments in
 *                                 papszArgv.
 * @param papszArgv             I: The command line arguments as an array of
 *                                 pointers to character vectors.
 *
 * @retval TASK_UNKNOWN         No main action specified, i.e. neither "-l",
 *                              "-i", "-{oO}", "-{bB}" nor "-{sS}" was
 *                              specified on the command line.
 * @retval TASK_ERROR           An error occurred evaluating the command line.
 * @retval TASK_USAGE           Only the help text shall be printed.
 * @retval TASK_*               The respective task shall be executed.
 */

CConfig::TaskToExecute_t CConfig::EvalCommandLine
   (int    iArgc,
    char **papszArgv)
{
   int                                    iNextArg;
   CCmdLineEvaluator::CmdLineEvalResult_t resEval;

   // if the command line is empty, the task to execute is to print usage
   // information
   if (iArgc <= 1)
   {
      return (TASK_USAGE);
   }
   
   // evaluate the command line parameters up to the first non-option
   iNextArg = 1;
   resEval = m_cmdLineEvaluator.EvalCommandLine
                (iArgc, papszArgv, iNextArg, &iNextArg);

   // distinguish between the possible results
   switch (resEval)
   {
      case CCmdLineEvaluator::CLE_ERR_DUPLICATE_OPTION:
         // duplicate option error detected: abort
         std::cerr << GetProgName ()
                   << ": Options \"-i\" and \"-a\" may only be specified once"
                   << std::endl;
         return (TASK_ERROR);

      default:
         // other error detected: abort
         std::cerr << GetProgName ()
                   << ": Error in command line evaluation at \""
                   << (papszArgv [iNextArg] ? papszArgv [iNextArg] : "(NULL)")
                   << "\": " << m_cmdLineEvaluator.ResultMsg (resEval)
                   << std::endl;
         return (TASK_ERROR);

      case CCmdLineEvaluator::CLE_EVALUATION_TERMINATED:
         // evaluation terminated: must be help option, return to caller
         return (TASK_USAGE);

      case CCmdLineEvaluator::CLE_OK:
         // no error, continue with checking the action specified
         break;
   }

   // determine the main task to execute and check if more than one task was
   // specified
   m_mainTask = TASK_UNKNOWN;
   if (m_cmdOptListCtlr.WasFound ())
   {
      m_mainTask = (m_mainTask == TASK_UNKNOWN) ? TASK_LIST_CTLR : TASK_ERROR;
   }
   if (m_cmdOptInCalls.WasFound ())
   {
      m_mainTask = (m_mainTask == TASK_UNKNOWN) ? TASK_IN_CALLS : TASK_ERROR;
   }
   if (m_cmdOptOutCalls.WasFound ())
   {
      m_mainTask = (m_mainTask == TASK_UNKNOWN) ? TASK_OUT_CALLS : TASK_ERROR;
   }
   if (m_cmdOptRepOutCalls.WasFound ())
   {
      m_mainTask = (m_mainTask == TASK_UNKNOWN)
                   ? TASK_REP_OUT_CALLS : TASK_ERROR;
   }
   if (m_cmdOptBatchCalls.WasFound ())
   {
      m_mainTask = (m_mainTask == TASK_UNKNOWN) ? TASK_BATCH_CALLS : TASK_ERROR;
   }
   if (m_cmdOptRepBatchCalls.WasFound ())
   {
      m_mainTask = (m_mainTask == TASK_UNKNOWN)
                   ? TASK_REP_BATCH_CALLS : TASK_ERROR;
   }
   if (m_cmdOptSelfConnect.WasFound ())
   {
      m_mainTask = (m_mainTask == TASK_UNKNOWN)
                   ? TASK_SELF_CONNECT : TASK_ERROR;
   }
   if (m_cmdOptRepSelfConnect.WasFound ())
   {
      m_mainTask = (m_mainTask == TASK_UNKNOWN)
                   ? TASK_REP_SELF_CONNECT : TASK_ERROR;
   }
   if (m_mainTask == TASK_UNKNOWN)
   {
      std::cerr << GetProgName ()
                << ": Neither \"-l\", \"-i\", \"-{oO}\", \"-{bB}\" nor \"-{sS}\" specified, no task to execute"
                << std::endl;
      return (TASK_UNKNOWN);
   }
   if (m_mainTask == TASK_ERROR)
   {
      std::cerr << GetProgName ()
                << ": More than one task to execute specified"
                << std::endl;
      return (TASK_ERROR);
   }
   
   // copy the current command line parameter values to their propper member
   // variables
   m_cmdLineEvaluator.TakeCommandLineOptions ();
   
   // if the main task is "incoming calls" and no number of concurrent
   // connections was specified, the default number of concurrent calls should
   // be null to accept any number of connections
   if (m_mainTask == TASK_IN_CALLS && ! m_cmdOptNumCalls.WasFound ())
   {
      m_uNumCalls = 0;
   }
   
   // if there are more command line arguments, they are the destination and
   // (optional) source phone number, in this order
   if (iNextArg < iArgc && papszArgv [iNextArg] != NULL)
   {
      m_strDstPhoneNumber = papszArgv [iNextArg];
      ++iNextArg;
   }
   if (iNextArg < iArgc && papszArgv [iNextArg] != NULL)
   {
      m_strSrcPhoneNumber = papszArgv [iNextArg];
   }
   
   return (m_mainTask);
} // CConfig::EvalCommandLine





/**
 * Print hints for using this program.
 */

void CConfig::Usage (void)
{
   std::cout << std::endl;
   
   std::cout << m_strProgName
             << " - Performing ISDN connections for testing purposes"
             << std::endl;
   std::cout << std::endl;

   std::cout << "Usage:" << std::endl;
   std::cout << '\t' << m_strProgName
             << " [-h|-?]" << std::endl;
   std::cout << '\t' << m_strProgName
             << " -l [-v]" << std::endl;
   std::cout << '\t' << m_strProgName
             << " -i [-f <file name pattern>] [options]" << std::endl;
   std::cout << '\t' << m_strProgName
             << " {-o|-O} <source file> [options] <dest.no.> [<src.no.>]"
             << std::endl;
   std::cout << '\t' << m_strProgName
             << " {-b|-B} <batch file> [options]" << std::endl;
   std::cout << '\t' << m_strProgName
             << " {-s|-S} [options] <dest.no.> [<src.no.>]" << std::endl;
   std::cout << std::endl;
   std::cout << "\t-h|-?     Print out this help text." << std::endl;
   std::cout << "\t-l        List available controllers. The only additional option "
             << std::endl;
   std::cout << "\t          supported is \"-v\" for verbose output." << std::endl;
   std::cout << "\t-i        Accept incoming connections, terminate with <ctrl-c>. The"
             << std::endl;
   std::cout << "\t          data received may be stored into a file or discarded."
             << std::endl;
   std::cout << "\t-o        Do one series of outgoing calls. By default one call is made."
             << std::endl;
   std::cout << "\t          But this may be changed with \"-n#\" to perform # calls in"
             << std::endl;
   std::cout << "\t          parallel."
             << std::endl;
   std::cout << "\t-O        This is just like \"-o\", but each time an outgoing call is"
             << std::endl;
   std::cout << "\t          complete, it is restarted. Terminate with <ctrl-c>."
             << std::endl;
   std::cout << "\t-b        Do one series of calls specified in a batch file. For every"
             << std::endl;
   std::cout << "\t          call to be made, the batch file must contain a line with five"
             << std::endl;
   std::cout << "\t          whitespace separated fields for the controller number, call"
             << std::endl;
   std::cout << "\t          type, source file, destination phone number and source phone"
             << std::endl;
   std::cout << "\t          number."
             << std::endl;
   std::cout << "\t-B        This is just like \"-b\", but each time a call is completed,"
             << std::endl;
   std::cout << "\t          it is restarted. Terminate with <ctrl-c>."
             << std::endl;
   std::cout << "\t-s        Do a self connection and check for successful data transfer."
             << std::endl;
   std::cout << "\t-S        This is just like \"-s\", but each time a call is completed,"
             << std::endl;
   std::cout << "\t          it is restarted. Terminate with <ctrl-c>."
             << std::endl;
   std::cout << std::endl;

   std::cout << "Options supported:" << std::endl;

   std::cout << "\t-a\tHandle all available controllers, not only one. This option"
             << std::endl;
   std::cout << "\t\tis only meaningful for \"-i\", i.e. for handling incoming calls."
             << std::endl;
   std::cout << "\t-c#\tSpecify the controller number to use. Default is 1."
             << std::endl;
   std::cout << "\t-e\tEcho all incoming data. This option is only valid for"
             << std::endl;
   std::cout << "\t\tconnections that do not explicitly send out a data file. Echo"
             << std::endl;
   std::cout << "\t\tis also not supported for fax connections."
             << std::endl;
   std::cout << "\t-f <file name pattern>" << std::endl;
   std::cout << "\t\tFor \"-i\" the data received can be stored into files. The file"
             << std::endl;
   std::cout << "\t\tnames are created from this pattern with an ascending number"
             << std::endl;
   std::cout << "\t\tand an extension from the call type appended. The number turns"
             << std::endl;
   std::cout << "\t\taround at 999 to not use up all disk space."
             << std::endl;
   std::cout << "\t-n#\tSpecify the maximum number of concurrent connections. Default"
             << std::endl;
   std::cout << "\t\tis one connection."
             << std::endl;
   std::cout << "\t-t {a|auto|v|voice|h|hdlc|x|x75|f|fax}" << std::endl;
   std::cout << "\t\tSpecify the connection type. Default is automatic, i.e."
             << std::endl;
   std::cout << "\t\tdetermine the connection type from the D-channel information."
             << std::endl;
   std::cout << "\t\tNote that HDLC and X.75 cannot be distinguished through"
             << std::endl;
   std::cout << "\t\treceived D-channel information (as long as proprietary"
             << std::endl;
   std::cout << "\t\tuser-user data is not used). Auto is only valid for incoming"
             << std::endl;
   std::cout << "\t\tconnections."
             << std::endl;
   std::cout << "\t-u\tFor outgoing calls send out the file size and a checksum as an"
             << std::endl;
   std::cout << "\t\tuser-user data information element to the called party. The"
             << std::endl;
   std::cout << "\t\tpartner can use this information to check the received data for"
             << std::endl;
   std::cout << "\t\tcorrectness (" << m_strProgName << " will do this)."
             << std::endl;
   std::cout << "\t-v\tVerbose output. This will lead to more output for the"
             << std::endl;
   std::cout << "\t\tcontroller list (decode the bits for supported global options"
             << std::endl;
   std::cout << "\t\tand B-channel protocols) and during connections (a message for"
             << std::endl;
   std::cout << "\t\teach data block received or sent out is printed)."
             << std::endl;
   std::cout << std::endl;

   std::cout << "\tNote: Some options do not make sense for every main task."
             << std::endl;
   std::cout << std::endl;
   
} // CConfig::Usage





/**
 * General configuration: Get program name.
 */

const char *CConfig::GetProgName (void)
{
   return (m_strProgName.c_str ());
} // CConfig::GetProgName





/**
 * Get setting for verbose output.
 */

bool CConfig::GetVerboseOutput (void)
{
   return (m_fVerboseOutput);
} // CConfig::GetVerboseOutput





/**
 * Get setting for the addressed controller.
 */

unsigned CConfig::GetController (void)
{
   return (m_uController);
} // CConfig::GetController





/**
 * Get the setting to use all controllers.
 */

bool CConfig::GetUseAllControllers (void)
{
   return (m_fUseAllControllers);
} // CConfig::GetUseAllControllers





/**
 * Get the requested connection type.
 */

CCapiStateMachine::ConnectionType_t CConfig::GetConnectionType (void)
{
   return ((CCapiStateMachine::ConnectionType_t) m_iCallType);
} // CConfig::GetConnectionType





/**
 * Get the requested number of concurrent calls.
 */

size_t CConfig::GetNumCalls (void)
{
   return ((size_t) m_uNumCalls);
} // CConfig::GetNumCalls





/**
 * Get the setting to send user-user data.
 */

bool CConfig::GetSendUUData (void)
{
   return (m_fSendUUData);
} // CConfig::GetSendUUData





/**
 * Get the setting to echo all incoming data.
 */

bool CConfig::GetEchoData (void)
{
   return (m_fEchoData);
} // CConfig::GetEchoData





/**
 * Get the output file name.
 */

const std::string &CConfig::GetOutputFileName (void)
{
   return (m_strOutputFileName);
} // CConfig::GetOutputFileName





/**
 * Get the batch file name.
 */

const std::string &CConfig::GetBatchFileName (void)
{
   return (m_strBatchFileName);
} // CConfig::GetBatchFileName





/**
 * Get the input file name pattern.
 */

const std::string &CConfig::GetInputFileNamePattern (void)
{
   return (m_strInputFileNamePattern);
} // CConfig::GetInputFileNamePattern





/**
 * Get the destination phone number.
 */

const std::string &CConfig::GetDstPhoneNumber (void)
{
   return (m_strDstPhoneNumber);
} // CConfig::GetDstPhoneNumber





/**
 * Get the source phone number.
 */

const std::string &CConfig::GetSrcPhoneNumber (void)
{
   return (m_strSrcPhoneNumber);
} // CConfig::GetSrcPhoneNumber





// === Definitions for public functions ==================================





// === Definitions for private functions =================================
