//
// Copyright (C) 2005 Pingtel Corp.
// 
//
// $$
////////////////////////////////////////////////////////////////////////

// System includes
#include <getopt.h>

#include "os/OsDefs.h"
#if defined(_WIN32)
#   include <windows.h>
#elif defined(__pingtel_on_posix__)
#   include <unistd.h>
#endif

// APPLICATION INCLUDES
#include <utl/UtlInt.h>
#include <utl/UtlHashMapIterator.h>
#include <net/XmlRpcRequest.h>
#include <os/OsSysLog.h>
#include <os/OsTask.h>
#include <os/OsEvent.h>
#include <net/HttpConnectionMap.h>

// CONSTANTS
#define MEMCHECK_DELAY 45
#define HTTP_PORT               8200    // Default HTTP port

char InputFile[128];
void fileExecute(const char *, bool);

// Define a client task for multi-threaded requests /////////////////////////////////////
class ClientTask : public OsTask
{
public:    
    ClientTask(void* pArg);
    virtual int run(void* runArg);
};

ClientTask::ClientTask(void* pArg) : OsTask("xmlClientTask-%d", pArg)
{
}

int ClientTask::run(void* runArg)
{
    OsEvent* pEvent = (OsEvent*)runArg;
    OsStatus status;
    
    fileExecute(InputFile, false);
    
    do {
        //printf("%s is signaling\n", mName.data());
        status = pEvent->signal(1);
    } 
    while (status == OS_ALREADY_SIGNALED);
   
    return 0;
}
///////////////////////////////////////////////////////////////////////////////////////////
int HttpPort = HTTP_PORT;
enum Verbosity
{
   Quiet,
   Normal,
   Verbose
} Feedback = Quiet;

const char* LogFile = "xmlrpcClient.log";
const char* xmlrpcURI;
int MemCheckDelay = 0;

void initLogger(char* argv[])
{
   OsSysLog::initialize(0, // do not cache any log messages in memory
                        argv[0]); // name for messages from this program
   OsSysLog::setOutputFile(0, // no cache period
                           LogFile); // log file name
   switch (Feedback)
   {
   case Quiet:
      OsSysLog::setLoggingPriority(PRI_WARNING);
      break;
   case Normal:
      OsSysLog::setLoggingPriority(PRI_INFO);
      break;
   case Verbose:
      OsSysLog::setLoggingPriority(PRI_DEBUG);
      break;
   }
}

void showHelp(char* argv[])
{
   printf("Usage: \n"
          "   %s:\n"
          "   [ {-v|--verbose} | {-q|--quiet} ]\n"
          "   [ {-h|-?|--help} ]\n"
          "   [ --version <xmlrpc URI> <dataset> ]\n"
          "   [ {-g|--get} <xmlrpc URI> <dataset> <name> ... ]\n"
          "   [ {-s|--set} <xmlrpc URI> <dataset> <name> <value> [ <name> <value> ] ... ]\n"
          "   [ {-d|--delete} <xmlrpc URI> <dataset> <name> ... ]\n"
          "   [ {-f|--file} <file name> ]\n"
          "   [ {-t|--threads} <number> ]\n"
          , argv[0]
          );
}

typedef enum
{
   Version,         ///< configurationParameter.version
   Get,             ///< configurationParameter.get
   Set,             ///< configurationParameter.set
   Delete           ///< configurationParameter.delete
} MethodType;

MethodType Method;
UtlString  DataSet;
bool bInputFile = false;
bool bSingleStep = false;
bool bRepeatFile = false;
bool bThreads = false;
int numThreads = 1;

void parseArgs(int argc, char* argv[])
{
   int optResult = 0;
   int temp = 0;
   
   const char* short_options = "p:l:f:t:vqmhgsr";
   
   const struct option long_options[] =
      {
         {"verbose", no_argument, NULL, 'v'},
         {"quiet",   no_argument, NULL, 'q'},
         {"memcheck",no_argument, &MemCheckDelay, MEMCHECK_DELAY},
         {"help",    no_argument, NULL, 'h'},
         {"port",    required_argument, NULL, 'p'},
         {"log",     required_argument, NULL, 'l'},
         {"file",    required_argument, NULL, 'f'},
         {"threads", required_argument, NULL, 't'},
         {"step",    no_argument, NULL, 's'},
         {"repeat",  no_argument, NULL, 'r'},
         {"version", no_argument, (int*)&Method, Version },
         {"get",     no_argument, (int*)&Method, Get },
         {"set",     no_argument, (int*)&Method, Set },
         {"delete",  no_argument, (int*)&Method, Delete },
         {0, 0, 0, 0}
      };

   while (  (optResult = getopt_long (argc, argv, short_options, long_options, NULL))
          >= 0
          )
   {
      char* optend;

      switch (optResult)
      {
      case 'p':
         HttpPort = strtoul(optarg, &optend, 10);
         if ( '\0' != *optend )
         {
            fprintf( stderr, "Invalid HTTP port %s\n", optarg );
            exit(1);
         }
         break;

      case 'l':
         LogFile = optarg;
         break;
         
      case 'f':
         strncpy(InputFile, (char*)optarg, 128);
         bInputFile = true;
         break;
         
      case 't':
         temp = atoi((char*)optarg);
         numThreads = (temp > 0) ? temp : 1;
         break;
         
      case 's':
      {
         bSingleStep = true;
         break;
      }
         
      case 'r':
         bRepeatFile = true;
         break;

      case 'v':
         Feedback = Verbose;
         break;

      case 'q':
         Feedback = Quiet;
         break;

      case 'h':
      case '?':
         showHelp(argv);
         exit(0);
         break;

      default:
         break;
      }
   }

   if (optind < argc)
   {
      xmlrpcURI = argv[optind++];
   }

   if (optind < argc)
   {
      DataSet = argv[optind++];
   }
}

void exitFault(XmlRpcResponse& response)
{
   UtlString reason;
   int code;
   response.getFault(&code,reason);
   fprintf(stderr, "XML-RPC Fault %d: %s\n", code, reason.data() );
   //exit(1);
}

void requestVersion(Url& url)
{
    XmlRpcRequest* request;
    XmlRpcResponse response;
    
    request = new XmlRpcRequest(url, "configurationParameter.version");
    request->addParam(&DataSet);    
    
    if (!request->execute(response/*, &pSocket*/))
    {
        exitFault(response);
    }
    else
    {
        UtlContainable* value;
        if (response.getResponse(value))
        {
            UtlString* versionId = dynamic_cast<UtlString*>(value);
            if (versionId)
            {
                printf("%s\n", versionId->data());
            }
            else
            {
                fprintf(stderr, "Incorrect type returned.\n");
                exit(1);
            }
        }
        else
        {
            fprintf(stderr, "No value returned.\n");
        }
    }    
    delete request;
    request = NULL;
}

void requestGet(Url& url, UtlSList& names)
{
    XmlRpcRequest* request;
    XmlRpcResponse response;
    
    request = new XmlRpcRequest(url, "configurationParameter.get");
    request->addParam(&DataSet);        

    if (!names.isEmpty())
    {
        request->addParam(&names);
    }
      
    if (!request->execute(response/*, &pSocket*/))
    {
        exitFault(response);
    }
    else
    {
        UtlContainable* value;
        if (response.getResponse(value))
        {
            UtlHashMap* paramList = dynamic_cast<UtlHashMap*>(value);
            if (paramList)
            {
                UtlHashMapIterator params(*paramList);
                UtlString* name;
                while ((name = dynamic_cast<UtlString*>(params())))
                {
                    UtlString* value = dynamic_cast<UtlString*>(paramList->findValue(name));
                    printf("%s : %s\n", name->data(), value->data());
                }
            }
            else
            {
                fprintf(stderr, "Incorrect type returned.\n");
                exit(1);
            }
        }
        else
        {
            fprintf(stderr, "No value returned.\n");
            exit(1);
        }
    }    
    delete request;
    request = NULL;
}

void requestSet(Url& url, UtlHashMap& parameters)
{
    XmlRpcRequest* request;
    XmlRpcResponse response;
    
    request = new XmlRpcRequest(url, "configurationParameter.set");
    request->addParam(&DataSet); 
    request->addParam(&parameters);
     
    if (request->execute(response /*, &pSocket*/))
    {
        UtlContainable* value;
        if (response.getResponse(value))
        {
            UtlInt* numberSet = dynamic_cast<UtlInt*>(value);
            if (numberSet)
            {
                if (Verbose == Feedback)
                {
                    printf("set %d name/value pairs.\n", numberSet->getValue());
                }
            }
            else
            {
                fprintf(stderr, "Incorrect type returned.\n");
                exit(1);
            }
        }
        else
        {
            fprintf(stderr, "No value returned.\n");
            exit(1);
         }
    }
    else
    {
        exitFault(response);
    }
    delete request;
    request = NULL;
}

void requestDelete(Url& url, UtlSList& names)
{
    XmlRpcRequest* request;
    XmlRpcResponse response;
    
    request = new XmlRpcRequest(url, "configurationParameter.delete");
    request->addParam(&DataSet); 
        
    if (!names.isEmpty())
    {
        request->addParam(&names);
    }
      
    if (!request->execute(response/*, &pSocket*/))
    {
        exitFault(response);
    }
    else
    {
        UtlContainable* value;
        if (response.getResponse(value))
        {
            UtlInt* deletedCount = dynamic_cast<UtlInt*>(value);
            if (deletedCount)
            {
                if (Verbose == Feedback)
                {
                   printf("deleted %d parameters.\n", deletedCount->getValue());
                }
            }
            else
            {
                fprintf(stderr, "Incorrect type returned.\n");
                exit(1);
            }
        }
        else
        {
            fprintf(stderr, "No value returned.\n");
            exit(1);
        }
    }    
    delete request;
    request = NULL;
}

int main(int argc, char* argv[])
{
   parseArgs(argc, argv);
   initLogger(argv);
   OsEvent taskDone;

   Url url(xmlrpcURI);
    
   if (MemCheckDelay)
   {
      // Delay 45 seconds to allow memcheck start
      printf("Wating %d seconds for start of memcheck ...", MemCheckDelay);
      OsTask::delay(MemCheckDelay * 1000);
      printf("starting\n");
   }
   
   // If an input file was specified we start up the number
   // of specified threads to execute that input file. If number
   // of threads wasn't specified we start up 1 thread.
   if (bInputFile)
   {
      int signaled = 0;
      
      for (int i=0; i<numThreads; i++)
      {
         ClientTask* pTask = new ClientTask(&taskDone);
         pTask->start();
      }

      // Wait for threads to shut down
      while (signaled < numThreads)
      {
         taskDone.wait();
         taskDone.reset();
         ++signaled;
      }
      exit(0);
   }

   switch (Method)
   {
   case Version: // --version <xmlrpc URI> <dataset>
   {
      if (optind < argc)
      {
         fprintf(stderr, "Too many arguments: '%s'\n", argv[optind]);
         showHelp(argv);
         exit(1);
      }

      requestVersion(url);

      break;
   }
   case Get: // --get <xmlrpc URI> <dataset> <name> ...
   {
      UtlSList names;
      // copy remaining arguments into the names list
      while (optind < argc)
      {
         names.append(new UtlString(argv[optind++]));
      }
      
      requestGet(url, names);

      break;
   }
   case Set: // --set <xmlrpc URI> <dataset> <name> <value> [ <name> <value> ] ... 
   {
      UtlHashMap parameters;
      // copy remaining arguments into the names list
      while (optind + 1 < argc)
      {
         UtlString* setName = new UtlString(argv[optind++]);
         UtlString* setValue = new UtlString(argv[optind++]);
         parameters.insertKeyAndValue(setName, setValue);
      }
      if (optind < argc)
      {
         fprintf(stderr, "name '%s' without a value\n", argv[optind]);
         showHelp(argv);
         exit(1);
      }
      
      if (parameters.isEmpty())
      {
         fprintf(stderr, "must specify at least one name and value\n");
         showHelp(argv);
         exit(1);
      }
      else
      {
        requestSet(url, parameters);
        parameters.destroyAll();
      }

      break;
   }
   case Delete: // --delete <xmlrpc URI> <dataset> <name> ... 
   {
      UtlSList names;
      // copy remaining arguments into the names list
      while (optind < argc)
      {
         names.append(new UtlString(argv[optind++]));
      }
      
      requestDelete(url, names);

      break;
   }
   default:
      fprintf(stderr, "No method specified\n");
      showHelp(argv);
      exit(1);
   }
   
   if (MemCheckDelay)
   {
      // Delay 45 seconds to allow memcheck start
      printf("Wating %d seconds for stop of memcheck ...", MemCheckDelay);
      OsTask::delay(MemCheckDelay * 1000);
      printf("starting\n");
   }

   exit(0);
      
}

void fileError(int error, int line)
{
    switch (error)
    {
        case 1:
            fprintf(stderr, "Expected URI in line %d - ignoring line\n", line);
            break;
        case 2:
            fprintf(stderr, "Expected data set in line %d - ignoring line\n", line);
            break;
        case 3:
            fprintf(stderr, "Name without a value specified in line %d - ignoring line\n", line);
            break;
        default:
            fprintf(stderr, "Unknown error encountered in line %d - ignoring line\n", line);
            break;
    }
}

void fileExecute(const char* inputFile, bool bSingleStep)
{
    FILE *fp;
    char szBuffer[128];
    char* token;
    int line = 0;
    
    if ((fp=fopen(inputFile, "r")) != NULL)
    {
        do {
            rewind(fp);
            while (fgets(szBuffer, 128, fp) != NULL)
            {
                ++line;
                if (szBuffer[0] != 0)
                {
                    printf("Executing %s", szBuffer);                
                }
                token = strtok(szBuffer, " ");
                if (token == NULL)
                {
                    break;
                }
                if (strcasecmp(token, "version") == 0)
                {
                    token = strtok(NULL, " ");
                    if (token == NULL)
                    {
                        fileError(1, line);
                    } 
                    else
                    {
                        Url url(token);                
                        token = strtok(NULL, " ");
                        if (token == NULL)
                        {
                            fileError(2, line);
                        }
                        else
                        {            
                            DataSet = token;
                    
                            requestVersion(url);
                        }
                    }
                }
                else if (strcasecmp(token, "get") == 0)
                {
                    token = strtok(NULL, " ");
                    if (token == NULL)
                    {
                        fileError(1, line);
                    }
                    else
                    {
                        Url url(token);                
                        token = strtok(NULL, " ");
                        if (token == NULL)
                        {
                            fileError(2, line);
                        }               
                        else
                        {
                            DataSet = token;
                            UtlSList names;
                            while (token != NULL)
                            {
                                token = strtok(NULL, " ");
                                if (token != NULL)
                                {
                                    names.append(new UtlString(token));
                                }
                            }
                            requestGet(url, names);
                            names.destroyAll();
                        }
                    }
                }
                else if (strcasecmp(token, "set") == 0)
                {
                    token = strtok(NULL, " ");
                    if (token == NULL)
                    {
                        fileError(1, line);
                    }
                    else
                    {
                        Url url(token);                
                        token = strtok(NULL, " ");
                        if (token == NULL)
                        {
                            fileError(2, line);
                        }               
                        else
                        {
                            DataSet = token;
                            UtlHashMap parameters;
                            char *key;
                            char *value;
                            while (token != NULL)
                            {
                                key = strtok(NULL, " ");
                                if (key == NULL)
                                {
                                    break;
                                }
                                value = strtok(NULL, " ");
                                if (value == NULL)
                                {
                                    fileError(3, line);
                                    break;
                                }
                                parameters.insertKeyAndValue(new UtlString(key), new UtlString(value));
                            }
                            int entries = parameters.entries();
                    
                            if (entries != 0 || (entries%2) == 0)
                            {
                                requestSet(url, parameters);
                                parameters.destroyAll();
                            }
                        }
                    }
                }
                else if (strcasecmp(token, "delete") == 0)
                {
                    token = strtok(NULL, " ");
                    if (token == NULL)
                    {
                        fileError(1, line);
                    }
                    else
                    {
                        Url url(token);                
                        token = strtok(NULL, " ");
                        if (token == NULL)
                        {
                            fileError(2, line);
                        }
                        else
                        {
                            DataSet = token;
                            UtlSList names;
                            while (token != NULL)
                            {
                                token = strtok(NULL, " ");
                                if (token != NULL)
                                {
                                    names.append(new UtlString(token));
                                }
                            }
                            requestDelete(url, names);
                            names.destroyAll();                             
                        }
                    }
                }
                else
                {
                    fprintf(stderr, "Unknown RPC request %s - ignoring line\n", token);
                }
                if (bSingleStep)
                {
                    getchar();
                }
            }
        }
        while ( bRepeatFile );
        fclose(fp);
    }
    else
    {
        fprintf(stderr, "Can't open %s\n", inputFile);
        exit(1);
    }
}

// Stub to avoid pulling in ps library
int JNI_LightButton(long)
{
   return 0;
}



syntax highlighted by Code2HTML, v. 0.9.1