/***********************************************************************
 * 
 *  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: -l lifetime [-D DebugLevel]\n";

int DebugLevel = 3;

// Test lifetime
int   testLifetime = 60; // Seconds

bool  processingRequests = true;


//++=======================================================================
void* UnInitThread()
//
//  Arguments: 
//
//  Returns:   
//
//  Abstract:  
//
//  Notes:
//
//  Environment:
//
//=======================================================================--
{
   DbgTrace(1, "UnInitThread- Start\n", 0);

   // Sleep for the configured amount
   sleep(testLifetime);

   // Stop processing Rpc Requests
   processingRequests = false;

   // Un-init the Svc Rpc Service Subsystem
   IpcServerShutdown();
   
   DbgTrace(1, "UnInitThread- End\n", 0);

   // Exit
   pthread_exit(NULL);

   return 0;   // never-reached!
}


//++=======================================================================
void* ProcessRequestThread(int32_t requestId)
//
//  Arguments: 
//
//  Returns:   
//
//  Abstract:  
//
//  Notes:
//
//  Environment:
//
//=======================================================================--
{
   char  *pReqData = NULL;

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

   // Get the rpc data
   int dataLen = IpcServerGetRequestData(requestId, &pReqData);
   if (dataLen != 0)
   {
      // Just echo the data back as the reply
      IpcServerCompleteRequest(requestId,pReqData);
   }
   else
   {
      DbgTrace(0, "ProcessRequestThread- Error obtaining Request data\n", 0);
      IpcServerAbortRequest(requestId);
   }

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

   return 0;   // never-reached!
}


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

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

   // Initialize the Svc Ipc Subsystem
   if (IpcServerInit("TestServer",
                     DebugLevel,
                     false) == 0)
   {
      // Set the server listen address
      if (IpcServerSetInAddress(5000) == 0)
      {
         // Now start servicing requests.
         if (IpcServerStart() == 0)
         {
            // The Ipc subsystem was started, now create a thread for
            // un-initializing the Ipc subsystem after a while.
            threadCreateStatus = pthread_create(&thread,
                                                NULL,
                                                (void*(*)(void*))UnInitThread,
                                                (void*)NULL);
            if (threadCreateStatus != 0)
            {
               DbgTrace(0, "ExecuteTests- Unable to create un-initialization thread, error = %08X\n", threadCreateStatus);
               IpcServerShutdown();
            }
            else
            {
               // Process Incoming Requests
               while (processingRequests)
               {
                  int32_t requestId = IpcServerGetRequest();
                  if (requestId != 0)
                  {
                     // Create a thread to handle the request
                     threadCreateStatus = pthread_create(&thread,
                                                         NULL,
                                                         (void*(*)(void*))ProcessRequestThread,
                                                         (void*)requestId);
                     if (threadCreateStatus != 0)
                     {
                        DbgTrace(0, "ExecuteTests- Unable to create process request thread, error = %08X\n", threadCreateStatus);
                        IpcServerAbortRequest(requestId);
                     }
                  }
                  else
                  {
                     // No need to service requests any longer
                     break;
                  }
               }
            }
         }
         else
         {
            DbgTrace(0, "ExecuteTests- Error starting the Ipc subsystem\n", 0);
            IpcServerShutdown();
         }
      }
      else
      {
         DbgTrace(0, "ExecuteTests- Error setting server address\n", 0);
         IpcServerShutdown();
      }
   }
   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;

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

   // Scan through the options specified
   while (!doneScanning)
   {
      opterr = 0;
      option = getopt(argc, argv, "l: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 'l':
            // Set the test lifetime
            printf("Lifetime = %s\n", optarg);
            testLifetime = 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)
   {
      ExecuteTests();
   }
   else
   {
      // Invalid option detected
      printf(usage, argv[0]);
   }

   return 0;

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