#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <math.h>
#include <signal.h>


void blinkled(char *led, const struct timespec *);
void term(int);

FILE *fp_dev;
char *s_pathToLed="";
bool b_initState=false;

int main(int argc, char **argv) {
	pid_t p_pid = 0;
	struct timespec ts_sleepValue;
	extern char *optarg;
        extern int i_optind, i_optopt;
	char *s_led=NULL;
	int i_seconds=-1, i_nanoseconds=-1;
	char c_opt;

	//parse options
	while((c_opt=getopt(argc,argv,"s:n:l:"))!=-1) {
		switch(c_opt) {
			case 's':
				i_seconds=atoi(optarg);
				printf("i_seconds is %i\n", i_seconds);
				break;
			case 'n':
				i_nanoseconds=atoi(optarg);
				printf("i_nanoseconds is %i\n", i_nanoseconds);
				break;
			case 'l':
				s_led=optarg;
				printf("s_led is %s\n", s_led);
				break;
		}
	}

	//check values
	if(i_seconds==-1 && i_nanoseconds==-1) {
		printf("i_nanoseconds and i_seconds can't be zero\n");
		exit(EXIT_FAILURE);
	} else if(i_seconds==-1) {
		i_seconds=0;
	} else if(i_nanoseconds==-1) {
		i_nanoseconds=0;
	}
	if(s_led==NULL) {
		s_led=(char*)malloc(5*sizeof(char));
		strcat(s_led, "wled");
		printf("s_led not set, using %s as led\n", s_led);
	}
	
	//set timespec struct for nanosleep
	ts_sleepValue.tv_sec=i_seconds;
	ts_sleepValue.tv_nsec=i_nanoseconds;

	//catch sighup and sigterm to reset the led's before exit();'ing
	if(signal(SIGHUP, term)==SIG_IGN) {
		signal(SIGHUP, SIG_IGN);
	}
	if(signal(SIGTERM, term)==SIG_IGN) {
		signal(SIGTERM, SIG_IGN);
	}

	//Fork to run as a daemon
	p_pid = fork();
	if (p_pid==0) {
		blinkled(s_led, &ts_sleepValue);
	}
	if (p_pid>0) {
		printf("fork();'ed.\n");
	}
	if (p_pid<0) {
		perror("In fork():");
	}
	return EXIT_SUCCESS;
}

//signal-handler for SIGHUP und SIGTERM, to restore led state on stop
void term(int i_signum) {
	switch(i_signum) {
		case SIGTERM:
			if(fp_dev==NULL) {
				fp_dev=fopen(s_pathToLed, "w");
			}
			if(fp_dev==NULL) {
				printf("can't open devie %s\n", s_pathToLed);
				exit(EXIT_FAILURE);
			}
			if(b_initState) {
				if(fputc('1', fp_dev)==EOF) {
					printf("could't not write state to %s\n", s_pathToLed);
					fclose(fp_dev);
					exit(EXIT_FAILURE);
				}
			} else {
				if(fputc('0', fp_dev)==EOF) {
					printf("could't not write state to %s\n", s_pathToLed);
					fclose(fp_dev);
					exit(EXIT_FAILURE);
				}
			}
			fclose(fp_dev);
			exit(EXIT_SUCCESS);
			break;
		case SIGHUP:
			return;
		default:
			break;
	}
	return;
}


//this is where the lights are blinking
void blinkled(char *s_led, const struct timespec *ts_sleepValue) {
	//initialize default values
	bool b_toggle=true;
	char *s_path="/proc/acpi/asus/"; 
	char c_ledValue;

	//cat together the devicename
	s_pathToLed=(char*)malloc((sizeof(char)*strlen(s_path))+(sizeof(char)*strlen(s_led))+sizeof(char));
	strcat(s_pathToLed, s_path);
	strcat(s_pathToLed, s_led);

	//open the device and exit if it can't be done
	fp_dev=fopen(s_pathToLed, "w+");
	if(fp_dev==NULL) {
		printf("can't open %s\n", s_pathToLed);
		exit(EXIT_FAILURE);
	} else {
		//read the current state of the led
		if((c_ledValue=fgetc(fp_dev))==EOF) {
			printf("can't get current state of the led\n");
			exit(EXIT_FAILURE);
		}
		//set the toggle value
		if(c_ledValue=='0') {
			b_toggle=false;
		} else {
			b_toggle=true;
		}
		//save value to restore on exit
		b_initState=b_toggle;
		do {
			fp_dev=fopen(s_pathToLed, "w");
			if(b_toggle) {
				if(fputc('0', fp_dev)==EOF) {
					printf("could't not write state to %s\n", s_pathToLed);
					fclose(fp_dev);
					exit(EXIT_FAILURE);
				}
			} else {
				if(fputc('1', fp_dev)==EOF) {
					printf("could't not write state to %s\n", s_pathToLed);
					fclose(fp_dev);
					exit(EXIT_FAILURE);
				}
			}
			b_toggle=!b_togge;
			fclose(fp_dev);
			fp_dev=NULL;
			nanosleep(ts_sleepValue, NULL);
		} while(true);
	}
	return;
}

