/*
 * libsysactivity
 * http://sourceforge.net/projects/libsysactivity/
 * Copyright (c) 2009, 2010 Carlos Olmedo Escobar <carlos.olmedo.e@gmail.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include <errno.h>

#include "libsysactivity.h"

char param_name[SA_NET_INTERFACE_NAME];
int error = 0;

void print_net_info(struct sa_net_interface* dev) {
#ifdef SA_NET_INTERFACE_NAME
	printf("%s\n received: ", dev->name);
#endif
#ifdef SA_NET_INTERFACE_RECEIVED_BYTES
	printf("%"PRIu64" bytes, ", dev->received_bytes);
#endif
#ifdef SA_NET_INTERFACE_RECEIVED_PACKETS
	printf("%"PRIu64" packets, ", dev->received_packets);
#endif
#ifdef SA_NET_INTERFACE_RECEIVED_ERRORS
	printf("%"PRIu64" errors, ", dev->received_errors);
#endif
#ifdef SA_NET_INTERFACE_RECEIVED_DROP
	printf("%"PRIu64" dropped, ", dev->received_drop);
#endif
#ifdef SA_NET_INTERFACE_RECEIVED_FIFO
	printf("%"PRIu64" fifo, ", dev->received_fifo);
#endif
#ifdef SA_NET_INTERFACE_RECEIVED_MULTICAST
	printf("%"PRIu64" multicast, ", dev->received_multicast);
#endif
	printf("\n sent: ");
#ifdef SA_NET_INTERFACE_SENT_BYTES
	printf("%"PRIu64" bytes, ", dev->sent_bytes);
#endif
#ifdef SA_NET_INTERFACE_SENT_PACKETS
	printf("%"PRIu64" packets, ", dev->sent_packets);
#endif
#ifdef SA_NET_INTERFACE_SENT_ERRORS
	printf("%"PRIu64" errors, ", dev->sent_errors);
#endif
#ifdef SA_NET_INTERFACE_SENT_DROP
	printf("%"PRIu64" dropped, ", dev->sent_drop);
#endif
#ifdef SA_NET_INTERFACE_SENT_FIFO
	printf("%"PRIu64" fifo, ", dev->sent_fifo);
#endif
#ifdef SA_NET_INTERFACE_SENT_MULTICAST
	printf("%"PRIu64" multicast, ", dev->sent_multicast);
#endif
	printf("\n");
}

void test_net_info(struct sa_net_interface* dev) {
#ifdef SA_NET_INTERFACE_NAME
#ifdef __NetBSD__
	size_t len = strlen(dev->name);
#else
	size_t len = strnlen(dev->name, SA_NET_INTERFACE_NAME);
#endif
	if (len == 0) {
		printf("%s:%d ERROR: The length of the device name is zero\n", __FILE__, __LINE__);
		error = 1;
	}
#endif
}

void* get_net_info(void* arg) {
	int ret;
	struct sa_net_interface* interfaces = NULL;
#ifdef SA_OPEN_NET
	ret = sa_open_net();
	if (ret != 0) {
		printf("%s:%d ERROR: sa_open_net(): %s\n", __FILE__, __LINE__, strerror(ret));
		exit(EXIT_FAILURE);
	}
#endif

	ret = sa_reset_net_interfaces();
	if (ret != 0) {
		printf("%s:%d ERROR: sa_reset_net_interfaces(): %s\n", __FILE__, __LINE__, strerror(ret));
		exit(EXIT_FAILURE);
	}

	struct sa_net_interface dev;
	if (param_name[0] == '\0')
#ifdef __linux__ // gcc -E -dM -x c /dev/null
		ret = sa_get_net_interface("lo", &dev);
#else
		ret = sa_get_net_interface("lo0", &dev);
#endif
	else
		ret = sa_get_net_interface(param_name, &dev);

	if (ret != 0) {
		printf("%s:%d ERROR: sa_get_net_interface(): %s\n", __FILE__, __LINE__, strerror(ret));
		exit(EXIT_FAILURE);
	}
	print_net_info(&dev);
	printf("\n");
	test_net_info(&dev);

	int i;
	uint16_t number_interfaces, number_ids, written, j;
	char* ids = NULL;
	for (i = 0; i < 6; i++) {
		ret = sa_count_net_interfaces(&number_interfaces);
		if (ret != 0 || number_interfaces < 2) {
			printf("%s:%d ERROR: sa_count_net_interfaces(): %s\n", __FILE__, __LINE__,
					strerror(ret));
			exit(EXIT_FAILURE);
		}
		printf("There are %d network interfaces\n", number_interfaces);

		ret = sa_reset_net_interfaces();
		if (ret != 0) {
			printf("%s:%d ERROR: sa_reset_net_interfaces(): %s\n", __FILE__, __LINE__,
					strerror(ret));
			exit(EXIT_FAILURE);
		}
		if (number_interfaces == 0)
			continue;

		if (i % 2 == 0) {
			ids = realloc(ids, number_interfaces * SA_NET_INTERFACE_NAME);
			ret = sa_get_net_interfaces_ids(ids, number_interfaces, &number_ids);
			if (ret != 0) {
				printf("%s:%d ERROR: sa_get_net_interfaces_ids(): %s\n", __FILE__, __LINE__,
						strerror(ret));
				exit(EXIT_FAILURE);
			}

			ret = sa_reset_net_interfaces();
			if (ret != 0) {
				printf("%s:%d ERROR: sa_reset_net_interfaces(): %s\n", __FILE__, __LINE__,
						strerror(ret));
				exit(EXIT_FAILURE);
			}

			for (j = 0; j < number_ids; j++) {
				ret = sa_get_net_interface(&ids[(number_ids - j - 1) * SA_NET_INTERFACE_NAME], &dev);
				if (ret != 0) {
					printf("%s:%d ERROR: sa_get_net_interface(): %s, device: %s\n", __FILE__,
							__LINE__, strerror(ret), &ids[(number_ids - j - 1) * SA_NET_INTERFACE_NAME]);
					exit(EXIT_FAILURE);
				}
				print_net_info(&dev);
				test_net_info(&dev);
			}
		} else {
			interfaces = (struct sa_net_interface*) realloc(interfaces, number_interfaces
					* sizeof(struct sa_net_interface));
			written = 0;
			ret = sa_get_net_interfaces(interfaces, number_interfaces, &written);
			if (ret != 0 || written < 2) {
				printf("%s:%d ERROR: sa_get_net_interfaces(): %s\n", __FILE__, __LINE__, strerror(
						ret));
				exit(EXIT_FAILURE);
			}
			for (j = 0; j < written; j++) {
				print_net_info(&interfaces[j]);
				test_net_info(&interfaces[j]);
			}
		}

		printf("\n");
		sleep(1);
	}
	if (interfaces != NULL)
		free(interfaces);
#ifdef SA_CLOSE_NET
	ret = sa_close_net();
	if (ret != 0) {
		printf("%s:%d ERROR: sa_close_net(): %s\n", __FILE__, __LINE__, strerror(ret));
		exit(EXIT_FAILURE);
	}
#endif
#ifdef SA_OPEN_NET
	ret = sa_open_net();
	if (ret != 0) {
		printf("%s:%d ERROR: sa_open_net(): %s\n", __FILE__, __LINE__, strerror(ret));
		exit(EXIT_FAILURE);
	}
#endif
#ifdef SA_CLOSE_NET
	ret = sa_close_net();
	if (ret != 0) {
		printf("%s:%d ERROR: sa_close_net(): %s\n", __FILE__, __LINE__, strerror(ret));
		exit(EXIT_FAILURE);
	}
#endif
	return NULL;
}

int main(int argc, char *argv[]) {
	if (argc > 1)
		strncpy(param_name, argv[1], SA_NET_INTERFACE_NAME);
	else
		param_name[0] = '\0';

	pthread_t thread1;
	struct timespec delay;
	delay.tv_sec = 0;
	delay.tv_nsec = 500000000;

	pthread_create(&thread1, NULL, get_net_info, NULL);
	nanosleep(&delay, NULL);
	get_net_info(NULL);

	if (error)
		return EXIT_FAILURE;

	return EXIT_SUCCESS;
}
