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

#define ENABLE_GRAPH 1

#ifdef ENABLE_GRAPH
//#include <math.h>
#include <gd.h>
#include "gdfonts.h"
#include "gdfontt.h"
#include "gdfontl.h"
#include "gdfontl.h"
#include "gdfontmb.h"
#define POINTS 1720
#endif

#define SENSORS 4

void *Monitor(void *tid);
void *Exit(void *tid);

uint32_t No_Exit = 1;
static void *terminate_addr = NULL;

static void terminate_intr(int signo){
	if(terminate_addr)
		longjmp(terminate_addr, 1);
	exit(1);
}

static void capture_terminate(jmp_buf term_addr){
	terminate_addr = term_addr;
	signal (SIGHUP, terminate_intr);
	signal (SIGINT, terminate_intr);
	signal (SIGPIPE, terminate_intr);
	signal (SIGTERM, terminate_intr);
	signal (SIGUSR1, terminate_intr);
	signal (SIGUSR2, terminate_intr);
}

static void uncapture_terminate(void){
	terminate_addr = NULL;
	signal (SIGHUP, SIG_DFL);
	signal (SIGINT, SIG_DFL);
	signal (SIGPIPE, SIG_DFL);
	signal (SIGTERM, SIG_DFL);
	signal (SIGUSR1, SIG_DFL);
	signal (SIGUSR2, SIG_DFL);
}

inline uint32_t Read_File(const char *file){
	FILE *fp;
	//const char root_dir[] = "/sys/class/hwmon/hwmon";
	long size;
	char *buffer;
	uint32_t temp;
	if( (fp = fopen(file, "rb" )) == NULL){
		printf("%s: fopen - %s\n", file, strerror(errno) );
		printf("Error: cannot open file %s , exiting!\n", file);
		exit(1);
	}
	// obtain file size:
	fseek(fp , 0 , SEEK_END);
	size = ftell(fp);
	rewind(fp);
	// allocate memory to contain the whole file:
	if( (buffer = (char *)malloc(sizeof(char)*size)) == NULL){
	//if(buffer == NULL){
		printf("malloc error: %s\n", strerror(errno));
		exit(1);
	}
	// copy the file into the buffer:
	(void)fread(buffer, 1, size, fp);
	/* the whole file is now loaded in the memory buffer. */
	temp = atoi(buffer);
	// terminate
	fclose(fp);
	free(buffer);
//	return temp/1000;
	return temp;
}

static void Printws(uint32_t input){
	float tmp = (float)input/1000;
	if(tmp < 10)
		printf("  %.04f   ", tmp);
	else if(input < 100)
		printf(" %.04f   ", tmp);
	else
		printf("%.04f  ", tmp);
}

#ifdef ENABLE_GRAPH
void Graph(uint32_t *points, uint32_t point_cur){
	gdImagePtr im;
	//FILE *in;
	FILE *out;
	//just use vcore for now
	//const uint32_t member = 3;
	int width; // = POINTS;
	int height; // = 700;
	int white, black, green; //, red, blue, green, yellow, orange, purple, brown, grey;
	int styleDashed[8];
	//char labelstr[32];
	int x1, y1, x2, y2;
	uint32_t largest = 0;
	uint32_t smallest = INT32_MAX;

	uint32_t range = 0;
	uint32_t num1, num2;
	//uint32_t tmp1;



        //left labels
        for(num1=0; num1<POINTS; num1++){
		if(points[num1] == 0)
			break;
                if(points[num1] > largest)
                        largest = points[num1];
                if(points[num1] < smallest)
                        smallest = points[num1];
        }
	//for(num1=0; num1<POINTS; num1++){
	//	if(points[num1] == 0)
	//	smallest

	//}
        //measure to 3 decimal places
        //1.4000
        range = largest - smallest;
        //height = ceil((float)range/1000)+200;
        height = range*10 + 200;
	width = POINTS + 200;

	im = gdImageCreate(width, height);

	white = gdImageColorAllocate(im, 255, 255, 255);
	black = gdImageColorAllocate(im, 0, 0, 0);
	green = gdImageColorAllocate(im, 0, 255, 0);
/*
	red = gdImageColorAllocate(im, 255, 0, 0);
	blue = gdImageColorAllocate(im, 0, 0, 255);
	green = gdImageColorAllocate(im, 0, 255, 0);
	yellow = gdImageColorAllocate(im, 255, 255, 0);
	orange = gdImageColorAllocate(im, 255, 128, 0);
	purple = gdImageColorAllocate(im, 255, 0, 255);
	brown = gdImageColorAllocate(im, 128, 64, 0);
	grey = gdImageColorAllocate(im, 128, 128, 128);
	styleDashed[0] = gdTransparent;
	styleDashed[1] = black;
	styleDashed[2] = gdTransparent;
	styleDashed[3] = black;
	styleDashed[4] = gdTransparent;
	styleDashed[5] = black;
	styleDashed[6] = gdTransparent;
	styleDashed[7] = black;
	gdImageSetStyle(im, styleDashed, 8);
*/
	//gdImageLine(im, 100, 100, 100, height-100, black);
	//gdImageLine(im, 100, height-100, width-100, height-100, black);
	//gdImageLine(im, width-100, 100, width-100, height-100, black);
	//gdImageString(im, gdFontMediumBold, 5, 5, (unsigned char *)"gflops", blue);

	//just graph the vcore for now


	gdImageLine(im, 100, 100, 100, height-100, black);
	gdImageLine(im, 100, height-100, width-100, height-100, black);

	//each pixel is the smallest increment starting from smallest
	x1 = width-100;
	y1 = 100;
	num2 = point_cur;
	for(num1=0; num1<POINTS; num1++){
		x2 = width-num1-100;
		y2 = (largest-points[num2])*10+100;
		gdImageLine(im, (int)x1, (int)y1, (int)x2, (int)y2, green);

		//points[member][num2]


		if(num2 == 0)
			num2 = POINTS-1;
		else
			num2--;
		x1 = x2;
		y1 = y2;

		if(points[num2] == 0)
			break;
	}

	out = fopen("lsmon.png", "w");
	gdImageInterlace(im, 1);
	gdImagePng(im, out);
	fclose(out);
	gdImageDestroy(im);
}
#endif

int main(int argc, char **argv){
	jmp_buf terminate_env;
	pthread_t Exit_thread_ptr, Monitor_thread_ptr;
//	uint16_t tid = 0;

        if(setjmp(terminate_env)){
                signal(SIGALRM, SIG_IGN);
                fputs("\nInterrupt caught, Exiting\n", stderr);
                exit(1);
        }
        capture_terminate(terminate_env);
        printf("Press q to exit\n");

	//threads
	if(pthread_create(&Exit_thread_ptr, NULL, Exit, NULL) != 0){
		printf("Error: pthread_create failed for Exit thread !\n");
		printf("\tExiting!\n");
		uncapture_terminate();
		exit(1);
	}
	if(pthread_create(&Monitor_thread_ptr, NULL, Monitor, NULL) != 0){
		printf("Error: pthread_create failed for Monitor thread !\n");
		printf("\tExiting!\n");
		uncapture_terminate();
		exit(1);
	}

	if(pthread_join(Exit_thread_ptr, NULL) != 0){
		printf("Error: pthread_join failed for Exit thread !\n");
		printf("\tExiting!\n");
		uncapture_terminate();
		exit(1);
	}

	if(pthread_join(Monitor_thread_ptr, NULL) != 0){
		printf("Error: pthread_join failed for Monitor thread !\n");
		printf("\tExiting!\n");
		uncapture_terminate();
		exit(1);
	}

	uncapture_terminate();
	return 0;
}

void *Exit(void *tid){
	system("stty raw");
	//while(1)
	//	if(getchar() == 'q')
	//		break;
	while(getchar() != 'q')
		continue;
	system("stty cooked");
	No_Exit = 0;
	pthread_exit(NULL);
}


void *Monitor(void *tid){
//	const uint16_t t = *(uint16_t *)tid;
	//jmp_buf terminate_env;
	//microseconds
	const uint32_t poll = 500000;
	uint32_t reads = 0;
	uint32_t num1 = 0;

	uint32_t tmp1 = 0;

//	const char temp_itfch[]  = "/sys/class/hwmon/hwmon2/device/temp1_input";
//	const char temp_itvcore[]  = "/sys/class/hwmon/hwmon2/device/in0_input";
	//const char temp_itvcore[]  = "/sys/class/hwmon/hwmon2/device/temp2_input";
	//const char temp_k10[]    = "/sys/devices/pci0000:00/0000:00:18.3/hwmon/hwmon0/temp1_input";
//	const char temp_k10[]    = "/sys/class/hwmon/hwmon0/temp1_input";
//	const char temp_radeon[] = "/sys/class/hwmon/hwmon1/temp1_input";


	//const char files[4][] = {"2/device/temp1_input", "2/device/in0_input", "0/temp1_input", "1/temp1_input"};
	//const char root_dir[] = "/sys/class/hwmon/hwmon";
	const char *files[SENSORS] = {"/sys/class/hwmon/hwmon0/temp1_input", "/sys/class/hwmon/hwmon1/temp1_input", 
		"/sys/class/hwmon/hwmon2/device/temp1_input", "/sys/class/hwmon/hwmon2/device/in0_input"};
	const char *names[SENSORS] = {"k10temp ", "radeon  ", "fch     ", "vcore   "};

	uint32_t sums[SENSORS] = {0, 0, 0, 0};
	uint32_t maxes[SENSORS] = {0, 0, 0, 0};
	uint32_t mins[SENSORS] = {INT32_MAX, INT32_MAX, INT32_MAX, INT32_MAX};
#ifdef ENABLE_GRAPH
	uint32_t points[SENSORS][POINTS];
	uint32_t point_cur = POINTS-1;
	(void)memset((void *)points, '\0', sizeof(uint32_t)*SENSORS*POINTS);
#endif
//	float itfch_sum = 0;
//	float itvcore_sum = 0;
//	float k10_sum = 0;
//	float radeon_sum = 0;

//	float itfch_min = INT32_MAX;
//	float itvcore_min = INT32_MAX;
//	float k10_min = INT32_MAX;
//	float radeon_min = INT32_MAX;

//	float itfch_avg = 0;
//	float itvcore_avg = 0;
//	float k10_avg = 0;
//	float radeon_avg = 0;

//	float itfch_max = 0;
//	float itvcore_max = 0;
//	float k10_max = 0;
//	float radeon_max = 0;

//	uint32_t sums = 0;

//	float tmp1 = 0;
/*
	if(setjmp(terminate_env)){
		signal(SIGALRM, SIG_IGN);
		fputs("\nInterrupt caught, Exiting\n", stderr);
/
		//print min/avg/max
		printf("read %u times\n", sums);
		printf("Name    Min    Avg    Max");
		printf("itfch   %.02f  %.02f  %.02f\n", itfch_min, itfch_sum/sums, itfch_max);
		printf("itvcore   %.02f  %.02f  %.02f\n", itvcore_min, itvcore_sum/sums, itvcore_max);
		printf("k10     %.02f  %.02f  %.02f\n", k10_min, k10_sum/sums, k10_max);
		printf("radeon  %.02f  %.02f  %.02f\n", radeon_min, radeon_sum/sums, radeon_max);
/
		exit(1);
	}
	capture_terminate(terminate_env);
*/
	//printf("Press q to exit\n");
//	system("stty raw");
	while(No_Exit){
#ifdef ENABLE_GRAPH
                if(point_cur == POINTS-1)
                        point_cur = 0;
                else
                        point_cur++;
#endif

		for(num1=0; num1<SENSORS; num1++){
			tmp1 = Read_File(files[num1]);
			sums[num1] += tmp1;
			if(tmp1 > maxes[num1])
				maxes[num1] = tmp1;
			if(tmp1 < mins[num1])
				mins[num1] = tmp1;
#ifdef ENABLE_GRAPH
			points[num1][point_cur] = tmp1;
#endif
		}
/*
		tmp1 = Read_File(temp_itfch);
		itfch_sum += tmp1;
		if(tmp1 > itfch_max)
			itfch_max = tmp1;
		if(tmp1 < itfch_min)
			itfch_min = tmp1;


		tmp1 = Read_File(temp_itvcore);
		itvcore_sum += tmp1;
		if(tmp1 > itvcore_max)
			itvcore_max = tmp1;
		if(tmp1 < itvcore_min)
			itvcore_min = tmp1;

		tmp1 = Read_File(temp_k10);
		k10_sum += tmp1;
		if(tmp1 > k10_max)
			k10_max = tmp1;
		if(tmp1 < k10_min)
			k10_min = tmp1;

		tmp1 = Read_File(temp_radeon);
		radeon_sum += tmp1;
		if(tmp1 > radeon_max)
			radeon_max = tmp1;
		if(tmp1 < radeon_min)
			radeon_min = tmp1;
		sums++;
		//sleep(poll);
		//usleep(500000);
*/
		reads++;
		usleep(poll);

//		if(getchar() == 'q')
//			break;
	}
//	system("stty cooked");

	printf("\nread %u times\n", reads);
	printf("Name         Min        Avg        Max\n");

	for(num1=0; num1<SENSORS; num1++){
		//printf("%s  %.04f  %.04f  %.04f\n", names[num1], mins[num1], sums[num1]/reads, maxes[num1]);
		printf("%s  ", names[num1]);
		Printws(mins[num1]);
		Printws(sums[num1]/reads);
		Printws(maxes[num1]);
		printf("\n");
/*
tmp1 = mins[num1];
if(tmp1 < 10)
printf("%.04f     ", tmp1);
else if(tmp1 < 100)
printf("%.04f    ", tmp1);
else
printf("%.04f  ", tmp1);
*/

	}

#ifdef ENABLE_GRAPH
	//generate graph
	Graph(points[3], point_cur);
#endif
/*
	printf("itfch   %.02f  %.02f  %.02f\n", itfch_min, itfch_sum/sums, itfch_max);
	printf("itvcore   %.04f  %.04f  %.04f\n", itvcore_min, itvcore_sum/sums, itvcore_max);
	printf("k10     %.02f  %.02f  %.02f\n", k10_min, k10_sum/sums, k10_max);
	printf("radeon  %.02f  %.02f  %.02f\n", radeon_min, radeon_sum/sums, radeon_max);
*/
	//uncapture_terminate();
//	return 0;
	pthread_exit(NULL);
}
