/*
 * timer.cpp
 *
 *  Created on: Nov 9, 2015
 *      Author: Philipp Hinz
 *		Source: http://man7.org/linux/man-pages/man2/timer_create.2.html
 *				http://www.informit.com/articles/article.aspx?p=23618&seqNum=14
 */

#include <csignal>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//#include <sys/time.h>
#include <time.h>
#include <pthread.h>
#include <errno.h>
//#include <unistd.h>
#include "global.h"
#include "timer.h"
#include "logger.h"

int counter = 0;
int timercnt = 10;
timer *firstTimer = NULL;
timer *lastTimer = NULL;

/**
 * Timer constructor
 * Depending on the call of the constructor the handler will be called as thread or not
 * @param *handler target pointer to the method
 */

timer::timer(void (*handler)(void)) {
	this->active = false;
	this->divider = 0;
	this->handler = handler;
	this->thandler = NULL;
	this->next = firstTimer;
	this->asThread = false;
	this->id = timercnt++;
	this->thread = pthread_t();
	firstTimer = this;
}

timer::timer(void *(*handler)(void *)) {
	this->active = false;
	this->divider = 0;
	this->thandler = handler;
	this->handler = NULL;
	this->next = firstTimer;
	this->thread = pthread_t();
	this->id = timercnt++;

	// Now calling a dummy thread, to allow later the check of the thread state
	int rc = pthread_create(&this->thread, NULL, nullThread, (void *) this->id);
	if (rc) {
		logger_error("Error: unable to create thread in timer, %d\n", rc);
		exit(-1);
	}
	this->asThread = true;
	firstTimer = this;
}

/**
 * Calls the timer handler if enabled
 */
// void *MainLoop(void *threadid)
void timer::call() {
	int rc, ret = 0;
	if (this->asThread) {
		if (this->thread)
			ret = pthread_kill(this->thread, 0);
		if (ret == ESRCH) { // Thread clear
			pthread_detach(this->thread); // Important! Freeing the resources of the previous thread
			rc = pthread_create(&this->thread, NULL, this->thandler,
					(void *) this->id);
			if (rc) {
				logger_error(
						"Error: unable to create thread in timer, %s (%d)\n",
						strerror(rc), rc);
				exit(-1);
			}
		}
	} else
		this->handler();
}

/**
 * enables the timer
 */

void timer::start() {
	this->active = true;
}

/**
 * disables the timer
 */

void timer::stop() {
	this->active = false;
}

/**
 * sets the divider for this timer (based on system timer)
 * @param divider integer timer divider
 */

void timer::setDivider(int divider) {
	this->divider = divider;
}

/**
 * reads the timer divider
 * @return timer divider
 */

int timer::getDivider() {
	return this->divider;
}

/**
 * Returns the timers active state
 * @return true if active
 */

bool timer::isActive() {
	return this->active;
}

/**
 * this method is called by the system timer and handles the other timers
 */

void timer_handler(int sig, siginfo_t *si, void *uc) {
	timer *t = firstTimer;
	if (++counter >= TIMER_DELAY_US)
		counter = 0;

	while (t != NULL) {
		if (t->isActive() && t->getDivider() > 0
				&& (counter % t->getDivider()) == 0)
			t->call();
		t = t->next;
	}
}

/**
 * inits the system timer based on signals
 */

void initTimers(void) {
	/*struct sigaction sa;
	 struct itimerval timer;

	 * Install timer_handler as the signal handler for SIGVTALRM.
	 memset(&sa, 0, sizeof(sa));
	 sa.sa_handler = &timer_handler;
	 sigaction(SIGVTALRM, &sa, NULL);*/

	timer_t timerid;
	struct sigevent sev;
	struct itimerspec its;
	//sigset_t mask;
	struct sigaction sa;

	/* Establish handler for timer signal */

	logger(V_BASIC, "timer.cpp: Establishing handler for signal %d\n", SIG);
	sa.sa_flags = SA_SIGINFO;
	sa.sa_sigaction = &timer_handler;
	sigemptyset(&sa.sa_mask);
	if (sigaction(SIG, &sa, NULL) == -1)
		errExit("sigaction");

	/* Block timer signal temporarily

	 printf("Blocking signal %d\n", SIG);
	 sigemptyset(&mask);
	 sigaddset(&mask, SIG);
	 if (sigprocmask(SIG_SETMASK, &mask, NULL) == -1)
	 errExit("sigprocmask"); */

	/* Create the timer */

	sev.sigev_notify = SIGEV_SIGNAL;
	sev.sigev_signo = SIG;
	sev.sigev_value.sival_ptr = &timerid;
	if (timer_create(CLOCKID, &sev, &timerid) == -1)
		errExit("timer_create");

	logger(V_BASIC, "timer ID is 0x%lx\n", (long) timerid);

	/* Configure the timer to expire after x msec... */
	its.it_value.tv_sec = TIMER_DELAY_US / 1000000;
	its.it_value.tv_nsec = TIMER_DELAY_US * 1000;
	/* ... and every x msec after that. */
	its.it_interval.tv_sec = its.it_value.tv_sec;
	its.it_interval.tv_nsec = its.it_value.tv_nsec;
	/* Start a virtual timer. It counts down whenever this process is
	 executing. */
	//setitimer(ITIMER_VIRTUAL, &timer, NULL);
	if (timer_settime(timerid, 0, &its, NULL) == -1)
		errExit("timer_settime");

	counter = 0;
}

/**
 * Stops all existing timers
 */

void stopTimers(void) {
	timer *t = firstTimer;

	while (t != NULL) {
		t->stop();
		t = t->next;
	}
	logger(V_BASIC, "Stopped all timers.\n");
}

/**
 * This is a dummy thread
 * @param *threadid Thread ID
 */

void *nullThread(void *threadid) {
	pthread_exit(NULL);
}

