#include <stdio.h>
#include <time.h>
#include <sys/timeb.h>
#include "Connect.h"
#include "inet-udp.h"
#include "sim.h"

#define printError() fprintf(stderr, "%s\n", Error_String(Error))

Card32 process_packet(); 
Card32 send_next();
Card32 add_to_queue();
TOSEND * get_next();
int print_trans();
int print_conn();
CONN_STATUS *look_up_conn();
void get_random_num();


static  TOSEND *first = 0; /* next to send */
static int num_conn_rec = 0;
static time_t elapsed_time = 0;
static int debug = 0;
static struct timeb  timenow;
static int i = 0; /* number in Q */
main(argc, argv)
     int argc;
     char **argv;
{
  Addr a, b;
  Packet p,*apacket;
  Card32 next_time = 0,waketime;          /* millesecs from start */
  time_t start_time;                  /*starting time in secs */ 

  time(&start_time);

  if (argc > 1)
    debug = atoi(argv[1]);
  printf("debug level = %d\n",debug);
  if (Connect_Initialize())
    printError();
  if (Connect_RegisterDomain(&inetudp))
    printError();
  load_conn_defs();
  if (Connect_NameToAddress("inet-udp:jimi/7777" , &a))
    printError();
 if (Connect_NameToAddress( FAKE_ADDR , &b))
    printError();
/* how do we set a dummy connect to addr? */
  if (Connect_OpenConnection(b,a))
    printError();

  for (;;)
    {
      /*****/
      ftime(&timenow);
      elapsed_time = (timenow.time - start_time) * 1000 + timenow.millitm;
      if ((next_time <= elapsed_time) && (next_time != 0))
	next_time = send_next();    /* oh no! we should send something */
      if (next_time != 0)
	waketime = next_time - elapsed_time;
      else
	waketime = NOTIMEOUT;  
      if (debug) {
	printf("top of main loop: time - %ld, next wake = %ld # in Q = %d\n",
	       elapsed_time,waketime,i); 
	printf("next_time = %ld\n",next_time);
      }
      Error = 0;

      if (Connect_Wait(&p, waketime))
	{
	  printError();
	/*  continue; */
	}
      ftime(&timenow);
      elapsed_time = (timenow.time - start_time) * 1000 + timenow.millitm;
      if (Error == CONNECT_TIMEOUT) {
	next_time = send_next();
      }
      else /* got a packet*/
	{
	  /* copy packet */
	  apacket = (Packet *) malloc(sizeof(Packet));
	  bcopy(&p,apacket,sizeof(Packet)); /*****/
	  next_time = process_packet(apacket);
	}
      if (next_time == 0)
	if (first)
	  next_time = first->when;
     } /* end of for forever */
}
/*****************************************************************/
/* remove leading addr info and save addresses */
Card32
process_packet(Packet *p) 
{
Card32 rc;
TOSEND *pending_trans;
Card32 when;

pending_trans = (TOSEND *) malloc (sizeof(TOSEND));
rc = gen_trans(p,pending_trans);

if (rc == NOW) { /* send now */
  if (Connect_SendPacket(p))
    printError();
  return 0;
}
else if (rc == DROP) {
  if (debug > 2)
    printf("dropping this one!! \n");
  return 0;   /* never mind let's throw this away */
}
else
  when = add_to_queue(pending_trans);
if (debug > 5)
  printf("time returned from add to Q = %ld\n",when);
  return when;
}

/*****************************************************************/
gen_trans(Packet *p, TOSEND *pending_trans)
     /* takes packet and figures out when to send it etc
	returns this struct */
{
  CONN_STATUS *conn;

  if (debug > 5) {
    printf("gen_trans... looking for conn rec ");
    printf("from %ld to %ld\n",p->Source,p->Destination);
  }
  conn = look_up_conn(p);
  if (!conn)
    {
      printf("conn rec not found\n");
      return NOW;
    }
  else if (debug > 7)
    print_conn(conn);
  pending_trans->conn_ptr = conn; 
  pending_trans->duplicate = FALSE; 
  pending_trans->next = NULL; 
  pending_trans->packet = p; 
  decide_fate(pending_trans);
  if (debug > 7)
    print_trans(pending_trans);

  return pending_trans->when;
}

/*****************************************************************/
Card32
send_next()      /* takes packet off Q, returns  next time value */
{
  TOSEND *trans;
  Card32 rc;
  int dupe;

  trans = get_next();

if (!trans)
  {
    printf("couldn't get next\n");
    return 0;
  }

if (debug > 7) {
  print_trans(trans);
}
  dupe = trans->duplicate;

  if (Connect_SendPacket(trans->packet))
    printError();

  if (dupe)  /* we're supposed to duplicate this so put one
		back on the Q as well as send it */
    {
      rc = decide_fate(trans);
      trans->duplicate = FALSE;
      trans->next = NULL; 
      if (trans->when == DROP) 
	trans->when = NOW;           /* Don't want to drop packets we're 
				       trying to duplicate */
      if  (trans->when != NOW)  
	rc = add_to_queue(trans);
      if (debug > 9)
	printf("return from add to Q (in send) = %ld\n",rc);
      return rc;
      }


  if (!dupe) /* free both packet & trans */
    {
      free(trans->packet);
      free(trans);
    }
  if (first)
    return first->when;
  else
    return 0;
}

/*****************************************************************/
Card32
add_to_queue(TOSEND *pending_trans) /* adds packet to Q time sorted, returns next time value */
{

TOSEND *aptr,*last;
 
i++;
    if (debug > 9)
      printf("adding rec to Q as # %d\n",i);

   if (!first) {  
      first = pending_trans;
      return pending_trans->when;
    }

if (first->when > pending_trans->when)
  {
    pending_trans->next = first;
    first = pending_trans;
    return first->when;
  }

for (aptr = first; aptr->next; aptr = aptr->next)
  {
    if (pending_trans->when < aptr->when) /*  newer */
      {
	last->next = pending_trans;
	pending_trans->next = aptr;
	return first->when;
      }
    last = aptr;
   }

aptr->next = pending_trans;
return first->when; 
}

/*****************************************************************/
TOSEND *
get_next() /* remove from q */
{
  TOSEND *pending_trans;

i--;
  pending_trans = first;

  if (first)
    first = first->next;

  if (debug > 9) {
    printf("FOUND NEXT:\n");
    print_trans(pending_trans); 
  }

  return pending_trans;
}

/*****************************************************************/
decide_fate(TOSEND *pending_trans)
{ /* determine the delay and add it to the present time &
     determine if this packet should be duplicated */

time_t time_delay = 100;
CONN_STATUS *conn;
int rd100;
long rd;
int dupe,drop,delay;

conn = pending_trans->conn_ptr;

/* get a random # 
rd = random(); */
get_random_num(&conn->random_sd);
 /* get delay */
rd = conn->random_sd;
time_delay = (rd  % (conn->max_delay - conn->min_delay)) + conn->min_delay;

rd100 = rd % 100;

if (debug)
  printf("rd100 = %ld\n",rd100);

if (rd100 < conn->drop)  /* drop this packet! */
  {
    pending_trans->when = DROP; 
    return;  
  }
if (rd100 > (100 -  conn->delayed)) /* delay this one */
  {  
    pending_trans->when = elapsed_time + time_delay;
    return;
  }
if ((rd100 >= conn->drop) && (rd100 <= (conn->drop + conn->duplicate)))
  {   /* dupe this! */
      pending_trans->duplicate = TRUE;
      pending_trans->when = elapsed_time + time_delay;
      return;
    }
pending_trans->when = NOW; 
return; 
}

CONN_STATUS conn_list[1000];
static int num_conn_entries = 0;

/*****************************************************************/
load_conn_defs() /* we need to load the definitions of connection 
		    characteristics from a file */
{
FILE *conn_file;
int cnt = 0;
char line[BUFSIZ];

if (!(conn_file = fopen(CONN_FILE,"r"))) {
  printf("couldn't open conn file \n");
  exit();
}

while (fgets(line,BUFSIZ,conn_file)) {
  if (*line == '#' || *line == ' ')
    continue;
  line[strlen(line) - 1] = '\0';
  make_conn_rec(line,cnt);
  cnt++;
}
num_conn_rec = cnt;
fclose(conn_file);
}

/***************************************************************/
char    *
next_field(ptr)
        char          **ptr;
{
        register char  *cp = *ptr;

        while (*cp != NULL && *cp != DLM)
                cp++;
        if (*cp == NULL)  /* end of the string */
            return (*ptr = cp);

        *cp = '\0';
        return (*ptr = ++cp);
}

/*****************************************************************/
make_conn_rec(char *line,int cnt)
{
char *cp,*ptr,*addr1,*addr2,tmpstr[100];
CONN_STATUS *conn;

conn = &conn_list[cnt];

cp = line;
addr1 = line;

addr2 = next_field(&cp);
conn->t_range_start = atol(next_field(&cp));
conn->t_range_end = atol(next_field(&cp));
conn->drop = atoi(next_field(&cp));
conn->delayed = atoi(next_field(&cp));
conn->duplicate = atoi(next_field(&cp));
conn->min_delay = atoi(next_field(&cp));
conn->max_delay = atoi(next_field(&cp));
conn->random_sd = atol(next_field(&cp));

sprintf(tmpstr,"inet-udp:%s",addr1);
Connect_NameToAddress(tmpstr,&conn->addr1);
sprintf(tmpstr,"inet-udp:%s",addr2);
Connect_NameToAddress(tmpstr,&conn->addr2);

if (debug > 5)
  print_conn(conn);

}

/*****************************************************************/
CONN_STATUS *
look_up_conn(Packet *p)

{ /* given the addrs figure out the connection characteristics */
/* compare addr pairs and time period until a match is found  */
 /* don't care which way its going */

int i;
Addr from,to;

from = p->Source;
to = p->Destination;

for (i = 0; i < num_conn_rec; i++)
  {   
   /*  print_conn(&conn_list[i]); */
    if (((conn_list[i].addr1 == from) &&  
	 (conn_list[i].addr2 == to))
     || ((conn_list[i].addr1 == to) &&  
	 (conn_list[i].addr2 == from)))
      {
	/* ok found a valid addr pair now check times */
	if (((conn_list[i].t_range_start)*1000 <= elapsed_time) &&
	    ((conn_list[i].t_range_end)*1000 >= elapsed_time))
	  return &conn_list[i];
      }
  }
return 0;
}


print_trans(TOSEND *trans)
{
  printf("Conn : %ld-->%ld\n",trans->packet->Source,
	 trans->packet->Destination);
  printf("delay           : %ld\n",trans->when);
  printf("duped           : %ld\n",trans->duplicate);
  printf("packet== <%s>\n",trans->packet->packet);
}

print_conn(CONN_STATUS *conn)
{
  printf("addr1   = %ld\n",conn->addr1);
  printf("addr2   = %ld\n",conn->addr2);
  printf("start   = %ld\n",conn->t_range_start);
  printf("end     = %ld\n",conn->t_range_end);
  printf("lost    = %d \n",conn->drop);
  printf("duped    = %d \n",conn->duplicate);
  printf("delayed = %d \n",conn->delayed);
  printf("min delay = %d \n",conn->min_delay);
  printf("max delay = %d \n",conn->max_delay);
  printf("seed = %ld \n\n\n",conn->random_sd);
}

void
get_random_num(unsigned long *seedptr)
{
unsigned long seed,x;

seed = *seedptr;

x = seed * seed;

*seedptr = x % 1000009;
return;

}
