#define _POSIX_C_SOURCE 199309L

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <time.h>
#include <string.h>
#include <getopt.h>
#include "web100.h"
#include "web100-int.h"

#define VERSION		"New Trace Daemon (Version 1.0)"
#define CONFIGFILE	"ntd.conf"

struct ConDetails {
	unsigned int	LocalPort;
	char		LocalAddress[strlen("255.255.255.255") + 3];
	unsigned int	RemotePort;
	char		RemoteAddress[strlen("255.255.255.255") + 3];
	int		varCount;
	char		*varName[256];
} cd;

/* processConfFile reads in the given configuration file
 * and writes the values to the global conDetails struct. 
 * This is the only function that manipulates the struct */
void processConfFile();

/* setUpConnection sets up the web100_connection for
 * the given IP addresses and ports.
 * Currently the IP addresses and ports are defined globally. */
void setUpConnection(web100_connection **, web100_agent **);

/* prints out the usage information for this program */
void usage(char *);

int main(int argc, char *argv[])
{
	web100_agent		*agent;
	web100_connection	*conn;
	web100_log		*log = NULL;
	web100_group		*group = NULL;
	web100_snapshot		*snap = NULL;
	char 	buf[8], *outputfilename = NULL;
	int	cid = -1, i = 0, j = 0;
	int	option, option_index = 0, outputFlag = 0; 
	int	time = 10;	/* default measurement time is 10 seconds */
	double	currtime = 0.0;
	/* interval_size specifies the step size of each measurement interval 
	* and corresponds to [milliseconds]. Therefore a value of 100 
	* means 100 ms (= 0.1 s) */
	int	interval_size = 10;
	struct	timespec req;
	req.tv_sec = 0;
	req.tv_nsec = interval_size * 1000 * 1000; /* 0.1 sec */
	struct	option long_options[] = {
		{ "time",    1, NULL, 't' },
		{ "output",  1, NULL, 'o' },
		{ "help",    0, NULL, 'h' },
		{ "version", 0, NULL, 'v' },
		{ NULL,      0, NULL,  0  }
	};

	/* parse command line options */
	while(1) {
		option = getopt_long(argc, argv, "t:o:hv0", long_options, &option_index);
		if(option == EOF)
			break;

		switch(option) {
			case 't':
				time = atoi(optarg);
				break;
			case 'o':
				if((outputfilename = malloc(strlen(optarg) + 1)) == NULL) {
					fprintf(stderr, "Not enough memory.\n");
					exit(1);
				}
				strncpy(outputfilename, optarg, strlen(optarg));
				if(strncmp(outputfilename, "log.txt", 7) == 0) {
					fprintf(stderr, "Please use a different filename.\n");
					exit(1);
				}
				/* set flag that indicates that we have an output file name */
				outputFlag = 1;
				break;
			case 'h':
				usage(argv[0]);
				exit(0);
			case 'v':
				printf("%s\n", VERSION);
				exit(0);
			default:
				fprintf(stderr, " ... not a valid option '%c'\n", option);
				break;
		}
	}

	if((outputFlag == 1) && (freopen(outputfilename, "w", stdout) != stdout)) {
		fprintf(stderr, "Error with freopen and stdout.\n");
		exit(1);
	}

	/* start processing the configuration file */
	processConfFile();
	if((agent = web100_attach(WEB100_AGENT_TYPE_LOCAL, NULL)) == NULL) {
		web100_perror("web100_attach");
		exit(EXIT_FAILURE);
	}

	setUpConnection(&conn, &agent);
	
	cid = web100_get_connection_cid(conn);
	printf("Connection ID: %i\n", cid);
	
	group = web100_group_find(agent, "read");
	snap  = web100_snapshot_alloc(group, conn);
	
	log   = web100_log_open_write("log.txt", conn, group);

	for(i = 0; i < (time * 1000 / interval_size); ++i) {
		web100_snap(snap);
		web100_log_write(log, snap);
		nanosleep(&req, NULL);
	}
	web100_log_close_write(log);
	web100_snapshot_free(snap);

	snap = NULL;
	log = NULL;
	
	/*
	 * Now post process
	 */
	log = web100_log_open_read("log.txt");
	agent = web100_get_log_agent(log);
	group = web100_get_log_group(log);
	conn = web100_get_log_connection(log);
	snap = web100_snapshot_alloc(group, conn);

	/* create and initiliaze an array of web100_vars */
	web100_var	*var[cd.varCount];
	for(i = 0; i < cd.varCount; ++i) {
		var[i] = web100_var_find(group, cd.varName[i]);
	}

	/* print the headlines (each var's name per column) */
	printf("\nTime\t");
	for(i = 0; i < cd.varCount; ++i)
		printf("%s\t", var[i]->name);
	printf("\n");

	/* print the var's values for each time step */
	for(i = 0; i < (time * 1000 / interval_size); ++i) {
		printf("%6.2f\t", currtime);
		web100_snap_from_log(snap, log);
		for(j = 0; j < cd.varCount; ++j) {
			web100_snap_read(var[j], snap, &buf);
			printf("%s\t", web100_value_to_text(WEB100_TYPE_COUNTER32, &buf));
		}
		printf("\n");
		/* Increase currtime by the defined time step size */
		currtime += (req.tv_nsec / 1000000000.);
	}
	
	web100_log_close_read(log);
	
	return 0;
}

void setUpConnection(web100_connection **conn, web100_agent **agent) 
{
	struct 	web100_connection_spec 	*spec, *spec2;
	struct	in_addr src_t, dst_t;
	
	spec  = (struct web100_connection_spec*) malloc(sizeof(struct web100_connection_spec));
	spec2 = (struct web100_connection_spec*) malloc(sizeof(struct web100_connection_spec));

	printf("LocalAddress: %s\n", cd.LocalAddress);
	printf("RemoteAddress: %s\n", cd.RemoteAddress);
	printf("LocalPort: %u\n", cd.LocalPort);
	printf("RemotePort: %u\n", cd.RemotePort);
	inet_pton(AF_INET, cd.LocalAddress, &src_t);
	spec->src_addr = src_t.s_addr;
	inet_pton(AF_INET, cd.RemoteAddress, &dst_t);
	spec->dst_addr = dst_t.s_addr;
	spec->src_port = cd.LocalPort;
	spec->dst_port = cd.RemotePort;

	*conn = web100_connection_head(*agent);

	while(*conn) {
		web100_get_connection_spec(*conn, spec2);
		if (spec->src_addr == spec2->src_addr && 
		    spec->dst_addr == spec2->dst_addr && 
		    spec->dst_port == spec2->dst_port)
			break;
		*conn = web100_connection_next(*conn);
	}

	/* No connection? Then exit the programm immediately */
	if(*conn == NULL) {
		fprintf(stderr, "connection not found\n");
		exit(1);
	}
	
	free(spec);
	free(spec2);
}

void processConfFile() 
{
	FILE *cf;
	char line[80], *temp;
	
	if(!(cf = fopen(CONFIGFILE, "r"))) {
		fprintf(stderr, "%s\n", "processConfFile: ntd.conf File Open Error");
		exit(1);
	}

	/* first of all: the connection
	 * We ignore comments that start with a '#' sign
	 * as well as blank lines (only \n) */
	while(!feof(cf)) {
		if(fgets(line, sizeof(line), cf) == NULL) {
			if(ferror(cf))
				perror("File read error!\n");
		}
		if(line[0] == '#' || line[0] == '\n')
			continue;
		if((strncmp(line, "LocalAddress", 12)) == 0) {
			temp  = strtok(line, " \t\n");
			char *LocalAddress = strtok(NULL, " \t\n");
			strncpy(cd.LocalAddress, LocalAddress, strlen(LocalAddress));
		}
		else if((strncmp(line, "RemoteAddress", 13)) == 0) {
			temp  = strtok(line, " \t\n");
			char *RemoteAddress = strtok(NULL, " \t\n");
			strncpy(cd.RemoteAddress, RemoteAddress, strlen(RemoteAddress));
		}
		else if((strncmp(line, "LocalPort", 9)) == 0) {
			temp = strtok(line, " \t\n");
			temp = strtok(NULL, " \t\n");
			cd.LocalPort = atoi(temp);
		}
		else if((strncmp(line, "RemotePort", 10)) == 0) {
			temp = strtok(line, " \t\n");
			temp = strtok(NULL, " \t\n");
			cd.RemotePort = atoi(temp);
		}
		/* fill the ConDetails struct with the varNames */
		else if(line[0] == 'v' && line[1] == ' ') {
			temp = strtok(line, " \t\n");
			temp = strtok(NULL, " \t\n");
			cd.varName[cd.varCount] = malloc(strlen(temp) + 1);
			if(cd.varName[cd.varCount] == NULL) {
				printf("SegFault -- not enough memory\n");
				exit(1);
			} else
				strncpy(cd.varName[cd.varCount], temp, strlen(temp) + 1);
			cd.varCount++;
		}
	}
}

void usage(char *progname)
{
	fprintf(stderr,
		"Usage: %s [options]\n"
		"  -t, --time <seconds>     Run the daemon for specified seconds\n"
		"  -o, --output <filename>  Write Data to specified file (standard is stdout)\n"
		"  -h, --help               Prints this information and exits\n"
		"  -v, --version            Prints Version of the program and exits\n\n",
		progname);
}
