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



/*
# Configuration file generated by pwmconfig, changes will be lost
INTERVAL=3
DEVPATH=hwmon2=devices/platform/it87.656 hwmon0=devices/pci0000:00/0000:00:18.3
DEVNAME=hwmon2=it8728 hwmon0=k10temp
FCTEMPS=hwmon2/device/pwm2=hwmon2/device/temp1_input hwmon2/device/pwm1=hwmon0/temp1_input
FCFANS=hwmon2/device/pwm2=hwmon2/device/fan3_input+hwmon2/device/fan2_input hwmon2/device/pwm1=hwmon2/device/fan4_input+hwmon2/device/fan1_input
MINTEMP=hwmon2/device/pwm2=28 hwmon2/device/pwm1=8
MAXTEMP=hwmon2/device/pwm2=46 hwmon2/device/pwm1=26
MINSTART=hwmon2/device/pwm2=85 hwmon2/device/pwm1=85
MINSTOP=hwmon2/device/pwm2=65 hwmon2/device/pwm1=56
MINPWM=hwmon2/device/pwm2=65  hwmon2/device/pwm1=56
MAXPWM=hwmon2/device/pwm2=255 hwmon2/device/pwm1=255
*/

static void *terminate_addr = NULL;

struct PWM_data{
	uint32_t pwm_max_step;
	uint32_t pwm_cur;
	uint32_t pwm_max;
	uint32_t pwm_min;
	uint32_t temp_cur;
	uint32_t temp_min;
	uint32_t temp_max;
};


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);
}

void Write_File(const char *file, uint32_t num1){
FILE *fp;
if( (fp = fopen(file, "w" )) == NULL){
printf("%s: fopen - %s\n", file, strerror(errno) );
printf("Error: cannot open file %s , exiting!\n", file);
exit(1);
}
if(fprintf(fp, "%u", num1) < 0){
printf("%s: fprintf - %s\n", file, strerror(errno) );
printf("Error: cannot write to file %s , exiting!\n", file);
exit(1);
}

fclose(fp);

}

uint32_t Read_File(const char *file){
	FILE *fp;
	long size;
	char *buffer;
	//size_t result;
	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:
buffer = (char *)malloc(sizeof(char)*size);
if(buffer == NULL){
printf("malloc error: %s\n", strerror(errno));
exit(1);
}

// copy the file into the buffer:
(void)fread(buffer, 1, size, fp);
//if(result != size){
//printf("fread error: %u %u %s\n", result, size, strerror(errno));
//exit(1);
//}

/* the whole file is now loaded in the memory buffer. */
temp = atoi(buffer);

// terminate
fclose(fp);
free(buffer);
return temp;
}

int main(int argc, char **argv){
/*
	it87 fch /sys/class/hwmon/hwmon2/device/temp1_input
	it87 cpu /sys/class/hwmon/hwmon2/device/temp2_input
	k10 /sys/devices/pci0000\:00/0000\:00\:18.3/hwmon/hwmon0/temp1_input
	radeon /sys/class/hwmon/hwmon1/temp1_input

	const char fan_cpu1[] = "/sys/class/hwmon/hwmon2/device/fan1_input";
	const char fan_cpu2[] = "/sys/class/hwmon/hwmon2/device/fan4_input";
	const char fan_sys1[] = "/sys/class/hwmon/hwmon2/device/fan3_input";
	const char fan_sys2[] = "/sys/class/hwmon/hwmon2/device/fan3_input";

*/
	// 1000x
//	FILE *fp;
	jmp_buf terminate_env;
	const char temp_itfch[]  = "/sys/class/hwmon/hwmon2/device/temp1_input";
	const char temp_itcpu[]  = "/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_radeon[] = "/sys/class/hwmon/hwmon1/temp1_input";

	const char pwm_1[] = "/sys/class/hwmon/hwmon2/device/pwm2";
	const char pwm_2[] = "/sys/class/hwmon/hwmon2/device/pwm1";
	const char pwmenable_1[] = "/sys/class/hwmon/hwmon2/device/pwm1_enable";
	const char pwmenable_2[] = "/sys/class/hwmon/hwmon2/device/pwm2_enable";

	const uint32_t poll = 2;

	struct PWM_data pwm_data_1;
	struct PWM_data pwm_data_2;
	float tmp1, tmp2;
//	char buf1[16];

	//populate structs
	pwm_data_1.pwm_max_step = 20;
	pwm_data_1.pwm_cur = 85;
	pwm_data_1.pwm_max = 255;
	pwm_data_1.pwm_min = 65;
	pwm_data_1.temp_min = 30*1000;
	pwm_data_1.temp_max = 46*1000;

	pwm_data_2.pwm_max_step = 20;
	pwm_data_2.pwm_cur = 85;
	pwm_data_2.pwm_max = 255;
	pwm_data_2.pwm_min = 56;
//	pwm_data_2.temp_min = 8*1000;
//	pwm_data_2.temp_max = 28*1000;
	pwm_data_2.temp_min = 30*1000;
	pwm_data_2.temp_max = 46*1000;

	printf("temp_itfch %u\n", Read_File(temp_itfch));
	printf("temp_itcpu %u\n", Read_File(temp_itcpu));
	printf("temp_k10 %u\n", Read_File(temp_k10));
	printf("temp_radeon %u\n", Read_File(temp_radeon));



Write_File(pwmenable_1, 1);
Write_File(pwmenable_2, 1);

Write_File(pwm_1, pwm_data_1.pwm_cur);
Write_File(pwm_2, pwm_data_2.pwm_cur);


	if(setjmp(terminate_env)){
		signal(SIGALRM, SIG_IGN);
		fputs("\nInterrupt caught, restoring fans\n", stderr);
		Write_File(pwmenable_1, 0);
		Write_File(pwmenable_2, 0);
		//fflush();
		//fflush();
		exit(1);
	}
	capture_terminate(terminate_env);

while(1){

//pwm_data_1.temp_cur = Read_File(temp_itfch)
//pwm_data_2.temp_cur = Read_File(temp_k10)
//if(pwm_data_1.temp_cur

//tmp2 = pwm_data_1.temp_max - pwm_data_1.temp_min;
//tmp1 = (float)Read_File(temp_itfch)/pwm_data_1.temp_max;
//tmp1 *= pwm_data_1.pwm_max;
//tmp1 *= tmp2;
//tmp1 += pwm_data_1.temp_min;


tmp2 = pwm_data_1.temp_max - pwm_data_1.temp_min;
tmp1 = (float)Read_File(temp_itfch)-pwm_data_1.temp_min;
tmp1 = (tmp1 > 0) ? tmp1/tmp2 : 0;
//tmp1 /= tmp2
tmp2 = pwm_data_1.pwm_max - pwm_data_1.pwm_min;
tmp1 *= tmp2;
tmp1 += pwm_data_1.pwm_min;

if(tmp1 > pwm_data_1.pwm_cur){
	if((tmp1 - pwm_data_1.pwm_cur) > pwm_data_1.pwm_max_step){
		pwm_data_1.pwm_cur += pwm_data_1.pwm_max_step;
	}else{
		pwm_data_1.pwm_cur = tmp1;
	}
}else{
	if((pwm_data_1.pwm_cur - tmp1) > pwm_data_1.pwm_max_step){
		pwm_data_1.pwm_cur -= pwm_data_1.pwm_max_step;
	}else{
		pwm_data_1.pwm_cur = tmp1;
	}
}
Write_File(pwm_1, pwm_data_1.pwm_cur);

//tmp2 = pwm_data_2.temp_max - pwm_data_2.temp_min;
//tmp1 = (float)Read_File(temp_k10)/pwm_data_2.temp_max;
//tmp1 *= pwm_data_2.pwm_max;
//tmp1 *= tmp2;
//tmp1 /= tmp2;
//tmp1 += pwm_data_2.temp_min;
//tmp1 *= pwm_data_2.pwm_max;



tmp2 = pwm_data_2.temp_max - pwm_data_2.temp_min;
//tmp1 = (float)Read_File(temp_k10)-pwm_data_2.temp_min;
//tmp1 = (float)Read_File(temp_itcpu)-pwm_data_2.temp_min;
tmp1 = (float)Read_File(temp_itfch)-pwm_data_2.temp_min;

tmp1 = (tmp1 > 0) ? tmp1/tmp2 : 0;
//tmp1 /= tmp2
tmp2 = pwm_data_2.pwm_max - pwm_data_2.pwm_min;
tmp1 *= tmp2;
tmp1 += pwm_data_2.pwm_min;

if(tmp1 > pwm_data_2.pwm_cur){
        if((tmp1 - pwm_data_2.pwm_cur) > pwm_data_2.pwm_max_step){
                pwm_data_2.pwm_cur += pwm_data_2.pwm_max_step;
        }else{
                pwm_data_2.pwm_cur = tmp1;
        }
}else{
        if((pwm_data_2.pwm_cur - tmp1) > pwm_data_2.pwm_max_step){
                pwm_data_2.pwm_cur -= pwm_data_2.pwm_max_step;
        }else{
                pwm_data_2.pwm_cur = tmp1;
        }
}
Write_File(pwm_2, pwm_data_2.pwm_cur);




/*
if(abs(tmp1 - pwm_data_1.pwm_cur) > pwm_data_1.pwm_max_step){
pwm_data_1.pwm_cur += pwm_data_1.pwm_max_step;
}else{
if(tmp1 < pwm_data_1.pwm_min)
pwm_data_1.pwm_cur = pwm_data_1.pwm_min;
else
pwm_data_1.pwm_cur = tmp1;
}
Write_File(pwm_1, pwm_data_1.pwm_cur);
*/


sleep(poll);
}

/*
if( ( fp = fopen(temp_k10, "r") ) != NULL){
if( fgets(buf1, sizeof(buf1), fp) != NULL){
	tmp1 = atoi(buf1);
}

printf("%u\n", tmp1);

if(fclose(fp) != 0){
printf("%s: fclose - %s\n", temp_k10, strerror(errno) );
printf("Error: cannot close file %s , exiting!\n", temp_k10);
exit(1);
}


}else{
printf("%s: fopen - %s\n", temp_k10, strerror(errno) );
printf("Error: cannot open file %s , exiting!\n", temp_k10);
exit(1);
}

*/
	uncapture_terminate();


	return 0;
}
