/*
 * 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 "libsysactivity.h"

char param_name[SA_DISK_NAME];
int error = 0;

void print_disk_info(struct sa_disk* hd) {
#ifdef SA_DISK_NAME
	printf("%s: ", hd->name);
#endif
#ifdef SA_DISK_READS
	printf("%"PRIu64" reads, ", hd->reads);
#endif
#ifdef SA_DISK_READS_MERGED
	printf("%"PRIu64" reads merged, ", hd->reads_merged);
#endif
#ifdef SA_DISK_SECTORS_READ
	printf("%"PRIu64" sectors read, ", hd->sectors_read);
#endif
#ifdef SA_DISK_TIME_SPENT_READING
	printf("%"PRIu64" time spent reading, ", hd->time_spent_reading);
#endif
#ifdef SA_DISK_WRITES
	printf("%"PRIu64" writes, ", hd->writes);
#endif
#ifdef SA_DISK_SECTORS_WRITTEN
	printf("%"PRIu64" sectors written, ", hd->sectors_written);
#endif
#ifdef SA_DISK_TIME_SPENT_WRITING
	printf("%"PRIu64" time spent writing, ", hd->time_spent_writing);
#endif
#ifdef SA_DISK_BYTES_READ
	printf("%"PRIu64" bytes read, ", hd->bytes_read);
#endif
#ifdef SA_DISK_BYTES_WRITTEN
	printf("%"PRIu64" bytes written", hd->bytes_written);
#endif
	printf("\n");
}

void test_disk_info(struct sa_disk* hd) {
#ifdef SA_DISK_READS
#ifdef SA_DISK_SECTORS_READ
	if (hd->sectors_read < hd->reads) {
		printf("%s:%d ERROR: The number of sectors read is lower than the reads\n", __FILE__, __LINE__);
		error = 1;
	}
#endif
	if (hd->reads != 0) {
#if defined(SA_DISK_BYTES_READ) && !defined(__DragonFly__)
		if (hd->bytes_read == 0) {
			printf("%s:%d ERROR: The number of bytes read is zero\n", __FILE__, __LINE__);
			error = 1;
		}
#endif
	}
#endif

#ifdef SA_DISK_WRITES
#ifdef SA_DISK_SECTORS_WRITTEN
	if (hd->sectors_written < hd->writes) {
		printf("%s:%d ERROR: The number of sectors written is lower than the writes\n", __FILE__, __LINE__);
		error = 1;
	}
#endif
	if (hd->writes != 0) {
#ifdef SA_DISK_TIME_SPENT_WRITING
		if (hd->time_spent_writing == 0) {
			printf("%s:%d ERROR: The time spent writing is zero\n", __FILE__, __LINE__);
			error = 1;
		}
#endif
	}
#endif
}

void* get_disk_info(void* arg) {
	int ret;
#ifdef SA_OPEN_DISK
	ret = sa_open_disk();
	if (ret != 0) {
		printf("%s:%d ERROR: sa_open_disk(): %s\n", __FILE__, __LINE__, strerror(ret));
		exit(EXIT_FAILURE);
	}
#endif

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

	struct sa_disk dev;
	if (param_name[0] == '\0')
#if defined(__linux__) // gcc -E -dM -x c /dev/null
		ret = sa_get_disk("sda", &dev);
#elif defined(__OpenBSD__) || defined(__NetBSD__)
		ret = sa_get_disk("wd0", &dev);
#elif defined(__sun__)
		ret = sa_get_disk("cmdk0", &dev);
#elif defined(__DragonFly__)
		ret = sa_get_disk("ad1", &dev);
#else
		ret = sa_get_disk("ad0", &dev);
#endif
	else
		ret = sa_get_disk(param_name, &dev);

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

	struct sa_disk* hds = NULL;
	char* ids = NULL;
	uint16_t number_disks, number_ids, written, i;
	for (i = 0; i < 6; i++) {
		ret = sa_count_disks(&number_disks);
		if (ret != 0 || number_disks == 0) {
			printf("%s:%d ERROR: sa_count_disks(): %s\n", __FILE__, __LINE__, strerror(ret));
			exit(EXIT_FAILURE);
		}
		printf("there are %d disks\n", number_disks);

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

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

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

			for (j = 0; j < number_ids; j++) {
				ret = sa_get_disk(&ids[(number_ids - j - 1) * SA_DISK_NAME], &dev);
				if (ret != 0) {
					printf("%s:%d ERROR: sa_get_disk(): %s, device: %s\n", __FILE__, __LINE__,
							strerror(ret), &ids[(number_ids - j - 1) * SA_DISK_NAME]);
					exit(EXIT_FAILURE);
				}
				print_disk_info(&dev);
				test_disk_info(&dev);
			}
		} else {
			hds = (struct sa_disk*) realloc(hds, number_disks * sizeof(struct sa_disk));
			written = 0;
			ret = sa_get_disks(hds, number_disks, &written);
			if (ret != 0 || written == 0) {
				printf("%s:%d ERROR: sa_get_disks(): %s\n", __FILE__, __LINE__, strerror(ret));
				exit(EXIT_FAILURE);
			}
			for (j = 0; j < written; j++) {
				print_disk_info(&hds[j]);
				test_disk_info(&hds[j]);
			}
		}

		printf("\n");
		sleep(1);
	}
	if (hds != NULL)
		free(hds);
#ifdef SA_CLOSE_DISK
	ret = sa_close_disk();
	if (ret != 0) {
		printf("%s:%d ERROR: sa_close_disk(): %s\n", __FILE__, __LINE__, strerror(ret));
		exit(EXIT_FAILURE);
	}
#endif
#ifdef SA_OPEN_DISK
	ret = sa_open_disk();
	if (ret != 0) {
		printf("%s:%d ERROR: sa_open_disk(): %s\n", __FILE__, __LINE__, strerror(ret));
		exit(EXIT_FAILURE);
	}
#endif
#ifdef SA_CLOSE_DISK
	ret = sa_close_disk();
	if (ret != 0) {
		printf("%s:%d ERROR: sa_close_disk(): %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_DISK_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_disk_info, NULL);
	nanosleep(&delay, NULL);
	get_disk_info(NULL);

	if (error)
		return EXIT_FAILURE;

	return EXIT_SUCCESS;
}
