

#include "csim.h"
#include <stdio.h>
#include <math.h>

typedef struct s_msg{
    int src;
    int des;     /* -1 means base station */
    int type;
    double time;
    int dataSize;
} s_msg;

typedef struct s_sensor{
    double x;
    double y;
    MBOX mb;
    EVENT ev_fire_detection;
} s_sensor;

MBOX      BS_mb;
EVENT     ev_sim_done;
s_sensor *a_sensor;

double    maxX, maxY, SR, C, Te, SIM_TIME;
int       N, totRcvMsg;
double    totaldelay;
int       generated, detected;

void Base_station();
void propagate_msg(s_msg *a_msg);
void sensor (int i);
void sensor_com (int i, MBOX mb);
void sensor_detection (int i, MBOX mb);
void Generate_fire_event();
void myReport();

void sim(int argc, char *argv[]){
    
    short parerror;
    int seed,i;
    
    create ("sim");
    totRcvMsg=0;
    totaldelay=0.0;
    detected = 0;
    generated = 0;
    
    i = 1;
    max_events ((long)1000);
    max_mailboxes ((long)1000);
    parerror = 0;
    while (i < argc){
	if ((strcmp (argv[i], "-s") == 0) && (i+1 < argc) &&
	    (sscanf(argv[i+1], "%i", &seed) == 1)) i++;
	else if ((strcmp(argv[i], "-N") == 0) && (i+1 < argc) &&
		 (sscanf (argv[i+1], "%i", &N) == 1)) i++;
	else if ((strcmp(argv[i], "-SR") == 0) && (i+1 < argc) &&
		 (sscanf (argv[i+1], "%lf", &SR) == 1)) i++;
	else if ((strcmp(argv[i], "-MaxX") == 0) && (i+1 < argc) &&
		 (sscanf (argv[i+1], "%lf", &maxX) == 1)) i++;
	else if ((strcmp(argv[i], "-MaxY") == 0) && (i+1 < argc) &&
		 (sscanf (argv[i+1], "%lf", &maxY) == 1)) i++;
	else if ((strcmp(argv[i], "-C") == 0) && (i+1 < argc) &&
		 (sscanf (argv[i+1], "%lf", &C) == 1)) i++;
	else if ((strcmp(argv[i], "-Te") == 0) && (i+1 < argc) &&
		 (sscanf (argv[i+1], "%lf", &Te) == 1)) i++;
	else if ((strcmp(argv[i], "-SIM_TIME") == 0) && (i+1 < argc) &&
		 (sscanf (argv[i+1], "%lf", &SIM_TIME) == 1)) i++;
	else parerror = 1;
	i++;
    }  // end of while for command line argument test
    if (argc <= 7 || parerror == 1){
	fprintf (stderr, "Usage: %s -s valueOfSeed -N numOfSensors -SR sensorRange -MaxX lengthOfRoom -MaxY widthOfRoom -C someGivenC -Te inter-arrTimeExp -SIM_TIME durationOfSim\n", argv[0]);
	exit (-1);
    }
    
    reseed (NIL, seed);
    
    if ((a_sensor = (s_sensor *)malloc(N*sizeof(s_sensor))) == NULL){
	fprintf (stderr, "Malloc Error - not able to create sensor\n");
	exit (-1);   // cannot create sensor - cannot continue with function
    }
 
    ev_sim_done = event("event_sim_done");
 
    Base_station();
    for (i = 0; i < N; i ++){
	sensor(i);
    }
    Generate_fire_event();
    hold (SIM_TIME * 60.0);
    set(ev_sim_done);
    myReport();
}

void Base_station(){
    int i;
    s_msg *a_msg;
    s_msg *rcv_msg;
    double current_time;
    
    create ("Base_station");  
    
    BS_mb = mailbox ("BS_mb");
    
    hold (1.0); // hold 1 sec
    while (1){
	for (i = 0; i < N; i ++){
	    if ((a_msg =(s_msg *)malloc (sizeof (s_msg))) == NULL){
		fprintf (stderr, "Malloc error - not able to create message\n");
		exit (-1);  // cannot create message - cannot continue
	    }
	    a_msg->src = -1; // src is Base station
	    a_msg->des = i;
	    a_msg->type = 1;
	    a_msg->dataSize = 0;
	    hold ((8.0*sizeof(s_msg))/(C*1000)); // transmission delay
	    propagate_msg(a_msg);
	    
	    receive (BS_mb, (long *)&rcv_msg);
	    if(rcv_msg->type == 2) {
		totRcvMsg ++;
		current_time = simtime();
		totaldelay += (current_time - rcv_msg->time);
	    }
	    free(rcv_msg);
	} // end of for loop
    } // done with while loop
} // done with base station

void propagate_msg (s_msg *a_msg){
    double sendX, sendY, receiveX, receiveY, distance;
    
    create ("propagate_msg");
    
    if (a_msg->src == -1){  // if src is base station
	sendX = maxX/2;
	sendY = maxY/2;
    } else {
	sendX = a_sensor[a_msg->src].x;
	sendY = a_sensor[a_msg->src].y;
    }
    if (a_msg->des == -1){
	receiveX = maxX/2;
	receiveY = maxY/2;
    } else {
	receiveX = a_sensor[a_msg->des].x;
	receiveY = a_sensor[a_msg->des].y;
    }
    distance = sqrt( pow((sendX-receiveX),2) + pow((sendY-receiveY),2));
    hold (distance/200000000.0);  // propagation delay
    if (a_msg->des == -1){
	send (BS_mb, (long)a_msg);
    } else {
	send (a_sensor[a_msg->des].mb,(long)a_msg);
    }
}

void sensor(int i){
    MBOX localmb;   // to exchange messages locally in a sensor
    
    create ("sensor");
    
    a_sensor[i].x = uniform(0, maxX);
    a_sensor[i].y = uniform(0, maxY);
    a_sensor[i].mb = mailbox("sensor");
    a_sensor[i].ev_fire_detection = event("sensor");
    localmb = mailbox("sensor");
    hold(1.0);
    sensor_com (i,localmb);
    sensor_detection (i,localmb);
    wait(ev_sim_done);
}

void sensor_com (int i, MBOX localmb){
    s_msg *new_msg;
    s_msg *a_msg;
    create ("sensor_com");
    while (1){
	receive (a_sensor[i].mb, (long *)&a_msg);
	// base station asks this sensor to send a packet if exists.
	free(a_msg);
	if (msg_cnt (localmb) > 0){
	    receive (localmb, (long *)&new_msg);
	} else {  
	    if ((new_msg = (s_msg *)malloc (sizeof(s_msg))) == NULL){
		fprintf (stderr, "Malloc Error - cannot create new message\n");
		exit (-1);  // cannot create new message - cannot continue
	    }
	    new_msg->src = i;
	    new_msg->type = 3;  // no data to send
	    new_msg->des = -1;
	    new_msg->time = simtime();
	    new_msg->dataSize = 0;
	}
	hold ((8.0*(sizeof(s_msg) + new_msg->dataSize))/(C * 1000));
	propagate_msg(new_msg);
    }
}

void sensor_detection (int i, MBOX localmb){
    s_msg *new_msg;
    
    create ("sensor_detection");
    
    while (1){
	queue(a_sensor[i].ev_fire_detection);
	if ((new_msg = (s_msg *)malloc(sizeof(s_msg))) == NULL){
	    fprintf (stderr, "Malloc Error - cannot create new message");
	    exit (-1);  // cannot create new message - cannot continue  
	}
	new_msg->src = i;
	new_msg->type = 2; //  there are some data to send 
	new_msg->des = -1;
	new_msg->time = simtime();
	new_msg->dataSize = uniform_int(10,100);
	send(localmb, (long)new_msg);
    } // end while
}

void Generate_fire_event(){
    double x, y;
    int test, i;
    create ("Generate_fire_event");
    hold(1.1);
    while (1){
	hold (exponential(Te));	
	generated++;
	x = uniform (0.0, maxX);
	y = uniform (0.0, maxY);
	test=0;
	for (i = 0; i < N; i++){
	    if((sqrt(pow(x-a_sensor[i].x,2.0)) + (pow(y-a_sensor[i].y,2.0))) <= SR){
		test = 1;
		set(a_sensor[i].ev_fire_detection);
	    } // end if
	}  // end for loop
	if (test == 1){
	    detected++;
	}
    }  // end while loop
}

void myReport(){
    fprintf (stdout, "\nDetected: %i\n", detected);
    fprintf (stdout, "Generated: %i\n", generated);
    fprintf (stdout, "total Delay time: %f\n", totaldelay);
    fprintf (stdout, "Total Messages Received: %i\n", totRcvMsg);
    fprintf (stdout, "Percent of events acually detected: %lf%%\n", ((double)detected/generated)*100);
    fprintf (stdout, "Average delay time: %lf\n\n\n", totaldelay/totRcvMsg);
}

