/*
 * libsysactivity
 * http://sourceforge.net/projects/libsysactivity/
 * Copyright (c) 2009-2011 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 <stdlib.h>
#include <string.h>
#include <math.h>

#include "libsysactivity.h"

int error = 0;

void print_cpu_info(struct sa_cpu* cpu) {
#ifdef SA_CPU_USER
	printf("user: %f", cpu->user);
#endif
#ifdef SA_CPU_NICE
	printf(", nice: %f", cpu->nice);
#endif
#ifdef SA_CPU_SYSTEM
	printf(", system: %f", cpu->system);
#endif
#ifdef SA_CPU_IDLE
	printf(", idle: %f", cpu->idle);
#endif
#ifdef SA_CPU_WAITING_FOR_IO
	printf(", waiting_for_io: %f", cpu->waiting_for_io);
#endif
#ifdef SA_CPU_HARDWARE_IRQ
	printf(", hardware_irq: %f", cpu->hardware_irq);
#endif
#ifdef SA_CPU_SOFTWARE_IRQ
	printf(", software_irq: %f", cpu->software_irq);
#endif
#ifdef SA_CPU_STOLEN
	printf(", stolen: %f", cpu->stolen);
#endif
#ifdef SA_CPU_INTR
	printf(", intr: %f", cpu->intr);
#endif
	printf("\n");
}

void check_range(float cpu_time) {
	cpu_time = roundf(cpu_time);
	if (cpu_time > 100.0 || cpu_time < 0.0) {
		printf("%s:%d ERROR: cpu time out of range: %f\n", __FILE__, __LINE__, cpu_time);
		error = 1;
	}
}

void test_cpu_info(struct sa_cpu* cpu) {
	float sum = 0;
#ifdef SA_CPU_USER
	check_range(cpu->user);
	sum += cpu->user;
#endif
#ifdef SA_CPU_NICE
	check_range(cpu->nice);
	sum += cpu->nice;
#endif
#ifdef SA_CPU_SYSTEM
	check_range(cpu->system);
	sum += cpu->system;
#endif
#ifdef SA_CPU_IDLE
	check_range(cpu->idle);
	sum += cpu->idle;
#endif
#ifdef SA_CPU_WAITING_FOR_IO
	check_range(cpu->waiting_for_io);
	sum += cpu->waiting_for_io;
#endif
#ifdef SA_CPU_HARDWARE_IRQ
	check_range(cpu->hardware_irq);
	sum += cpu->hardware_irq;
#endif
#ifdef SA_CPU_SOFTWARE_IRQ
	check_range(cpu->software_irq);
	sum += cpu->software_irq;
#endif
#ifdef SA_CPU_STOLEN
	check_range(cpu->stolen);
	sum += cpu->stolen;
#endif
#ifdef SA_CPU_INTR
	check_range(cpu->intr);
	sum += cpu->intr;
#endif

	check_range(sum);
	if (sum < 90.0) // Sometimes it happens: user: 0.000000, nice: 0.000000, system: 0.000000, idle: 0.000000, waiting_for_io: 0.000000, hardware_irq: 0.000000, software_irq: 0.000000, stolen: 0.000000
		printf("%s:%d WARNING: Summatory of all cpu times is lower than 90.0: %f\n", __FILE__, __LINE__, sum);
}

void* get_cpu_info(void* arg) {
	int i;
	int j;
	uint16_t number_cpus;
	uint16_t written;
	int ret;

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

	ret = sa_count_cpus(&number_cpus);
	if (ret != 0 || number_cpus == 0) {
		printf("%s:%d ERROR: sa_count_cpus(): %s\n", __FILE__, __LINE__, strerror(ret));
		exit(EXIT_FAILURE);
	}
	printf("Number of cpus: %u\n", number_cpus);

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

	struct sa_cpu* cpus = malloc(number_cpus * sizeof(struct sa_cpu));
	ret = sa_get_cpu(0, cpus);
	if (ret != 0) {
		printf("%s:%d ERROR: sa_get_cpu(): %s\n", __FILE__, __LINE__, strerror(ret));
		exit(EXIT_FAILURE);
	}
	print_cpu_info(cpus);
	printf("\n");

	sleep(1);
	for (i = 0; i < 6; i++) {
		ret = sa_reset_cpus();
		if (ret != 0) {
			printf("%s:%d ERROR: sa_reset_cpus(): %s\n", __FILE__, __LINE__, strerror(ret));
			exit(EXIT_FAILURE);
		}

		written = 0;
		ret = sa_get_cpus(cpus, number_cpus, &written);
		if (ret != 0 || written == 0) {
			printf("%s:%d ERROR: sa_get_cpus(): %s\n", __FILE__, __LINE__, strerror(ret));
			exit(EXIT_FAILURE);
		}
		for (j = 0; j < written; j++) {
			printf("cpu index: %d\n", cpus[j].id);
			print_cpu_info(&cpus[j]);
			if (i > 0)
				test_cpu_info(&cpus[j]);
		}
		printf("\n");
		sleep(1);
	}
	free(cpus);

#ifdef SA_CLOSE_CPU
	ret = sa_close_cpu();
	if (ret != 0) {
		printf("%s:%d ERROR: sa_close_cpu(): %s\n", __FILE__, __LINE__, strerror(ret));
		exit(EXIT_FAILURE);
	}
#endif
#ifdef SA_OPEN_CPU
	ret = sa_open_cpu();
	if (ret != 0) {
		printf("%s:%d ERROR: sa_open_cpu(): %s\n", __FILE__, __LINE__, strerror(ret));
		exit(EXIT_FAILURE);
	}
#endif
#ifdef SA_CLOSE_CPU
	ret = sa_close_cpu();
	if (ret != 0) {
		printf("%s:%d ERROR: sa_close_cpu(): %s\n", __FILE__, __LINE__, strerror(ret));
		exit(EXIT_FAILURE);
	}
#endif
	return NULL;
}

int main() {
	pthread_t thread1;
	struct timespec delay;
	delay.tv_sec = 0;
	delay.tv_nsec = 500000000;

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

	if (error)
		return EXIT_FAILURE;

	return EXIT_SUCCESS;
}
