/*!
  @file           veo964.cpp
  @author         RaymondR
  @special area   ping
  @brief          Ping to vserver / niserver
  @see            example.html ...

\if EMIT_LICENCE

    ========== licence begin  GPL
    Copyright (c) 2000-2005 SAP AG

    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.
    ========== licence end



\endif
*/




/*===========================================================================*
 *  INCLUDES                                                                 *
 *===========================================================================*/

#include <stdio.h>
#include "gsp00.h"
#include "gsp01.h"
#include "geo03.h"
#include "heo03.h"
#include "SAPDBCommon/SAPDB_StandardArgumentParser.hpp"
#include "RunTime/System/RTESys_MicroTime.h"

/*===========================================================================*
 *  DEFINES                                                                  *
 *===========================================================================*/

#define OPTION_MESSAGE


/*===========================================================================*
 *  MACROS                                                                   *
 *===========================================================================*/



/*===========================================================================*
 *  LOCAL CLASSES, STRUCTURES, TYPES, UNIONS ...                             *
 *===========================================================================*/

class  PingArgParser : public SAPDB_StandardArgumentParser
{
public:
    PingArgParser ( const SAPDB_Int4            argc,
                    const RTE_ArgChar* const*   argv,
                    const SAPDB_UInt4           sendBufferSize,
                    const SAPDB_UInt1           maximumHops,
                    const SAPDB_UInt4           numOfQueries,
                    const SAPDB_Bool            silent );

    enum { optID_Help,
           optID_HostName,
           optID_ServerDB,
           optID_NumOfQueries,
           optID_SendBufferSize,
           optID_Silent,
           optID_MaximumHops};

    char const * const  HostName()  const      { return m_HostName; }
    char const * const  ServerDB()  const      { return m_ServerDB; } 
    SAPDB_UInt4         SendBufferSize() const { return m_SendBufferSize; } 
    SAPDB_UInt1         MaximumHops() const    { return m_MaximumHops; } 
    SAPDB_UInt4         NumOfQueries() const   { return m_NumOfQueries; }
    SAPDB_Bool          Silent() const         { return m_Silent; }
        
protected:
    virtual SAPDB_Bool HandleNextOption  ( const SAPDB_Int4       optionID,
                                           const RTE_ArgChar*     optionArgument = 0 );

    virtual SAPDB_Bool ArgumentWithoutOption( const SAPDB_Int4       argIndex,
                                              const RTE_ArgChar*     argument );

    virtual SAPDB_Bool ParseFinished( const RTE_ArgChar* const* argvRemaining,
                                      SAPDB_Int4                argsRemaining,
                                      SAPDB_Int4                argsParsed,
                                      SAPDB_Bool                breakOptionFound );

private:
    RTE_ArgChar          m_HostName[256];
    tsp00_DbNamec        m_ServerDB;
    SAPDB_UInt4          m_SendBufferSize;
    SAPDB_UInt1          m_MaximumHops;
    SAPDB_UInt4          m_NumOfQueries;
    SAPDB_Bool           m_Silent;
};

/*===========================================================================*
 *  STATIC/INLINE FUNCTIONS (PROTOTYPES)                                     *
 *===========================================================================*/

static PingArgParser::OptionDesc optionDesc[] = 
       {{ PingArgParser::optID_HostName,       RTE_ArgCharT('n'), RTE_ArgCharT("Host"),           OptionHasAnArgument      | OptionIsOptional, RTE_ArgCharT("<HostName>"),         RTE_ArgCharT("Host name/saprouter string.") },
        { PingArgParser::optID_ServerDB,       RTE_ArgCharT('d'), RTE_ArgCharT("ServerDB"),       OptionHasAnArgument      | OptionIsOptional, RTE_ArgCharT("<ServerDB>"),         RTE_ArgCharT("Name of the database.") },
        { PingArgParser::optID_SendBufferSize, RTE_ArgCharT('b'), RTE_ArgCharT("SendBufferSize"), OptionHasAnArgument      | OptionIsOptional, RTE_ArgCharT("<Count>"),            RTE_ArgCharT("Size of the send buffer in bytes (min 512).") },
        { PingArgParser::optID_MaximumHops,    RTE_ArgCharT('H'), RTE_ArgCharT("MaxHops"),        OptionHasAnArgument      | OptionIsOptional, RTE_ArgCharT("<MaximumHops>"),      RTE_ArgCharT("Maximum number of hops on the way to the target.") },
        { PingArgParser::optID_NumOfQueries,   RTE_ArgCharT('q'), RTE_ArgCharT("Queries"),        OptionHasAnArgument      | OptionIsOptional, RTE_ArgCharT("<NumOfQueries>"),     RTE_ArgCharT("Number of queries per hop.") },
        { PingArgParser::optID_Silent,         RTE_ArgCharT('S'), RTE_ArgCharT("Silent"),                                    OptionIsOptional, 0,                                  RTE_ArgCharT("Silent mode.") },
        { PingArgParser::optID_Help,           RTE_ArgCharT('h'), RTE_ArgCharT("Help"),           OptionArgumentIsOptional | OptionIsOptional, RTE_ArgCharT("<Option> | LONG"),    RTE_ArgCharT("Help, -h LONG shows long options.") },
};

/*===========================================================================*
 *  METHODS                                                                  *
 *===========================================================================*/

PingArgParser::PingArgParser ( const SAPDB_Int4            argc,
                               const RTE_ArgChar* const*   argv,
                               const SAPDB_UInt4           sendBufferSize,
                               const SAPDB_UInt1           maximumHops,
                               const SAPDB_UInt4           numOfQueries,
                               const SAPDB_Bool            silent )

              : m_SendBufferSize(sendBufferSize),
                m_MaximumHops(maximumHops),
                m_NumOfQueries(numOfQueries),
                m_Silent(silent),
                SAPDB_StandardArgumentParser ( argc, argv, PingArgParser::optID_Help, optionDesc, 
                                               NUM_OF_OPTIONS_DESC(optionDesc), false, false,  
                                               false ) // An automatic check not possible
                                                     // because we allow the first argument to be
                                                     // the host name ( Refer to 'ArgumentWithoutOption'
                                                     // and 'ParseFinished' )
{ 
    m_HostName[0] = '\0';
    m_ServerDB[0] = '\0';
}

/*---------------------------------------------------------------------------*/

SAPDB_Bool PingArgParser::HandleNextOption ( const SAPDB_Int4       optionID,
                                             const RTE_ArgChar*     optionArgument )
{
  SAPDB_UInt4  Tmp;
  SAPDB_Bool   ok = true;

  switch (optionID)
  {
  case optID_HostName:
      strncpy ( m_HostName, optionArgument, sizeof(m_HostName) - 1 );
      m_HostName[sizeof(m_HostName) - 1] = '\0';
      break;

  case optID_Silent:
      m_Silent = true;
      break;

  case optID_ServerDB:
      strncpy ( m_ServerDB, optionArgument, sizeof(m_ServerDB) - 1 );
      m_ServerDB[sizeof(m_ServerDB) - 1] = '\0';
      break;

  case optID_SendBufferSize:
      if (( false == ArgumentToUInt ( optionArgument, m_SendBufferSize )) || 
          ( 512   >  m_SendBufferSize ))
      {
          printf ( "Invalid buffer size '%s' (min: 512)\n", optionArgument );
          ok = false;
      }
      break;

  case optID_MaximumHops:
      if (( false == ArgumentToUInt ( optionArgument, Tmp )) ||
          ( 255  < Tmp ))
      {
          printf ( "Invalid number of maximum hops '%s' (max: 255)\n", optionArgument );
          ok = false;
      }
      else
        m_MaximumHops = (SAPDB_UInt1)Tmp;
      break;

  case optID_NumOfQueries:
      if ( false == ArgumentToUInt ( optionArgument, m_NumOfQueries ))
      {
          printf ( "Invalid number of queries '%s'\n", optionArgument );
          ok = false;
      }
      break;

  default:
      printf ( "Unknown option identfier '%d'\n", optionID  );
      ok = false;
      break;
  }

  return ok;
}
/*---------------------------------------------------------------------------*/

SAPDB_Bool PingArgParser::ArgumentWithoutOption( const SAPDB_Int4       argIndex,
                                                 const RTE_ArgChar*     argument )
{
  // The first argument might be the host name if the old
  // argument list is used "x_ping <hostname>"
  if ( argIndex == 1 )
  {
      strncpy ( m_HostName, argument, sizeof(m_HostName) - 1 );
      m_HostName[sizeof(m_HostName) - 1] = '\0';
      return true;
  }
  return false;
}

/*---------------------------------------------------------------------------*/

SAPDB_Bool PingArgParser::ParseFinished( const RTE_ArgChar* const* argvRemaining,
                                         SAPDB_Int4                argsRemaining,
                                         SAPDB_Int4                argsParsed,
                                         SAPDB_Bool                breakOptionFound ) 
{ 
    // We have to check the if the host name or database name has specified because
    // 'handleMissingNoneOptionalOptions' was set to false.
    if (( 0 < argsRemaining ) || ( 0 == argsParsed ) || 
        (( '\0' == m_HostName[0] ) && ( '\0' == m_ServerDB[0] )) )
    {
        Help();
        return false;
    }
    return true;
};

/*---------------------------------------------------------------------------*/

#define MAX_LINE_LEN 80

static void ShowAndRemoveNiErrorTrace (int ShowBeforeRemove )
{
  tsp00_Pathc NiErrorTrace ;

  eo03NiBuildTracefileName  (NiErrorTrace) ;

  if ( ShowBeforeRemove )
  {
    FILE *stream = fopen ( NiErrorTrace, "r" ) ;
    if ( stream != (FILE *) NULL )
    { 
        char line[ MAX_LINE_LEN + 1 ] ;
        bool ShowOk ;
        do
          if ( (ShowOk = fgets ( line, MAX_LINE_LEN , stream ) != NULL) )
            printf ("%s", line ) ;
        while ( ShowOk ) ;
        fclose ( stream ) ;
    }
  else
    { perror ( "open error" ) ;
      printf ( "missing NiErrorTrace  >%s<", (char *) NiErrorTrace ) ;
    }
  }

  /* Remove Tracefile */
  unlink(NiErrorTrace);

  return ;
}

/*---------------------------------------------------------------------------*/

static int DoDBPing ( tsp00_Int4                         connection,
                      char*                              szServerNode,
                      char*                              szServerDB,
                      tsp00_Uint4                        queries,
                      tsp00_Uint4                        sendBufferSize,
                      tsp00_Uint1                        maxHops,
                      SAPDB_Bool                         silent )
{
    tsp00_ErrTextc    ErrTextC; 
    int               rc        = 0;
    SAPDB_UInt8       time      = 0;
    SAPDB_UInt8       sumTime   = 0;
    SAPDB_UInt8       startTime = 0;
    SAPDB_UInt8       stopTime  = 0;
    SAPDB_UInt8       avgTime   = 0;
    SAPDB_UInt8       maxTime   = 0;
    SAPDB_UInt8       minTime   = (tsp00_Uint4)-1;
    tsp00_Uint1       hops;
    tsp00_Uint4       loopCnt   = 0;
    tsp00_Uint1       hopCnt;
    tsp00_DbNamec     server;
    tsp00_Versionc    serverVersion;

    ErrTextC[0] = '\0';

    if ( szServerNode[0] == '\0' )
        printf("\nPinging %s", szServerDB );
    else
        printf("\nPinging %s on %s", szServerDB, szServerNode );

    RTESys_InitMicroSecTimer();

    hops = hopCnt = 0; // start with one hop

    if ( SqlDBPing ( connection, &sendBufferSize, &hops, server, serverVersion, ErrTextC ) == commErrOk_esp01 )
    {
        printf(" with %d bytes of data over a maximum of %d hops.", sendBufferSize, maxHops );

        if ( false == silent )
        {
            printf("\n\nHop  Server\n%2d   %s\n", hopCnt, (char*)server );

            while( !hops )
            {
                hops = ++hopCnt;

                if ( SqlDBPing ( connection, &sendBufferSize, &hops, server, serverVersion, ErrTextC ) != commErrOk_esp01)
                    break;

                printf(" %d   %s\n", hopCnt, (char*)server );
            }

            printf("\n", sendBufferSize );
        }

        do 
        {
            hops      = maxHops;
            startTime = RTESys_MicroSecTimer();

            if ( SqlDBPing ( connection, &sendBufferSize, &hops, server, serverVersion, ErrTextC ) == commErrOk_esp01 )
            {
                stopTime  = RTESys_MicroSecTimer();
                time      = stopTime - startTime;
                sumTime  += time;

                if ( time < minTime )
                    minTime = time;

                if ( time > maxTime )
                    maxTime = time;

                if ( false == silent )
                {
                    if ( time < 10000 )
                        printf( "%s: reply time=%-3dus\n", (char *)server, (tsp00_Uint4)time );
                    else
                        printf( "%s: reply time=%-3dms\n", (char *)server, (tsp00_Uint4)(time / 1000) );
                }
            }
            else
                rc = 1;
        }
        while (( rc == 0 ) && ( ++loopCnt < queries ));
    }
    else
    {
        printf("\n\n");
        rc = 1;
    }

    if ( rc == 0 )
    {
        avgTime = sumTime / loopCnt;

        printf( "\n\n%s: '%s'\n\n", (char *)server, 
                serverVersion[0] != '\0' ? (char *)serverVersion : "UNKNOWN");

        printf( "Approximate round trip times: \n"
                "Minimum = %d%s, Maximum = %d%s, Average = %d%s\n\n",
                (tsp00_Uint4)(( minTime < 10000 ) ? minTime : minTime / 1000),
                ( minTime < 10000 ) ? "us" : "ms",
                (tsp00_Uint4)(( maxTime < 10000 ) ? maxTime : maxTime / 1000),
                ( maxTime < 10000 ) ? "us" : "ms",
                (tsp00_Uint4)(( avgTime < 10000 ) ? avgTime : avgTime / 1000),
                ( avgTime < 10000 ) ? "us" : "ms" );
    }
    else
    {
        printf("ERROR\n\n");
        printf("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n\n");
        printf("Problem: %s\n\n", (char *)ErrTextC );
        printf("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");

    }

    return rc;
}

/*-----------------------------------------------------------*/
int main ( int argc, char ** argv )
/*-----------------------------------------------------------*/
{
    int               rc;
    tsp00_ErrTextc    ErrTextC; 
    tsp00_Int4        connection;

    ErrTextC[0] = '\0';

    PingArgParser   ArgParser( argc,argv, 
                               512,      // default buffer size
                               10,       // default maximum hops
                               5,        // default queries
                               false );  // we are not silent (true), we are loud (false)

    if ( ArgParser.StartParsing() )
    {
        if ( ArgParser.ServerDB()[0] != '\0' )
        {

            if ( SqlPingConnect ((char*)ArgParser.HostName(), (char*)ArgParser.ServerDB(), &connection, ErrTextC ) == commErrOk_esp01 )
            {
                rc = DoDBPing ( connection, (char*)ArgParser.HostName(), (char*)ArgParser.ServerDB(),
                                ArgParser.NumOfQueries(), ArgParser.SendBufferSize(), 
                                ArgParser.MaximumHops(), ArgParser.Silent() );

                if ( SqlIsSaprouterString ((char*)ArgParser.HostName()) ) 
                    ShowAndRemoveNiErrorTrace(rc);

                SqlPingRelease(connection);
            }
            else
            {
                printf("ERROR\n\n");
                printf("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n\n");
                printf("Problem: %s\n\n", (char *)ErrTextC );
                printf("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
            }
       }
       else
       {
           tsp00_Versionc    serverVersion;

           serverVersion[0] = '\0' ;

           printf("\nChecking connection to '%s' ... ", ArgParser.HostName() );

           if ( SqlAPing ( (char*)ArgParser.HostName(), serverVersion, ErrTextC ) == commErrOk_esp01 )
           {
               printf("ok.\n\n");
               printf("Version of Server is\n");
               printf("----------------------------------------------------------\n");
               printf("%s\n", serverVersion[0] != '\0' ? (char *)serverVersion : "UNKNOWN");
               printf("----------------------------------------------------------\n");
               rc = 0 ;
           }
           else
           {
               printf("ERROR\n\n");
               printf("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n\n");
               printf("Problem: %s\n\n", (char *)ErrTextC );
               printf("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
               rc = 1 ;
           }
           if ( SqlIsSaprouterString ((char*)ArgParser.HostName()) ) 
               ShowAndRemoveNiErrorTrace(+rc);
       }
    }

    return (rc);
}

/*===========================================================================*
 *  END OF CODE                                                              *
 *===========================================================================*/
