#include <telldus-core.h>
#include <sys/time.h>
#include <stdio.h>
#include <time.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <syslog.h>


#define TIMEOUT_US 500000L
#define TIMEOUT_S 1
#define event_wrapper "/root/bin/event_wrapper.pl"
#define DUMMYDEVICE 10           //used for testing communication with telldusd

int volatile last_id = -1;       // Last recieved id and method
int volatile last_method = -1;
int volatile report_id = -1;     // id and method reported to child
int volatile report_method = -1;
int volatile do_report = 0;      // Set by signal handler
int volatile do_reload = 0;      
int volatile do_exit = 0;        
pid_t volatile pID = -1;         // pID of forked child

int debug = 0;                   // Debug mode, console output and no deamonize


// Tellstick events
int callbacksensorId = 0;
int callbackdeviceId = 1;
int callbackchangeId = 2;

void WINAPI deviceEvent(int deviceId, int method, const char *data, int callbackId, void *context);

void WINAPI deviceChange(int deviceId, int method, const char *data, int callbackId, void *context);

void WINAPI sensorEvent(const char *protocol, const char *model, int sensorId, int dataType, const char *value, int ts, int callbackId, void *context);



void unregister_telldusd() {
  tdUnregisterCallback( callbacksensorId );
  tdUnregisterCallback( callbackdeviceId );
  tdUnregisterCallback( callbackchangeId );
  //  tdClose();
}

void register_telldusd() {
  // tdInit();
  callbacksensorId = tdRegisterSensorEvent( (TDSensorEvent)&sensorEvent, 0 );
  callbackdeviceId = tdRegisterDeviceEvent( (TDDeviceEvent)&deviceEvent, 0 );
  callbackchangeId = tdRegisterDeviceChangeEvent( (TDDeviceChangeEvent)&deviceChange, 0 );
  if (tdTurnOff(DUMMYDEVICE)) {
	syslog (LOG_NOTICE, "telldusd error, exiting");
	tdClose();
	exit(1);
  }
}




/* 
Fork off and let child call event_wrrapper

 */
void report_event(int id, int method) {
  char s1[60];
  char timeBuf[80];
  time_t timestamp;

  if (id > 0) {
    // fork process and let child handle event
    pID = fork();
    if (pID == 0) {               // child
	unregister_telldusd();	
	tdClose();
	timestamp = time(NULL);
	strftime(timeBuf, sizeof(timeBuf), "%Y-%m-%d %H:%M:%S", localtime(&timestamp));
	if (method == TELLSTICK_TURNON) {
		sprintf (s1, "ON event from device %i\n", id);
		if (id != DUMMYDEVICE) syslog (LOG_NOTICE, s1);
		if (debug) printf("%s: %s", timeBuf, s1);
		sprintf(s1, "%s %d %d", event_wrapper, id, TELLSTICK_TURNON);
		system(s1);
	} else if (method == TELLSTICK_TURNOFF) {
		sprintf(s1, "OFF event from device %i\n", id);
		if (id != DUMMYDEVICE) syslog (LOG_NOTICE, s1);
		if (debug) printf("%s: %s", timeBuf, s1);
		sprintf(s1, "%s %d %d", event_wrapper, id, TELLSTICK_TURNOFF);
		system(s1);
        } else if (method == TELLSTICK_BELL) {
	  sprintf(s1, "BELL event from device %i\n", id);
	  if (id != DUMMYDEVICE) syslog (LOG_NOTICE, s1);
	  if (debug) printf("%s: %s", timeBuf, s1);
	  sprintf(s1, "%s %d %d", event_wrapper, id, TELLSTICK_BELL);
	  system(s1);
	} else {
		sprintf(s1, "Unknown event from device %i\n", id);
		syslog (LOG_NOTICE, s1);
		if (debug) printf("%s: %s", timeBuf, s1);
	}
	closelog();
	exit(0);                    
    }                             // child exit
  }
}




// Signal handlers for timeout and kill  

void timeout_handler(int signum) {
  do_report = 1;
  report_id = last_id;
  report_method = last_method;
  last_id = -1;
  last_method = -1;
}

void exit_handler(int signum) {
  do_exit = signum;
}

void reload_handler(int signum) {
  do_reload = 1;
}


void start_timer(int id, int method) {
	struct itimerval value;
	struct sigaction sa;

	// Report old if new event
	if ((id != last_id) || (method != last_method) ) {
	  do_report = 1;
	  report_id = last_id;
	  report_method = last_method;
	}
        last_id = id;
        last_method = method;

	value.it_value.tv_sec = TIMEOUT_S;
	value.it_value.tv_usec = TIMEOUT_US;
	value.it_interval.tv_sec = 0;
	value.it_interval.tv_usec = 0;
	memset (&sa, 0, sizeof (sa));
	sa.sa_handler = &timeout_handler;
	sigaction (SIGALRM, &sa, NULL);
	setitimer (ITIMER_REAL, &value, NULL);
}




// Signal handlers for Tellstick

void WINAPI deviceEvent(int deviceId, int method, const char *data, int callbackId, void *context) {
  if (pID) start_timer(deviceId, method); // Ignore if child
}

void WINAPI deviceChange(int deviceId, int method, const char *data, int callbackId, void *context) {
  if (pID) start_timer(deviceId, method); // Ignore if child
}

void WINAPI sensorEvent(const char *protocol, const char *model, int sensorId, int dataType, const char *value, int ts, int callbackId, void *context) {
  char timeBuf[80];
  time_t timestamp = ts;
  char s1[60];

  strftime(timeBuf, sizeof(timeBuf), "%Y-%m-%d %H:%M:%S", localtime(&timestamp));

  if (pID) {                              // Ignore if child 
    // Print the sensor
    sprintf(s1, "%s, %s, %i\n", protocol, model, sensorId);
    syslog (LOG_NOTICE, s1);
    if (debug) printf(s1);
  
    // Retrieve the values the sensor supports
    if (dataType == TELLSTICK_TEMPERATURE) {
      sprintf(s1, "Temperature: %s", value);  
      syslog (LOG_NOTICE, s1);
      sprintf(s1, "%s: Temperature: %s\n", timeBuf, value);  
      if (debug) printf(s1);
    } else if (dataType == TELLSTICK_HUMIDITY) {
      sprintf(s1, "Humidity: %s", value);
      syslog (LOG_NOTICE, s1);
      sprintf(s1, "%s: Humidity: %s\n", timeBuf, value);
      if (debug) printf(s1);
    }
  }
}


  

int main(int argc, char *argv[]) {
  debug = argc -1;
  pid_t deamon_pid, sid;

  //syslog     
  setlogmask (LOG_UPTO (LOG_NOTICE));
  openlog ("eventd", LOG_CONS | LOG_PID | LOG_NDELAY, LOG_LOCAL1);
  syslog (LOG_NOTICE, "Program started");
 
  if (!debug) {
    //syslog(LOG_NOTICE, "daemonizing...");
    deamon_pid = fork();
    if (deamon_pid < 0) {
      exit(EXIT_FAILURE);
    }
    if (deamon_pid > 0) {
	exit(EXIT_SUCCESS);
    }
    umask(0);
    sid = setsid();
    if (sid < 0) {
      exit(EXIT_FAILURE);
    }
    if ((chdir("/")) < 0) {
      exit(EXIT_FAILURE);
    }
    close(STDIN_FILENO);
    close(STDOUT_FILENO);
    close(STDERR_FILENO);
    syslog(LOG_NOTICE, "Running as deamon");
  }

  tdInit(); 
  register_telldusd();	  

  // trap POSIX exit signals
  signal(SIGHUP, reload_handler);
  signal(SIGINT, exit_handler);
  signal(SIGTERM, exit_handler);

  while(1) {
	sleep(1);
	waitpid(-1, NULL, WNOHANG);
	if (do_reload) {
		char s1[60];
		sprintf (s1, "Recieved SIGHUP, reloading telldusd...\n");
		syslog (LOG_NOTICE, s1);
		if (debug) printf(s1);
		unregister_telldusd();	  
		register_telldusd();
		do_reload = 0;
	}
	if (do_report) {
		do_report = 0;
		report_event(report_id, report_method);
		report_id = -1;
		report_method = -1;
	}
        if (do_exit) {
		char s1[60];
		sprintf (s1, "Recieved signal %d, exiting...\n", do_exit);
  		syslog (LOG_NOTICE, s1);
  		if (debug) printf(s1);
  		unregister_telldusd();	  
  		closelog();
  		waitpid(-1, NULL, WNOHANG);
  		tdClose();
  		exit(do_exit);
	}
  }
}