/*
 * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
 * This software is the proprietary information of Sun Microsystems, Inc.
 * Use is subject to license terms.
 * 
 * Copyright 2004 Sun Microsystems, Inc.  Tous droits réservés.
 * Ce logiciel est propriété de Sun Microsystems, Inc.
 * Distribué par des licences qui en restreignent l'utilisation.
 *
 * ident        "@(#)libmfTransaction.c 1.23     04/10/12 SMI"
 *
 */

#include <pthread.h>
#include "nspr.h"
#include "string.h"

#include "mfTransaction.h"
#include "mfTransactionPrivate.h"
#include "mfTimers.h"


/***************** globals ******************* */

int global_num_txn_types = 0;
pthread_mutex_t global_mutex_num_txn_types;
transaction_type_info_t global_txn_types_array[MF_MAX_NUM_TXN_TYPES];
int *global_is_monitoring_enabledp = NULL; 


/* Pool of misc transactions: to be used when the caller
 * does not specify a trans_id in start()
 */
#define INITIAL_NUM_MISC_TXN_TYPES 1000
onetxn_list_t global_onetxn_misc_freelist;

 

/***************** define init function for this library *************/

static void mf_instrum_init();

#if defined(SunOS)
   #pragma init (mf_instrum_init)
#endif

#if defined(Linux)
 __attribute__ ((constructor)) void init() {
    mf_instrum_init();
}
#endif


/***************** static prototypes ********/

static void txn_init_txn_type(unsigned short index,
					   const char *transaction_type_name,
					   const char *uri,
					   const mfTransactionMetrics_t *metricsp );
static void txn_init_one_txn( transaction_type_info_t *txn_typep,
							   one_transaction_info_t *one_txnp );
static void txn_alloc_onetxns_list(  transaction_type_info_t *txntype_infop,
									 onetxn_list_t *txntype_freelistp,
									 int num );
static one_transaction_info_t *txn_get_onetxn( unsigned short trans_id );
static one_transaction_info_t *txn_dequeue_onetxn(
									transaction_type_info_t *txn_typep,
									onetxn_list_t *txntype_freelistp );
static void txn_enqueue_onetxn(onetxn_list_t *onetxn_freelistp,
                               one_transaction_info_t *onetxnp);
static void txn_update_responsetime_needs_lock(
                            mfTransactionMetrics_t *metricsp,
                            unsigned long response_time_ms);
static void txn_update_servicetime_needs_lock(
                            mfTransactionMetrics_t *metricsp,
                            unsigned long service_time_ms);
static int is_monitoring_enabled( transaction_type_info_t *txn_typep );

/***************** init function for this library **************/
/*
 * init function for this library:
 * defined via the "#pragma init" directive.
 *
 * This is called at load time so we are guaranteed it is called precisely
 * once.
*/
static void mf_instrum_init() {

  global_num_txn_types = 0;

  DEBUG_PRINTF(MF_UNDEFINED_TRAN_ID, "-->mf_instrum_init\n", 0,0,0,0);

#if defined(DEBUG_BUILD)
 /* init a mutex to help keep the dumping
  * of some stats together--strictly debug only.
  */
  pthread_mutex_init(&global_mutex_txn_display, NULL);
#endif

  pthread_mutex_init(&global_mutex_num_txn_types, NULL);
  bzero(&global_txn_types_array[0],
		sizeof(transaction_type_info_t) * MF_MAX_NUM_TXN_TYPES);

  txn_alloc_onetxns_list( NULL /* no transaction type */,
				   &(global_onetxn_misc_freelist),
				   INITIAL_NUM_MISC_TXN_TYPES);
 
  /* Dump the pre alloced txn misc list
   * ok not to take lock here as it's the init routine.
   *
   * txn_display_txns_needs_lock(global_onetxn_misc_freelist.otl_listp);
  */

  DEBUG_PRINTF(MF_UNDEFINED_TRAN_ID,
               LIBNAME": instrumentation library successfully initialized\n",
			 0,0,0,0);
  DEBUG_PRINTF(MF_UNDEFINED_TRAN_ID,
               "<--mf_instrum_init\n", 0,0,0,0);
}

/**************** Implementation of exported functions *******/

int mfRegisterTransaction(
					  /*[in]*/ const char *transaction_type_name,
				      /*[in]*/ const char *uri,
				      /*[in]*/ const mfTransactionMetrics_t *trans_metricsp,
				      /*[out]*/ unsigned short *tran_id){
  
  int index;

  DEBUG_PRINTF(MF_UNDEFINED_TRAN_ID,
               "-->mfRegisterTransaction\n",0,0,0,0);
  
  if (strlen(transaction_type_name) > MF_MAX_TXN_NAME_LEN ) {
	 DEBUG_PRINTF(MF_UNDEFINED_TRAN_ID,
                  "Transaction type name too long, Max allowed is %d\n",
				   MF_MAX_TXN_NAME_LEN, 0, 0, 0);
	 DEBUG_PRINTF(MF_UNDEFINED_TRAN_ID,
                  "<--mfRegisterTransaction\n",
                  0, 0, 0, 0);
	*tran_id = MF_UNDEFINED_TRAN_ID;
	return(MF_INSTRUM_ERR_TXN_TYPE_NAME_TOO_LONG);
  }
  
   if ( trans_metricsp == NULL ) {
	    DEBUG_PRINTF(MF_UNDEFINED_TRAN_ID,
                     "trans_metrics parameter must be allocated\n",
					 MF_MAX_TXN_NAME_LEN, 0, 0, 0);	
	    DEBUG_PRINTF(MF_UNDEFINED_TRAN_ID,
                     "<--mfRegisterTransaction (%s)\n",
                     0, 0, 0, 0);
	 *tran_id = MF_UNDEFINED_TRAN_ID;
	 return(MF_INSTRUM_ERR_TXN_TYPE_NAME_TOO_LONG);
  }

  /* increment transaction type */
  pthread_mutex_lock(&global_mutex_num_txn_types);
  index = global_num_txn_types;

  if ( index == MF_MAX_NUM_TXN_TYPES ) {
	DEBUG_PRINTF(MF_UNDEFINED_TRAN_ID,
                 "Max num txn types reached, Max allowed is %d\n",
				 MF_MAX_NUM_TXN_TYPES, 0, 0,0);
	DEBUG_PRINTF(MF_UNDEFINED_TRAN_ID,
                 "<--mfRegisterTransaction\n",0, 0, 0 ,0);
    *tran_id = MF_UNDEFINED_TRAN_ID;
     pthread_mutex_unlock(&global_mutex_num_txn_types);
	return(MF_INSTRUM_ERR_MAX_NUM_TXN_TYPES_REACHED);
  }
  
  /*
   * XXX Should check for duplicate uris or transaction names
   */

  global_num_txn_types++;
   
  
  /* initialize the txn type structure */
  txn_init_txn_type(index, transaction_type_name, uri, trans_metricsp);
  
  /* return the txn handle */
  *tran_id = index; 

#if defined(DEBUG_BUILD)
  txn_display_txn_type(&global_txn_types_array[index]);
#endif

 pthread_mutex_unlock(&global_mutex_num_txn_types);

  DEBUG_PRINTF(*tran_id,
               "<--mfRegisterTransaction\n", 0, 0, 0, 0);


  return(MF_INSTRUM_SUCCESS);

} 

int mfReportTransaction(/*[in]*/ const unsigned short trans_id, 
				    /*[in]*/ const int tran_status, 
				    /*[in]*/ unsigned long respTime_ms,
				    /*[in]*/ unsigned long servTime_ms,
				    /*[in]*/ long long stopTime){
  
  transaction_type_info_t *txn_typep = NULL;
  
  DEBUG_PRINTF(trans_id,
               "-->mfReportTransaction\n",0,0,0,0);
  

  if ( !(trans_id <= MF_MAX_NUM_TXN_TYPES) ||
	(trans_id == MF_UNDEFINED_TRAN_ID) ) {

	DEBUG_PRINTF(trans_id,"Invalid trans_id: must be in the range 0 to %d\n",
				 MF_MAX_NUM_TXN_TYPES-1,0,0,0);
	return(MF_INSTRUM_ERR_INVALID_TRANS_ID);
  }
 
  /* Pick up the corresponding txn type */
  txn_typep =  &global_txn_types_array[trans_id];

  if ( !is_monitoring_enabled(txn_typep) ) {
      DEBUG_PRINTF(trans_id,"Monitoring is Disabled\n",0,0,0,0);
      return(MF_INSTRUM_ERR_CALL_NOT_ALLOWED_IN_THIS_STATE);
  }
  
  /* One assumes this report implies a start and a stop. */
  pthread_mutex_lock(&txn_typep->txntype_mutex_metrics);
  
  txn_typep->txntype_metricsp->NbInRequests++;
  txn_typep->txntype_metricsp->NbOutRequests++;
 
  txn_update_responsetime_needs_lock(txn_typep->txntype_metricsp,
                                     respTime_ms);
  txn_update_servicetime_needs_lock(txn_typep->txntype_metricsp,
                                     servTime_ms);

  pthread_mutex_unlock(&txn_typep->txntype_mutex_metrics);

  DEBUG_PRINTF(trans_id, "Response time(ms): %lu\n",
               respTime_ms,0,0,0);
 DEBUG_PRINTF(trans_id, "Service time(ms): %lu\n",
               servTime_ms,0,0,0);

#if defined(DEBUG_BUILD)  
  /* This will take the txntype_mutex_metrics mutex so
   * must call this having freed that mutex.
   */
  txn_display_txn_type(txn_typep);
#endif
  DEBUG_PRINTF(trans_id,
               "<--mfReportTransaction\n", 0, 0, 0, 0);

  return(MF_INSTRUM_SUCCESS);
}

int mfStartTransaction(/*[in]*/ const unsigned short trans_id,
				   /*[in] */  handle_t *parent_handle,
				   /*[out]*/ handle_t *trans_handle){

  transaction_type_info_t *txn_typep = NULL;
  one_transaction_info_t *new_onetxnp = NULL;
  int rc = MF_INSTRUM_SUCCESS;

  DEBUG_PRINTF(trans_id,
               "-->mfStartTransaction\n",0,0,0,0);

   if ( !(trans_id <= MF_MAX_NUM_TXN_TYPES) ||
	   (trans_id ==  MF_UNDEFINED_TRAN_ID)) {

	DEBUG_PRINTF(trans_id,"Invalid trans_id: must be in the range 0 to %d\n",
				 MF_MAX_NUM_TXN_TYPES-1,0,0,0);
	*trans_handle = (handle_t)NULL;
	return(MF_INSTRUM_ERR_INVALID_TRANS_ID);
  }

  if ( trans_id != MF_LAZY_TRANS_ID ) {

	/* We can record the start of this txn right now */
	txn_typep =  &global_txn_types_array[trans_id];
    if ( !is_monitoring_enabled(txn_typep) ) {
      DEBUG_PRINTF(trans_id,"Monitoring is Disabled\n",0,0,0,0);
      return(MF_INSTRUM_ERR_CALL_NOT_ALLOWED_IN_THIS_STATE);
    }

	pthread_mutex_lock(&txn_typep->txntype_mutex_metrics);
	txn_typep->txntype_metricsp->NbInRequests++;
	pthread_mutex_unlock(&txn_typep->txntype_mutex_metrics);
	DEBUG_PRINTF(trans_id,"transaction uri '%s'\n",
                 txn_typep->txntype_metricsp->Uri,0, 0,0);
  } else {
    /* If the trans_id is a lazy one (ie. the caller does not currently
       know the type of the transaction, say because he has not decode the
       packet yet) then we don't know if it's disabled or not so fall through
       to allocate a txn object from the global msic list.
       
    */
	DEBUG_PRINTF(trans_id,"Lazy transaction idtransaction uri 'LAZY'\n",
                 0, 0, 0, 0);
  }

  /* pick up a new onetxn structure:
   * if a trans_id is supplied, we get it from the
   * pool for that transaction type.
   * Otherwise, it is taken from the misc pool.
   * In any case, if there are none left, we'll allocate
   * a new one.
   *
   */
  new_onetxnp = txn_get_onetxn( trans_id );
  new_onetxnp->onetxn_state = ONETXN_STATE_TICKING;
  new_onetxnp->onetxn_start = PR_IntervalNow();
  new_onetxnp->onetxn_cputime_start = mfGethrvtime();


  /* Return the handle (the pointer) to the onetxn structure */
  *trans_handle = (handle_t)new_onetxnp;

  DEBUG_PRINTF(trans_id,
               "<--mfStartTransaction\n",0,0,0,0);


  return(rc);
}




int mfStopTransaction(/*[in]*/ const unsigned short trans_id,
				  /*[in]*/ handle_t *transaction_handle,
				  /*[in]*/ const int tran_status){

  one_transaction_info_t *onetxnp = NULL;	
  transaction_type_info_t *txn_typep = NULL;
  PRIntervalTime response_time_delta = 0;
  long long service_time = 0;

  DEBUG_PRINTF(trans_id, "-->mfStopTransaction\n",0,0,0,0);

  if ( !(trans_id <= MF_MAX_NUM_TXN_TYPES) || 
	(trans_id == MF_UNDEFINED_TRAN_ID ) ) {

	DEBUG_PRINTF(trans_id,"Invalid trans_id: must be in the range 0 to %d\n",
				 MF_MAX_NUM_TXN_TYPES-1,0,0,0);
	return(MF_INSTRUM_ERR_INVALID_TRANS_ID);
  }

  /* Find the txn type
   * XXX What is the trans_id is MF_LAZY_TRANS_ID ?
   * In that case we won't find the right txn_type structure:
   * We should pass via the transaction handle and use:
   * onetxnp->onetxn_txntypep to get the txn_typep.
   *
   * In general, if monitoring is disabled we should skip calculations
   * but free any structures to avoid persistent objects resulting from
   * disbaling monitoring.
   */

  txn_typep =  &global_txn_types_array[trans_id];

  if ( !is_monitoring_enabled(txn_typep) ) {
    DEBUG_PRINTF(trans_id,"Monitoring is Disabled\n",0,0,0,0);
    return(MF_INSTRUM_ERR_CALL_NOT_ALLOWED_IN_THIS_STATE);
  }

  if ( transaction_handle == NULL || *transaction_handle == NULL ) {
	DEBUG_PRINTF(trans_id,
                 "Invalid NULL transaction_handle\n",0,0, 0, 0);
	return(MF_INSTRUM_ERR_INVALID_TRANS_HANDLE);
  }

  onetxnp = *((one_transaction_info_t **)transaction_handle);

  /* Check that it makes sense to call stop() on this transaction
  * should be synchronised with the java code.
  */   
  if ( onetxnp->onetxn_state == ONETXN_STATE_NOT_STARTED ||
       onetxnp->onetxn_state == ONETXN_STATE_STOPPED ) {
      DEBUG_PRINTF(trans_id,"Cannot call stop() when the transaction is not"
                   "started or already in state stopped\n",0,0,0,0);
      return(MF_INSTRUM_ERR_CALL_NOT_ALLOWED_IN_THIS_STATE);
  }

  /* Start the calculations to allow us to update the metrics object */
  onetxnp->onetxn_state = ONETXN_STATE_STOPPED;
  response_time_delta = 
    (PRIntervalTime)(PR_IntervalNow()-(onetxnp->onetxn_start));
  service_time = (mfGethrvtime()-onetxnp->onetxn_cputime_start) -
    onetxnp->onetxn_accumulated_blocked_cputime;

  DEBUG_PRINTF(trans_id, "Response time(us): %lu\n",
               PR_IntervalToMicroseconds(response_time_delta),0,0,0);
  DEBUG_PRINTF(trans_id, "Service time(ns): %llu\n",
               service_time,0,0,0);



  pthread_mutex_lock(&txn_typep->txntype_mutex_metrics);
  txn_typep->txntype_metricsp->NbOutRequests++;

  /* Whether to update NbInRequests depends on whether it was
   * a misc or txntype object (already done)
   */
  if ( onetxnp->onetxn_txntypep == NULL ) {
    txn_typep->txntype_metricsp->NbInRequests++;
  }
  
  /*
    fix of bugId: 5069933
    AccumulatedResponseTime always 0 
    the division performing the rounding to milliseconds
    were wrong
  */

  /* Adjust the times to be mili-seconds */
  txn_update_responsetime_needs_lock(txn_typep->txntype_metricsp,
                              (PR_IntervalToMicroseconds(response_time_delta))/1000);

  txn_update_servicetime_needs_lock(txn_typep->txntype_metricsp,
                             (service_time/1000000));
  /* XXX Need to do:
  * NbFailedRequests,
  * Single*
  */
  
  pthread_mutex_unlock(&txn_typep->txntype_mutex_metrics);

  /* reset the txn structure: must be reset to NULL
  * if it came from the misc pool and to the txntype
  * if it came from the txn type pool
  */
  txn_init_one_txn( onetxnp->onetxn_txntypep, onetxnp);

  /* Return the onetxn to the right queue--misc or txntype */  
  if( onetxnp->onetxn_txntypep == NULL ) {
    /* Came from the misc pool, so put it back */
    DEBUG_PRINTF(trans_id,"Returning onetxn to the misc pool\n",0,0,0,0);
   
    txn_enqueue_onetxn(&global_onetxn_misc_freelist, onetxnp);
  } else {
    DEBUG_PRINTF(trans_id,"Returning onetxn to the txntype pool\n",0,0,0,0);
    txn_enqueue_onetxn(&onetxnp->onetxn_txntypep->txntype_freelist, onetxnp);
  }
                     
#if defined(DEBUG_BUILD)  
  /* This will take the txntype_mutex_metrics mutex so
   * must call this having freed that mutex.
   */
  txn_display_txn_type(txn_typep);
#endif

  DEBUG_PRINTF(trans_id,"<--mfStopTransaction\n",0,0,0,0);

  return(MF_INSTRUM_SUCCESS);

} 


int mfBlockTransaction(/*[in] */ handle_t *transaction_handle){

  one_transaction_info_t *onetxnp = NULL;
	    


  if ( transaction_handle == NULL || *transaction_handle == NULL ) {
	DEBUG_PRINTF(MF_LAZY_TRANS_ID,
                 "Invalid NULL transaction_handle\n",0,0, 0, 0);
	return(MF_INSTRUM_ERR_INVALID_TRANS_HANDLE);
  }

  onetxnp = *((one_transaction_info_t **)transaction_handle);
  
  if ( !is_monitoring_enabled(onetxnp->onetxn_txntypep) ) {
    DEBUG_PRINTF(MF_LAZY_TRANS_ID,"Monitoring is Disabled\n",0,0,0,0);
    return(MF_INSTRUM_ERR_CALL_NOT_ALLOWED_IN_THIS_STATE);
  }

 /* Check the state transition */
 if ( onetxnp->onetxn_state == ONETXN_STATE_NOT_STARTED ||
       onetxnp->onetxn_state == ONETXN_STATE_STOPPED ||
      onetxnp->onetxn_state == ONETXN_STATE_PAUSED ) {

      DEBUG_PRINTF(MF_LAZY_TRANS_ID,
                   "Cannot call block() when the transaction is not"
            "started or already in state blocked or state stopped\n",0,0,0,0);
      return(MF_INSTRUM_ERR_CALL_NOT_ALLOWED_IN_THIS_STATE);
  }

 onetxnp->onetxn_state = ONETXN_STATE_PAUSED;
 
 onetxnp->onetxn_blocked_cputime_start = mfGethrvtime();

  return(MF_INSTRUM_SUCCESS);

}


int mfUnblockTransaction(/*[in] */ handle_t *transaction_handle){

  one_transaction_info_t *onetxnp = NULL;
  
  if ( transaction_handle == NULL || *transaction_handle == NULL ) {
	DEBUG_PRINTF(MF_LAZY_TRANS_ID,
                 "Invalid NULL transaction_handle\n",0,0, 0, 0);
	return(MF_INSTRUM_ERR_INVALID_TRANS_HANDLE);
  }

  onetxnp = *((one_transaction_info_t **)transaction_handle);
  
  if ( !is_monitoring_enabled(onetxnp->onetxn_txntypep) ) {
    DEBUG_PRINTF(MF_LAZY_TRANS_ID,"Monitoring is Disabled\n",0,0,0,0);
    return(MF_INSTRUM_ERR_CALL_NOT_ALLOWED_IN_THIS_STATE);
  }

 /* Check the state transition */
 if ( onetxnp->onetxn_state == ONETXN_STATE_NOT_STARTED ||
       onetxnp->onetxn_state == ONETXN_STATE_STOPPED ||
      onetxnp->onetxn_state == ONETXN_STATE_TICKING ) {

      DEBUG_PRINTF(MF_LAZY_TRANS_ID,
                   "Cannot call block() when the transaction is not"
            "started or already in state blocked or state stopped\n",0,0,0,0);
      return(MF_INSTRUM_ERR_CALL_NOT_ALLOWED_IN_THIS_STATE);
  }

 onetxnp->onetxn_accumulated_blocked_cputime += 
   (mfGethrvtime()-onetxnp->onetxn_cputime_start);

  return(MF_INSTRUM_SUCCESS);

}


int mfTransactionVersion(/*out */ char *jesmf_version){

  strcpy(jesmf_version, MF_INSTRUM_VERSION_STRING );
          
  return(MF_INSTRUM_SUCCESS);
}  

/*
 * From gettimeofday() man page:
 *
 * long    tv_sec;    seconds since Jan. 1, 1970 
 * long    tv_usec;   and microseconds 
*/  

int mfComputeDeltaTime(/*in */ struct timeval *start_tvp, 
				    /*in */ struct timeval *stop_tvp, 
				    /*out */ unsigned long *delta_time_msec) {

  *delta_time_msec = ((stop_tvp->tv_sec) * 1000 + (stop_tvp->tv_usec)/1000 ) -
    ((start_tvp->tv_sec) * 1000 + (start_tvp->tv_usec)/1000 );

  return(MF_INSTRUM_SUCCESS);

}

/* Provide a  */
void mfSetIsMonitoringEnabledPointer( long long *is_monitoring_enabledp,
                                      unsigned short tran_id ) {

  global_txn_types_array[tran_id].txntype_is_monitoring_enabledp =
    is_monitoring_enabledp;

}


/**************** Implementation of exported private functions *******/

#if 0
/* This has moved to the resource server or something */
/* Callable via some mechanism by the resource server, but not
 * generally available.
 */

void mfTransactionResetAllMetricsObjectCounters(void) {

  int i = 0;
  int num = 0;
  transaction_type_info_t *txn_typep = NULL;

  DEBUG_PRINTF(MF_UNDEFINED_TRAN_ID,
          "-->mfTransactionResetAllMetricsObjectCounters\n", 0,0,0,0);
  pthread_mutex_lock(&global_mutex_num_txn_types);
  num = global_num_txn_types;
  pthread_mutex_unlock(&global_mutex_num_txn_types);

  DEBUG_PRINTF(MF_UNDEFINED_TRAN_ID,
          "Resetting %d metrics objects\n", num,0,0,0);

  i = 0;
  while( i < num ) {

    txn_typep = &global_txn_types_array[i];
    
    pthread_mutex_lock(&txn_typep->txntype_mutex_metrics);
    /* XXX What about the uri ? mem leak
    bzero(txn_typep->txntype_metricsp, sizeof(mfTransactionMetrics_t));*/
    txn_typep->txntype_metricsp->NbInRequests = 0;
    txn_typep->txntype_metricsp->NbOutRequests = 0;
    txn_typep->txntype_metricsp->NbFailedRequests = 0;
    txn_typep->txntype_metricsp->AccumulatedResponseTime = 0;
    txn_typep->txntype_metricsp->MinResponseTime = 0;
    txn_typep->txntype_metricsp->MaxResponseTime = 0;
    txn_typep->txntype_metricsp->AccumulatedSqResponseTime = 0;
    txn_typep->txntype_metricsp->AccumulatedServiceTime = 0;
    txn_typep->txntype_metricsp->MinServiceTime = 0;
    txn_typep->txntype_metricsp->MaxServiceTime = 0;
    txn_typep->txntype_metricsp->AccumulatedSqServiceTime = 0;
    txn_typep->txntype_metricsp->SingleAccumulatedServiceTime = 0;
    txn_typep->txntype_metricsp->SingleAccumulatedSqServiceTime = 0;
    txn_typep->txntype_metricsp->SingleMaxServiceTime = 0;
    txn_typep->txntype_metricsp->SingleMinServiceTime = 0;
    txn_typep->txntype_metricsp->SingleNbFailedRequests = 0;

    pthread_mutex_unlock(&txn_typep->txntype_mutex_metrics);

    i++;

  }
  
  DEBUG_PRINTF(MF_UNDEFINED_TRAN_ID,
          "<--mfTransasetAllMetricsObjectCounters\n", 0,0,0,0);
}
#endif



/***************** Static Implementations ***************************/

/* Could be dispersed into different files */

/**************************************************************************/

/*
 * Initialize a new transaction type.
 *
 * All the parameters have been verfied already.
 * The memory has already been zeroed.
 *
 * No lock is required: we have already reserved the slot 'index'
 * in the global txn types array.  
 */
 
static void txn_init_txn_type(unsigned short index,
					   const char *transaction_type_name,
					   const char *uri,
					   const mfTransactionMetrics_t *metricsp  ){

  mfTransactionMetrics_t *transactionMetrics;
  
  transactionMetrics = (mfTransactionMetrics_t *) metricsp;
  
  /* Alloc a free list of one_transaction_info_t structures
	 for this transaction type.	 
  */
  txn_alloc_onetxns_list( &(global_txn_types_array[index]),
				   &(global_txn_types_array[index].txntype_freelist),
						  INITIAL_NUM_TXNS_PER_TXNTYPE);
  pthread_mutex_init(
				 &(global_txn_types_array[index].txntype_mutex_metrics),
				 NULL);

 /* copy the txn type name */
  strncpy( global_txn_types_array[index].txntype_name,
	     transaction_type_name,
	     MIN(strlen(transaction_type_name),MF_MAX_TXN_NAME_LEN) ) ;

  /* Consume the metrics structure provided */
  transactionMetrics->AccumulatedResponseTime = 0;
  transactionMetrics->AccumulatedServiceTime = 0;
  transactionMetrics->AccumulatedSqResponseTime = 0;
  transactionMetrics->AccumulatedSqServiceTime = 0;
  transactionMetrics->MaxResponseTime = 0;
  transactionMetrics->MaxServiceTime = 0;
  transactionMetrics->MinResponseTime = 0;
  transactionMetrics->MinServiceTime = 0;
  transactionMetrics->NbFailedRequests = 0;
  transactionMetrics->NbInRequests = 0;
  transactionMetrics->NbOutRequests = 0;
  transactionMetrics->SingleAccumulatedServiceTime = 0;
  transactionMetrics->SingleAccumulatedSqServiceTime = 0;
  transactionMetrics->SingleMaxServiceTime = 0;
  transactionMetrics->SingleMinServiceTime = 0;
  transactionMetrics->SingleNbFailedRequests= 0;
  

  global_txn_types_array[index].txntype_metricsp = 
	(mfTransactionMetrics_t *)metricsp;

  /* copy the uri name */
  global_txn_types_array[index].txntype_metricsp->Uri = 
      strdup(uri);	  

  /* Set the pointer to the memory indicating the monitoring status
   * for this txn type to null to start with
  */
   global_txn_types_array[index].txntype_is_monitoring_enabledp = NULL;
   
   
}

/*
 * Initializes a one_transaction_info_t structure.
 * The txn_typep can be NULL, for example
 * if start() is called with the lazy trans_id.
 */
static void txn_init_one_txn(  transaction_type_info_t *txn_typep,
							   one_transaction_info_t *one_txnp ){

  one_txnp->onetxn_txntypep = txn_typep;
  one_txnp->onetxn_blocked_cputime_start = 0;
  one_txnp->onetxn_accumulated_blocked_cputime = 0;
  one_txnp->onetxn_status = 0;
  one_txnp->onetxn_next_txnp = NULL;
  one_txnp->onetxn_state = ONETXN_STATE_NOT_STARTED;

}

/*
 * Allocates a linked list of one_transaction_info_t
 * structures of length "num" starting at (*first_txnp).
 *
 * txntype_infop can be NULL: for example when we allocate
 * a misc list of txns not pre attached to a transaction type.
 * this is needed because start() can be called with no trans_id.
 */

static void txn_alloc_onetxns_list(  transaction_type_info_t *txntype_infop,
									 onetxn_list_t *txntype_freelistp,
							 int num ) {

  int i = 0;
  one_transaction_info_t **prev_txnpp = &(txntype_freelistp->otl_listp);
  one_transaction_info_t *one_txnp = NULL;
  
  
  i = 0;
  while ( i < num ) {

	one_txnp = 
	  (one_transaction_info_t *) calloc( 1,
										 sizeof(one_transaction_info_t));
	txn_init_one_txn( txntype_infop, one_txnp );
	*prev_txnpp = one_txnp;
	prev_txnpp = &(one_txnp->onetxn_next_txnp);
	i++;
  }
  txntype_freelistp->otl_num_txns_in_list = num;
  txntype_freelistp->otl_num_active_txns = 0;
  pthread_mutex_init( &(txntype_freelistp->otl_mutex), NULL);
}

/*
 * Given a trans_id, gets a structure to store
 * info about this transaction.
 * Takes care of:
 * 1. if the tran_sid is not defined (takes the strucutre from teh misc
 * pool.
 * 2. if there are none availalbe in the pre-alloced pool, then 
 * allocs another one.  XXX Should we be freeing up these extra guys
 * till we get back to the initial levels ?
 *
 */
static one_transaction_info_t *txn_get_onetxn( unsigned short trans_id ) {

  one_transaction_info_t *ret_one_txnp = NULL;
  transaction_type_info_t *txn_typep = NULL;

  /* Here, pick up a structure to record data 
   * about this transaction
   */
  if ( trans_id == MF_LAZY_TRANS_ID ) {

	/* Here, the caller doesn't yet know his transid--perhaps he
	 * has not yet done his packet decoding.
	 * In this case, we'll record that fact and at
	 * mfStopTransaction() time we'll
	 * expect a trans_id to allow us to update
	 * th right transaction type.
	*/
	
	ret_one_txnp = txn_dequeue_onetxn(txn_typep, &global_onetxn_misc_freelist);
    DEBUG_PRINTF(trans_id,"Taking onetxn from misc pool\n",0,0,0,0);

  } else {
	/* Here a trans_id has been supplied so we can go straight to 
	 * the right transaction type
	 */
	txn_typep = &global_txn_types_array[trans_id];
	ret_one_txnp =
	  txn_dequeue_onetxn(txn_typep, &txn_typep->txntype_freelist);
    DEBUG_PRINTF(trans_id,"Taking onetxn from txntype pool\n",0,0,0,0);
  }
#if 0
/* moved to inside the txn_dequeue_onetxn() function as
 * we really need to inc the active txn counter which
must be done in there for the locking.
*/
  if ( ret_one_txnp == NULL ) {
	ret_one_txnp = 
	  (one_transaction_info_t *) calloc( 1,
										 sizeof(one_transaction_info_t));
	txn_init_one_txn( txn_typep, ret_one_txnp );


  }
#endif
  return(ret_one_txnp);
}

/* Dequeues a one_transaction_t structure from a free list.
 * Takes care of the locking.
 * If the free list is empty, 
 * it alloates a new one_transaction_t structure and
 * updatres the active counter in hte corresponding free list.
 * That's good because we can watch if the lsit grows above the initial
 * number--currently there is no mechanism to free these new guys which
 * would bring the free lis tbacl to it's rest size.
 */
static one_transaction_info_t *txn_dequeue_onetxn(
							transaction_type_info_t *txn_typep,
							   onetxn_list_t *freelistp ){

  one_transaction_info_t *ret_onetxnp = NULL;

  if ( freelistp != NULL ) {
	 pthread_mutex_lock(&freelistp->otl_mutex);
	 ret_onetxnp = freelistp->otl_listp;
	 if ( ret_onetxnp != NULL ) {
	   freelistp->otl_listp = ret_onetxnp->onetxn_next_txnp;
	   freelistp->otl_num_active_txns++;
	   freelistp->otl_num_txns_in_list--;
	   ret_onetxnp->onetxn_next_txnp = NULL; 
	 } else {
		/* Oh dear we've run out of pre-allocated txn objects
	     * Let's get some more 
	    */
		ret_onetxnp = 
	  		(one_transaction_info_t *) calloc( 1,
										 sizeof(one_transaction_info_t));
		/* It's Ok if txn_typep is NULL: not dereferenced */
		txn_init_one_txn( txn_typep, ret_onetxnp );
		freelistp->otl_num_active_txns++;
		DEBUG_PRINTF( MF_UNDEFINED_TRAN_ID,
					  "Allocated an additional txn object \n",0,0,0,0);
	}
	 pthread_mutex_unlock(&freelistp->otl_mutex);
  }
    
  return(ret_onetxnp);
}

static void txn_enqueue_onetxn(onetxn_list_t *onetxn_freelistp,
                        one_transaction_info_t *onetxnp) {

  /* Return it to the list */
   if ( onetxn_freelistp != NULL ) {
	 pthread_mutex_lock(&onetxn_freelistp->otl_mutex);
     if ( onetxn_freelistp->otl_listp != NULL ) {
       onetxnp->onetxn_next_txnp =
         onetxn_freelistp->otl_listp;
         //         onetxn_freelistp->otl_listp->onetxn_next_txnp;
       onetxn_freelistp->otl_listp = onetxnp;
     } else {
        onetxn_freelistp->otl_listp = onetxnp;
     }
     onetxn_freelistp->otl_num_txns_in_list++;
     onetxn_freelistp->otl_num_active_txns--;
     pthread_mutex_unlock(&onetxn_freelistp->otl_mutex);
   }	
   
}

/*
 * Update the response time metrics in th metrics object using this latest
 * response time info 
 */
static void txn_update_responsetime_needs_lock(
                            mfTransactionMetrics_t *metricsp,
                            unsigned long response_time_ms) {

  metricsp->MinResponseTime =
    MIN(response_time_ms,metricsp->MinResponseTime);

  metricsp->MaxResponseTime =
    MAX(response_time_ms,
        metricsp->MaxResponseTime);

  metricsp->AccumulatedResponseTime += response_time_ms;
  
  metricsp->AccumulatedSqResponseTime +=
    (response_time_ms * response_time_ms);

}

/*
 * Update the response time metrics in th metrics object using this latest
 * response time info 
 */
static void txn_update_servicetime_needs_lock(
                            mfTransactionMetrics_t *metricsp,
                            unsigned long service_time_ms) {

  metricsp->MinServiceTime =
    MIN(service_time_ms,metricsp->MinServiceTime);

  metricsp->MaxServiceTime =
    MAX(service_time_ms,
        metricsp->MaxServiceTime);

  metricsp->AccumulatedServiceTime += service_time_ms;
  
  metricsp->AccumulatedSqServiceTime += (service_time_ms * service_time_ms);

}

static int is_monitoring_enabled( transaction_type_info_t *txn_typep ) {
    
  if ( txn_typep == NULL) {
    return(1); /* default is enabled, as for java */
  } else {
    /* Looks like a valid trans id */   
    if ( txn_typep->txntype_is_monitoring_enabledp ) {
      if ( *(txn_typep->txntype_is_monitoring_enabledp) ){
        return(1);
      } else {
        return(0);
      }
    } else {
      return(1);/* default is enabled, as for java */
    }  
  }        
}

