 /***************************************************************************
 * OTG: Orbit Traffic Generator
 * Copyright (c) 2004,2005  
 * Copyright (c) 2004,2005
 *                                WINLAB, Rutgers University
 * All rights reserved.
 *
 * Permission to use, copy, modify and distribute this software and its
 * documentation is hereby granted, provided only with the following
 * conditions are satisfied:
 *
 * 1. Both the copyright notice and this permission notice appear in
 *    all copies of the software, derivative works or modified versions,
 *    and any portions thereof, and that both notices appear in
 *    supporting documentation.
 * 2. All advertising materials mentioning features or use of this
 *    software must display the following acknowledgement:
 *      This product includes software developed by Nara Institute of
 *      Science and Technology and its contributors.
 * 3. Neither the name of Wireless INformation LABoratory nor
 *    the names of its contributors may be used to endorse or promote
 *    products derived from this software without specific prior written
 *    permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPER ``AS IS'' AND WINLAB
 * DISCLAIMS ANY LIABILITY OF
 * ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF
 * THIS SOFTWARE. ALSO, THERE IS NO WARRANTY IMPLIED OR OTHERWISE,
 * NOR IS SUPPORT PROVIDED.
 *
 * Feedback of the results generated from any improvements or
 * extensions made to this software would be much appreciated.
 * Any such feedback should be sent to:
 *
 *  Zhibin Wu
 *  E-mail:  <zhibinwu@winlab.rutgers.edu>
 *  URL:     <http://www.winlab.rutgers.edu/~zhibinwu>
 *  Address: 73 Brett Rd., 
 *           WINLAB
 *           Piscataway, NJ 08854, USA
 *
 * WINLAB has the rights to redistribute these changes.
 ***************************************************************************/

#include <popt.h>
#include <sys/time.h>
#include <iostream>
using namespace std;
#include "stream.h"
#include "address.h"
#include "cbr_generator.h"
#include "expo_generator.h"
#include "sockport.h"
#include "udpsock_port.h"
#include "tcpsock_port.h"
#include "etherport.h"
#include "orbitapp.h"
#include "otg_application.h"
//#include "oml_orbit_winlab_otg.h" 

#include <pthread.h>

#define STDIN 0
#define MAX_INPUT_SIZE 256


struct poptOption options_phase1[] = {
  POPT_AUTOHELP
  { "protocol", '\0', POPT_ARG_STRING, NULL, 0, "Name of protocol", "[tcp|udp|raw_packet]"},
  { "generator", '\0', POPT_ARG_STRING, NULL, 0, "Name of generator", "[cbr|expoo]"},
  { "debuglog", '\0',  POPT_ARG_STRING, NULL, 0, "Filename of Measurement Log", "[filename]"},
  { "clockref", '\0',  POPT_ARG_INT, NULL, 0, "hours since 01/01/1970", "hours"},
  { NULL, 0, 0, NULL, 0 }
};


/**
 * Function to create a Generator based on the generator_name
 * @param generator_name: generator name
 * @return the generator created
 */

Generator*
createGenerator(
  char* generator_name
) {
  Generator* gen = NULL;   

  if (strcmp(generator_name, "cbr") == 0 ){
    gen = new CBR_Generator();
  }else if(strcmp(generator_name, "expoo") == 0){
    gen = new Expo_Generator();
  }else {
    cerr << "Error: Unknown Generator '" << generator_name << "'." << endl;
    exit(-1);
  } 
  return gen;
}


struct poptOption options[] = {
  POPT_AUTOHELP
  { NULL, '\0', POPT_ARG_INCLUDE_TABLE, NULL, 0, "Port" },
  { NULL, '\0', POPT_ARG_INCLUDE_TABLE, NULL, 0, "Generator" },
  { "protocol", '\0', POPT_ARG_STRING, NULL, 0, "Name of protocol"},
  { "generator", '\0', POPT_ARG_STRING, NULL, 0, "Name of generator"},
  { "debuglog", '\0', POPT_ARG_STRING, NULL, 0, "Filename of Measurements Log"},
  { "clockref", '\0',  POPT_ARG_INT, NULL, 0, "hours since 01/01/1970"},
  { "exit", '\0', POPT_ARG_NONE, NULL, 1, "Stop the generator and exit" },
  { "pause", '\0', POPT_ARG_NONE, NULL, 2, "pause the generator and exit" },
  { "resume", '\0', POPT_ARG_NONE, NULL, 3, "resume the generator" },
  { NULL, 0, 0, NULL, 0 }
};


/**
 *  Parse options for phase 1: Protocol and Generator Type
 *  @param argc:
 *  @param argv:
 *  @param protocol_name: protocol type
 *  @param gt: generator type:
 */
void parseOptionsPhase1(
int argc, 
const char * argv[], 
char** protocol_name, 
char** generator_name, 
char** log_name,
int* clockref 
){ 
 
  options_phase1[1].arg = protocol_name;
  options_phase1[2].arg = generator_name;
  options_phase1[3].arg = log_name;
  options_phase1[4].arg = clockref; 
  int rc;
  poptContext optCon = poptGetContext(NULL, argc, argv, options_phase1, 0);
  while ((rc = poptGetNextOpt(optCon)) != -1); 	
  /*
  if (*protocol_name == NULL ){
    cerr << "Error: Missing protocol option!" << endl;
    poptPrintUsage(optCon, stderr, 0);
    exit(-1);
  }
  if (*generator_name == NULL ){
    cerr << "Error: Missing Generator option!" << endl;
    poptPrintUsage(optCon, stderr, 0);
    exit(-1);
  }
  */
  //options_phase1[1].arg = NULL;
  //options_phase1[2].arg = NULL;
  //options_phase1[3].arg = NULL;
  //options_phase1[4].arg = NULL;
  poptFreeContext(optCon);
}


/**
 *  Parse options in second Phase.
 *  Check all port and generator parameters except "type" which has already been parsed.
 *  in Phase I.
 */
void
parseOptionsPhase2(
  int argc, 
  const char * argv[],  
  Port* port, 
  Generator* gen
) {

  options[1].arg = (void*)port->getOptions();
  options[2].arg = (void*)gen->getOptions();
  options[3].argDescrip = "FIXED";
  options[4].argDescrip = "FIXED";
  options[5].argDescrip = "FIXED";
  options[6].argDescrip = "FIXED";

  int rc;
  poptContext optCon = poptGetContext(NULL, argc, argv, options,0 );
  while ((rc = poptGetNextOpt(optCon)) >= 0);
  if (rc < -1) {
    cerr << "ERROR: " << poptBadOption(optCon, POPT_BADOPTION_NOALIAS)
	 << " (" << poptStrerror(rc) << ")" << endl;
    goto err;
  }
  {
    const char** leftover = poptGetArgs(optCon);
    if (leftover != NULL) {
      cerr << "ERROR: Additional argument '" << leftover[0] << "' found." << endl;
      goto err;
    }
  }
  poptFreeContext(optCon);
  return;

 err:
  poptPrintUsage(optCon, stderr, 0);
  exit(-1);
}

/**
 * Parse Runtime Options
 */

int parseRuntimeOptions(char * msg, Stream* str)
{
  int argc;
  const char** argv;
  char strin[MAX_INPUT_SIZE];

  if (*msg == '\0') return -1;
  if (*msg != '-') {
    // allow for commands without leading --
    strcpy(strin + 2, msg);
    strin[0] = strin[1] = '-';
    msg = strin;
  }
  poptParseArgvString(msg, &argc, &argv);
  poptContext optCon = poptGetContext(NULL, argc, argv, options, POPT_CONTEXT_KEEP_FIRST);
	
  int rc;
  while ((rc = poptGetNextOpt(optCon)) > 0) {

        if (rc == 1) {// Stop
	  str->exitStream();
          exit(0);  //exit terminate process and all its threads
        }
        else if (rc == 2)
	{ 
          str->pauseStream();         
	}
	else if (rc==3)
	{
          str->resumeStream();
	}     
  };
  if (rc < -1) {
    cerr << "ERROR: " << poptBadOption(optCon, POPT_BADOPTION_NOALIAS)
	 << " (" << poptStrerror(rc) << ")" << endl;
  }
  poptFreeContext(optCon);
  return rc;	
}

/**
 * Function to create a Port based on the protocol_name
 * @param protocol_name: protocol name
 * @return the port created
 */

Port*
createPort(
  char* protocol_name
) {
  Port* port = NULL;   

  if (strcmp(protocol_name, "udp") == 0 ){
    port = new UDPSockPort(); 
  } else if(strcmp(protocol_name, "tcp" ) == 0){
    port = new TCPSockPort();
  } else if(strcmp(protocol_name, "raw" ) == 0){
    port = new EthernetPort();
  } else {
    cerr << "Error: Unknown protocol '" << protocol_name << "'." << endl;
    exit(-1);
  } 
  return port;
}


/**
 * Function to creat a thread for one stream
 */
void *create_stream_function( void *ptr )
{
     Stream *para = (Stream *) ptr;
     para->startStream();  // start the stream, it cannot paused or resume unless killed by main thread
     return NULL;
}


/**
 * Fucntion in main thread to handling stdin (User input commands)
 * Parsing them as run-time options
 */

void work(Stream *str)
{
  int rc;
  char msg[MAX_INPUT_SIZE];
  while (1) {
 
        cin.getline(msg, MAX_INPUT_SIZE );
        rc = parseRuntimeOptions(msg, str);    
  }
}


/**
 *  Main funciton of the OTG
 *
 * The major purpose of this function are:
 * - handle command-line inputs, before starting the program and at run-time. 
 * - arrange wait, send, and stdin reading operations.
 *
 * It is designed as: First, parse all options in two phases.
 * After parsing the second level options, check the readiness of Port and Generator (if this is sender)
 * then initialize the port and set-up the scenario, and ready to start.

 * Here. we are going to have multiple streams in one OTG.  
 * using multiple thread. the main thread is handling commands.
 * each stream is a seperate thread, by calling th stream_init functon, the stream is independently sending packets 
 * 
 */
int main(int argc, const char * argv[])
{

  OrbitApp *otg1 = new OTGApp();   

  try {
    
    pthread_t thread1;
    char *protocol_name = "udp";
    char *generator_name = "cbr";
    char *debuglog_name = NULL;
    int clockref = -1;
    parseOptionsPhase1(argc, argv, &protocol_name, &generator_name, &debuglog_name, &clockref);
    Port* port = createPort(protocol_name);
    Generator *gen = createGenerator(generator_name);
    otg1->init(&argc, argv, debuglog_name);  //Initialize OML  
    parseOptionsPhase2(argc, argv, port, gen);                
    port->init();
    gen->init();
    Stream *str = new Stream(0, gen, port, otg1, clockref);  //default stream No.0    
    int cst1 = pthread_create( &thread1, NULL, create_stream_function, (void *)str);   
    if (cst1!=0)throw "Create a Stream Thread Failed...";               
    work(str); //this is the main thread

  } catch (const char *reason ) {
    cerr << "Exception:" << reason << endl;
    exit(-1);
  }  
  return 0;
}

//Note: I have tested that there is no issue to start more than one streams in the OTG with multiple threads,
//but I prefer not to use this feature now because it is of no need.
//
//struct poptOption options_runtime_phase1[] = {
//  POPT_AUTOHELP
//  { "stream", '\0', POPT_ARG_INT, NULL, 0, "ID of stream", "INTEGER"},
//  { NULL, 0, 0, NULL, 0 }
//};

/**
 *An struct
 *Parameter for create a new stream thread
 */

//struct stream_thread_param
//{
//  Generator *gp_; /*!<pointer to the generator */
//  Port      *pp_; /*!<pointer to port */   
//  short streamid_;/*!stream identifier*/
//};

/** An enum.
 * Indication of generator type
 */
//enum generator_type 
//{ 
//  cbr,         /*!< Enum value CBR traffic. */  
//  exponential  /*!< Enum value Exponential Traffic. */    
//};


  /*
    struct stream_thread_param sp1;
   
    sp1.gp_ = gen;
    sp1.pp_ = port; 
    sp2.gp_ = gen2;
    */
    //sp2.pp_ = port2;
    //sp1.streamid_= 1;
    //sp2.streamid_= 2;    
    // two threads, create two streams   


    //generator_type gtype = cbr;
    //Generator* gen = new CBR_Generator();
    //Generator* gen2 = new CBR_Generator();  //gen2 default options, does not need parse anycommandline options  
     //port2->setDstHostname("internal1");
    //port2->init(); 

    //Port* port2 = createPort(protocol_name); // port2 default options, does not need parse any commandline options 

 //int cst2 = pthread_create( &thread2, NULL, create_stream_function, (void *)&sp2);
