/***********************************************************************
 * 
 *  Copyright (C) 2006 Novell, Inc. All Rights Reserved.
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; version 2.1
 *  of the License.
 *
 *  This library 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
 *  Library Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, Novell, Inc.
 * 
 *  To contact Novell about this file by physical or electronic mail, 
 *  you may find current contact information at www.novell.com.
 * 
 *  Author: Juan Carlos Luciani <jluciani@novell.com>
 *
 ***********************************************************************/

//===[ Include files ]=====================================================

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <getopt.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <pthread.h>
#include <unistd.h>
#include <stdint.h>

#include "casa_s_ipc.h"

//===[ Type definitions ]==================================================

//
// DbgTrace macro define
//
#define DbgTrace(LEVEL, X, Y) {                          \
   if (LEVEL == 0)                                       \
      printf(X, Y);                                      \
   else if (DebugLevel >= LEVEL)                         \
         printf(X, Y);                                   \
}


//===[ Function prototypes ]===============================================

//===[ Global variables ]==================================================

// Usage string
char  usage[] = "\nTest: usage: [-c requestCount] [-t submitThreadCount] [-D DebugLevel]\n";

int DebugLevel = 3;

// Request count
int   submitReqCount = 100;

// Submit thread count
int   submitThreadCount = 1;

// Synch mutex
pthread_mutex_t   testMutex;

bool errorDetected = false;


//++=======================================================================
void* SubmitThread(
   uint32_t remoteEndPointHandle)
//
//  Arguments: 
//
//  Returns:   
//
//  Abstract:  
//
//  Notes:
//
//  Environment:
//
//=======================================================================--
{
   char  reqData[] = "This is the request data";
   char  *pReplyData;
   int   replyDataLen;

   DbgTrace(1, "SubmitThread- Start\n", 0);

   pthread_mutex_lock(&testMutex);
   while (submitReqCount != 0
          && !errorDetected)
   {
      submitReqCount --;
      pthread_mutex_unlock(&testMutex);

      // Submit request to the server
      if (IpcClientSubmitReq(remoteEndPointHandle,
                             reqData,
                             strlen(reqData),
                             &pReplyData,
                             &replyDataLen) != 0)
      {
         DbgTrace(0, "SubmitThread- Req submit error\n", 0);
         errorDetected = true;
      }
      else
      {
         // Free the reply data buffer
         free(pReplyData);
      }

      pthread_mutex_lock(&testMutex);
   }

   DbgTrace(1, "SubmitThread- End\n", 0);
   
   // Exit
   pthread_exit(NULL);

   return 0;   // never-reached!
}


//++=======================================================================
void
ExecuteTests(void)
//
//  Arguments: 
//
//  Returns:   
//
//  Abstract:  
//
//  Notes:
//
//  Environment:
//
//=======================================================================--
{
   pthread_t   thread;
   int         threadCreateStatus;
   int         threadCreatedCount = 0;
   int         i;
   uint32_t    endPointHandle;

   DbgTrace(1, "ExecuteTests- Start\n", 0);

   // Initialize the Client Ipc Subsystem
   if (IpcClientInit("TestClient",
                     true,
                     DebugLevel,
                     false) == 0)
   {
      // Set the server listen address
      if (IpcClientOpenInetRemoteEndPoint(5000,
                                          0x7F000001,
                                          0,
                                          &endPointHandle) == 0)
      {
         // Start the configured number of threads to submit requests to
         // the server.
         for (i = 0; i < submitThreadCount; i++)
         {
            threadCreateStatus = pthread_create(&thread,
                                                NULL,
                                                (void*(*)(void*))SubmitThread,
                                                (void*)endPointHandle);
            if (threadCreateStatus == 0)
               threadCreatedCount ++;
            else
            {
               DbgTrace(0, "ExecuteTests- Error creating submit thread, error = %08X\n", threadCreateStatus);
            }
         }

         // Wait for the requests to be submitted
         if (threadCreatedCount != 0)
         {
            while (submitReqCount
                   && !errorDetected)
               sleep(1);
            sleep(1);
         }

         // Close the remote endpoint
         if (IpcClientCloseRemoteEndPoint(endPointHandle) != 0)
         {
            DbgTrace(0, "ExecuteTests- Error closing remote endpoint\n", 0);
         }
      }
      else
      {
         DbgTrace(0, "ExecuteTests- Error opening remote endpoint\n", 0);
      }

      IpcClientShutdown();
      sleep(1);
   }
   else
   {
      DbgTrace(0, "ExecuteTests- Ipc subsystem initialization failed\n", 0);
   }

   DbgTrace(1, "ExecuteTests- End\n", 0);
}


//++=======================================================================
int
main(
   int argc,
   char* argv[])
//
//  Arguments: 
//
//  Returns:   
//
//  Abstract:  
//
//  Notes:
//
// L2
//=======================================================================--
{
   int         optionsSpecified = 0;
   bool        doneScanning = false;
   bool        invalidOption = false;
   int         option;
   int         initialCount;

   printf("**** Ipc Client test ****\n");

   // Scan through the options specified
   while (!doneScanning)
   {
      opterr = 0;
      option = getopt(argc, argv, "c:t:D:");

      // Proceed based on the result
      switch (option)
      {
         case 'D':
            // Set the debug level
            printf("DebugLevel = %s\n", optarg);
            DebugLevel = atoi(optarg);
            optionsSpecified++;
            break;

         case 'c':
            // Set the submit request count
            submitReqCount = atoi(optarg);
            optionsSpecified++;
            break;

         case 't':
            // Set the submit thread count
            submitThreadCount = atoi(optarg);
            optionsSpecified++;
            break;

         case '?':
            // Invalid option detected
            doneScanning = true;
            invalidOption = true;
            break;

         default:
            // Done scanning
            doneScanning = true;
            break;
      }
   }

   // Do some sanity checking
   if (!invalidOption)
   {
      printf("submitReqCount = %d\n", submitReqCount);
      printf("submitThreadCount = %d\n", submitThreadCount);
      initialCount = submitReqCount;
      ExecuteTests();
      printf("Submits issued = %d\n", initialCount - submitReqCount);
   }
   else
   {
      // Invalid option detected
      printf(usage, argv[0]);
   }

   return 0;

}  /*-- main() --*/