/* copyright Tariq Rashid, 21-04-04, all rights reserved  */
/* latest current GPL licence applied, unless overriden by author */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <radlib.h>
#include <time.h>
#include <sys/time.h>


/* command line parameters paramaters, with overriden defaults */
int MAX_THREADS = 1;
double INIT_INTERVAL = 1.0;
double DECR_INTERVAL = 0.25;
double STEP_TIME = 5.0;

/* global threads counter (protected by mutex) */
int CURRENT_THREADS = 0;

/* prototypes */
void test(void *);
void rad_test(void *);


/* global structures and variables  */
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
struct timeval first_time;
struct timespec delay;
/* thread data structure */
struct thread_data {
  int  thread_id;
  struct rad_handle *rad_handle;
  double interval;
};


int main(int argc, char *argv[])
{
  /* structures and variables */
  long thread_number = 0;
  struct rad_handle *rh = NULL;
  double interval = INIT_INTERVAL;
  struct timeval ct;
  double dt_expt = 0;
  pthread_attr_t pthread_attr;

  /* set global options from command line */
  if (argc != 5) {
    printf("usage: ./thread_test <threads> <init_interval> <decr_interval> <step_time>\n");
    printf("exit.\n");
    exit(1);
  } else {
    MAX_THREADS = atoi(argv[1]);
    INIT_INTERVAL = atof(argv[2]);
    DECR_INTERVAL = atof(argv[3]);
    STEP_TIME = atof(argv[4]);
  }
  interval = INIT_INTERVAL;

  /* time interval */
  delay.tv_sec = (long) (INIT_INTERVAL / 1.0);
  delay.tv_nsec = (INIT_INTERVAL - delay.tv_sec) * 1000000000;

  printf("#threaded radius test, max threads = %d\n", MAX_THREADS);

  /* radius context */
  rh = rad_auth_open();
  /* add server */
  rad_add_server(rh, "1.2.3.4", 1645, "the_secret", 60, 1);
  
  /* radius request */
  rad_create_request(rh, RAD_ACCESS_REQUEST);
  rad_put_string(rh, RAD_USER_NAME, "nadiatest1@dsl3.easynet.co.uk");
  rad_put_string(rh, RAD_USER_PASSWORD, "t3st1ng");
  
  /* mark start time for experiments */
  gettimeofday(&first_time, (struct timezone*)0);
  
  /* initialize and set thread detached attribute */
  pthread_attr_init(&pthread_attr);
  pthread_attr_setdetachstate(&pthread_attr, PTHREAD_CREATE_DETACHED);

  /* create new threads up to MAX_THREADS threads until interval is zero */
  while(interval >= DECR_INTERVAL) {
    /* only if maximum active threads has not been reached, create new thread */
    if (CURRENT_THREADS < MAX_THREADS) {
      pthread_t pthread;
      struct thread_data pthread_data;

      /* create thread and parameter data structure */
      pthread_data.thread_id = thread_number;
      pthread_data.rad_handle = rh;
      pthread_data.interval = interval;
      
      /* send off detached thread to do radius request */
      pthread_create(&pthread, &pthread_attr, (void *) rad_test, (void *) &pthread_data);

      /* increment total number of threads */
      ++thread_number;
    }

    /* check to see if interval needs decrementing  */
    gettimeofday(&ct, (struct timezone*)0);
    dt_expt = (ct.tv_sec - first_time.tv_sec) + (ct.tv_usec - first_time.tv_usec) * 0.000001;
    interval = INIT_INTERVAL - (DECR_INTERVAL * (int)(dt_expt/STEP_TIME));
    delay.tv_sec = (long) (interval / 1.0);
    delay.tv_nsec = (interval - delay.tv_sec) * 1000000000;
    // printf(" -- %f, [%d], %f --\n", dt_expt, (int)(dt_expt/STEP_TIME), interval);

    /* sleep for interval */
    nanosleep(&delay, NULL);
  } /* end of loop */


  /* clean up attributes */
  pthread_attr_destroy(&pthread_attr);

  /* close context handle */
  rad_close(rh);

  /* flush output buffers */
  fflush(stdout);

  /* exit thread */
  pthread_exit(NULL);
}


void rad_test(void  *void_ptr)
{

  struct thread_data *my_data;
  struct timeval t1, t2;
  double dt_secs = 0;
  double dt_expt = 0;
  int ret = 0;

  /* increment thread counter (protected)  */
  pthread_mutex_lock(&mutex);
  ++CURRENT_THREADS;
  pthread_mutex_unlock(&mutex);


  /* extract thread data */
  my_data = (struct thread_data *) void_ptr;
  
  /* lock & send request */
  // pthread_mutex_lock(&mutex);
  gettimeofday(&t1, (struct timezone*)0);
  ret = rad_send_request(my_data->rad_handle);
  gettimeofday(&t2, (struct timezone*)0);
  // pthread_mutex_unlock(&mutex);
  
  dt_secs = (t2.tv_sec - t1.tv_sec) + (t2.tv_usec - t1.tv_usec) * 0.000001;
  dt_expt = (t1.tv_sec - first_time.tv_sec) + (t1.tv_usec - first_time.tv_usec) * 0.000001;
  if (ret == RAD_ACCESS_ACCEPT) {
    /* printf ("accept : t = %f, dt_secs = %f\n", dt_expt, dt_secs); */
    printf ("%f\t%f\t%f\t%d\n", dt_expt, dt_secs, my_data->interval, my_data->thread_id);
  }
  if (ret == RAD_ACCESS_REJECT) {
    /* printf ("reject : dt = %f, t_secs = %f\n", dt_expt, dt_secs); */
    printf ("%f\t%d\t%f\t%d\n", dt_expt, -1, my_data->interval, my_data->thread_id);
  }

  /* decrement thread counter (protected)  */
  pthread_mutex_lock(&mutex);
  --CURRENT_THREADS;
  pthread_mutex_unlock(&mutex);

  /* exit thread */
  pthread_exit(NULL);
  
}
