/*----------------------------------------------------------------------------*
 *                    Copyright (C) 2010 Amit Chawre.                         *
 *----------------------------------------------------------------------------*/


/**
 * @file NwEgtPingMain.c
 * @brief This is a program demostrating usage of nw-gtpv2c library for eGTP ping.
*/

#include <stdio.h>
#include <assert.h>
#include <signal.h>
#include <string.h>
#include "NwEvt.h"
#include "NwLog.h"
#include "NwGtpv2c.h"

#include "NwMiniLogMgrEntity.h"
#include "NwMiniTmrMgrEntity.h"
#include "NwMiniUdpEntity.h"
#include "NwMiniUlpEntity.h"

#ifndef NW_ASSERT
#define NW_ASSERT assert
#endif

static
NwCharT* gLogLevelStr[] = {"EMER", "ALER", "CRIT",  "ERRO", "WARN", "NOTI", "INFO", "DEBG"};


typedef struct NwEgtPingS {
  NwU8T                         localIpStr[20];
  NwU8T                         targetIpStr[20];
  NwU32T                        pingInterval;
  NwU32T                        pingCount;
  NwGtpv2cNodeUlpT              ulpObj;
  NwGtpv2cNodeUdpT              udpObj;
} NwEgtPingT;

static NwGtpv2cNodeUlpT              ulpObj;
static NwGtpv2cNodeUdpT              udpObj;

static NwEgtPingT               egtPing;

void nwEgtPingHandleSignal(int sigNum)
{
  printf("\n--- %s ("NW_IPV4_ADDR") EGTPING statistics --- ", egtPing.targetIpStr, NW_IPV4_ADDR_FORMAT(inet_addr(egtPing.targetIpStr)));
  printf("\n%u requests sent, %u response received, %d%% packet loss \n\n", udpObj.packetsSent, udpObj.packetsRcvd,
         (udpObj.packetsSent ? 100 * (udpObj.packetsSent - udpObj.packetsRcvd ) / udpObj.packetsSent : 0) );
  exit(sigNum);
}

NwRcT
nwEgtPingHelp()
{
  printf("Usage: egtping [-i interval] [-c count] [-l local-ip] ");
  printf("\n               [-t3 t3-time] [-n3 n3-count] destination");
  printf("\n");
  printf("\n       -i <interval>     : Interval between two echo request messages. (Default: 1 sec)");
  printf("\n       -c <count>        : Stop after sending count pings. (Default: Infinite)");
  printf("\n       -t <t3-time>      : GTP T3 timeout value. (Default: 2 sec)");
  printf("\n       -n <n3-count>     : GTP N3 count value. (Default: 2 sec)");
  printf("\n       -l <local-ip>     : Local IP adddress to use. (Default: All local IPs)");
  printf("\n       -h                : Show this message.");
  printf("\n");
  printf("\n");
}

NwRcT
nwEgtPingParseCmdLineOpts(int argc, char* argv[])
{
  NwRcT rc = NW_OK;
  int i = 0;
  i++;

  egtPing.pingInterval  = 1;
  egtPing.pingCount     = 0xffffffff;

  if(argc < 2)
    return NW_FAILURE;

  if( (argc == 2) &&
      ((strcmp("--help", argv[i]) == 0)
       || (strcmp(argv[i], "-h") == 0)))
    return NW_FAILURE;

  while( i < argc - 1) {
    NW_LOG(NW_LOG_LEVEL_DEBG, "Processing cmdline arg %s", argv[i]);

    if((strcmp("--local-ip", argv[i]) == 0)
        || (strcmp(argv[i], "-l") == 0)) {
      i++;

      if(i >= (argc - 1))
        return NW_FAILURE;

      strcpy(egtPing.localIpStr, (argv[i]));

    } else if((strcmp("--interval", argv[i]) == 0)
              || (strcmp(argv[i], "-i") == 0)) {
      i++;

      if(i >= (argc - 1))
        return NW_FAILURE;

      egtPing.pingInterval = atoi(argv[i]);

    } else if((strcmp("--count", argv[i]) == 0)
              || (strcmp(argv[i], "-c") == 0)) {
      i++;

      if(i >= (argc - 1))
        return NW_FAILURE;

      egtPing.pingCount = atoi(argv[i]);
    } else if((strcmp("--help", argv[i]) == 0)
              || (strcmp(argv[i], "-h") == 0)) {
      rc = NW_FAILURE;
    } else {
      return NW_FAILURE;
    }

    i++;
  }

  strcpy(egtPing.targetIpStr, (argv[i]));

  return rc;
}

/*---------------------------------------------------------------------------
 *                T H E      M A I N      F U N C T I O N
 *--------------------------------------------------------------------------*/

int main(int argc, char* argv[])
{
  NwRcT                         rc;
  NwU32T                        logLevel;
  NwU8T*                        logLevelStr;

  NwGtpv2cStackHandleT          hGtpv2cStack = 0;

  NwGtpv2cUlpEntityT            ulp;
  NwGtpv2cUdpEntityT            udp;
  NwGtpv2cTimerMgrEntityT       tmrMgr;
  NwGtpv2cLogMgrEntityT         logMgr;

  printf("EGTPING 0.1, Copyright (C) 2011 Amit Chawre.\n");
  rc = nwEgtPingParseCmdLineOpts(argc, argv);

  if(rc != NW_OK) {
    rc = nwEgtPingHelp();
    exit(rc);
  }

  logLevelStr = getenv ("NW_LOG_LEVEL");

  if(logLevelStr == NULL) {
    logLevel = NW_LOG_LEVEL_INFO;
  } else {
    if(strncmp(logLevelStr, "EMER",4) == 0)
      logLevel = NW_LOG_LEVEL_EMER;
    else if(strncmp(logLevelStr, "ALER",4) == 0)
      logLevel = NW_LOG_LEVEL_ALER;
    else if(strncmp(logLevelStr, "CRIT",4) == 0)
      logLevel = NW_LOG_LEVEL_CRIT;
    else if(strncmp(logLevelStr, "ERRO",4) == 0)
      logLevel = NW_LOG_LEVEL_ERRO ;
    else if(strncmp(logLevelStr, "WARN",4) == 0)
      logLevel = NW_LOG_LEVEL_WARN;
    else if(strncmp(logLevelStr, "NOTI",4) == 0)
      logLevel = NW_LOG_LEVEL_NOTI;
    else if(strncmp(logLevelStr, "INFO",4) == 0)
      logLevel = NW_LOG_LEVEL_INFO;
    else if(strncmp(logLevelStr, "DEBG",4) == 0)
      logLevel = NW_LOG_LEVEL_DEBG;
  }

  /*---------------------------------------------------------------------------
   *  Initialize event library
   *--------------------------------------------------------------------------*/

  NW_EVT_INIT();

  /*---------------------------------------------------------------------------
   *  Initialize Log Manager
   *--------------------------------------------------------------------------*/
  nwMiniLogMgrInit(nwMiniLogMgrGetInstance(), logLevel);

  /*---------------------------------------------------------------------------
   *  Initialize Gtpv2c Stack Instance
   *--------------------------------------------------------------------------*/
  rc = nwGtpv2cInitialize(&hGtpv2cStack);

  if(rc != NW_OK) {
    NW_LOG(NW_LOG_LEVEL_ERRO, "Failed to create gtpv2c stack instance. Error '%u' occured", rc);
    exit(1);
  }

  rc = nwGtpv2cSetLogLevel(hGtpv2cStack, logLevel);

  /*---------------------------------------------------------------------------
   * Set up Ulp Entity
   *--------------------------------------------------------------------------*/
  rc = nwGtpv2cUlpInit(&ulpObj, hGtpv2cStack, egtPing.localIpStr);
  NW_ASSERT(NW_OK == rc);

  ulp.hUlp = (NwGtpv2cUlpHandleT) &ulpObj;
  ulp.ulpReqCallback = nwGtpv2cUlpProcessStackReqCallback;

  rc = nwGtpv2cSetUlpEntity(hGtpv2cStack, &ulp);
  NW_ASSERT(NW_OK == rc);

  /*---------------------------------------------------------------------------
   * Set up Udp Entity
   *--------------------------------------------------------------------------*/
  rc = nwGtpv2cUdpInit(&udpObj, hGtpv2cStack, egtPing.localIpStr);
  NW_ASSERT(NW_OK == rc);

  udp.hUdp = (NwGtpv2cUdpHandleT) &udpObj;
  udp.udpDataReqCallback = nwGtpv2cUdpDataReq;

  rc = nwGtpv2cSetUdpEntity(hGtpv2cStack, &udp);
  NW_ASSERT(NW_OK == rc);

  /*---------------------------------------------------------------------------
   * Set up Log Entity
   *--------------------------------------------------------------------------*/
  tmrMgr.tmrMgrHandle = 0;
  tmrMgr.tmrStartCallback = nwTimerStart;
  tmrMgr.tmrStopCallback = nwTimerStop;

  rc = nwGtpv2cSetTimerMgrEntity(hGtpv2cStack, &tmrMgr);
  NW_ASSERT(NW_OK == rc);

  /*---------------------------------------------------------------------------
   * Set up Log Entity
   *--------------------------------------------------------------------------*/
  logMgr.logMgrHandle   = (NwGtpv2cLogMgrHandleT) nwMiniLogMgrGetInstance();
  logMgr.logReqCallback  = nwMiniLogMgrLogRequest;

  rc = nwGtpv2cSetLogMgrEntity(hGtpv2cStack, &logMgr);
  NW_ASSERT(NW_OK == rc);

  /*---------------------------------------------------------------------------
   *  Send Message Request to Gtpv2c Stack Instance
   *--------------------------------------------------------------------------*/
  NW_LOG(NW_LOG_LEVEL_NOTI, "EGTPING %s ("NW_IPV4_ADDR")", egtPing.targetIpStr, NW_IPV4_ADDR_FORMAT(inet_addr(egtPing.targetIpStr)));
  rc = nwGtpv2cUlpPing(&ulpObj,
                       inet_addr(egtPing.targetIpStr),
                       egtPing.pingCount,
                       egtPing.pingInterval,
                       2,
                       3);
  NW_ASSERT(NW_OK == rc);

  /*---------------------------------------------------------------------------
   * Install signal handler
   *--------------------------------------------------------------------------*/
  signal(SIGINT, nwEgtPingHandleSignal);

  /*---------------------------------------------------------------------------
   * Event loop
   *--------------------------------------------------------------------------*/

  NW_EVT_LOOP();
  NW_LOG(NW_LOG_LEVEL_ERRO, "Exit from eventloop, no events to process!");

  /*---------------------------------------------------------------------------
   *  Destroy Gtpv2c Stack Instance
   *--------------------------------------------------------------------------*/
  rc = nwGtpv2cFinalize(hGtpv2cStack);

  if(rc != NW_OK) {
    NW_LOG(NW_LOG_LEVEL_ERRO, "Failed to finalize gtpv2c stack instance. Error '%u' occured", rc);
  }

  return rc;
}
