/**
 * @file timers.c
 * @brief Include functions for initialization, starting and destroing timers
 *  
 * Timers are system structures, that allows threads raise asynchronous event.
 * There is no need to allocate memory for this event, like threads must when 
 * creating new thread. The timer handling function is called after delay,
 * that thread can specified. Timers handling functions runs in context of thread, 
 * that was interuppted. Timers are storage in list, where timer structure has 
 * pointer to next timer.
 * 
 */
 
#include "timers.h"
#include "sys.h"
#include "errnums.h"
#include "debug.h"
#include "io.h"
#include "malloc.h"

/**
 * @brief A global list of timers in the system
 */
timer_list * timer_ll;

/**
 * @brief Function inicialized structure of timer
 * @param tmr - pointer to timer structure
 * @param usec - count of microseconds, that determine delay of timer
 * @param handler - pointer to function, that is called after delay is runned out
 * @param data - pointer to data, that are passed to handler
 * @return - EINVAL - if the handler is NULL, otherwise EOK
 * 
 * Function inicialized structure of timer for time usec microseconds. Parameters are
 * stored into timer structure and stop_cycle value is set to 0, which means, that timer 
 * is not pending. Timer structure is not inserted into list of timers!! 
 * 
 */
int timer_init (
          struct timer * tmr, const unsigned int usec,
          void (* handler) (struct timer *, void *), void * data){
	if (!handler)
		return EINVAL;
	tmr->data = data;
	tmr->handler = handler;
	tmr->usec = usec; 
	tmr->stop_cycle = 0; 
	tmr->next = NULL;
	tmr->prev = NULL;
	return EOK;
}

/**
 * @brief Function activate timer
 * @param tmr - pointer to timer structure
 * 
 * Function activate timer and insert it to the tail of list of active timers.
 * Activation mean that the stop_cycle value is set to count of cpu cycles + timer
 * microseconds multiply by cpu cycles per microseconds.
 * If the timer in argument is pending, than it is restarted - it stay in list of 
 * active timers and stop_cycle value is again computed to the new value
 * 
 */
void timer_start (struct timer * tmr){
						#ifdef TIMER_DEBUG
							printk("try to timer start\n");
						#endif
	// if timer is pending remove it from the list, because we need it newly enqueue
	if (timer_pending(tmr) ){ 
		timer_destroy(tmr);
	}
	// set the cycle after that the handler function will be called
	// value is actual cycle + usec * cpu cycles per microsecon
	tmr->stop_cycle = read_cp0_count() + ((MSIM_FREQUENCY / 1000000) * tmr->usec);
	// enqueue timer
	// inserting new item to the empty list
	if (!timer_ll->head){
		timer_ll->head = tmr;
		timer_ll->tail = tmr;
		tmr->next = NULL;
		tmr->prev = NULL;
					#ifdef TIMER_DEBUG
						printk("first item inserted\n");
					#endif
	}
	else
	{
						#ifdef TIMER_DEBUG
							printk("inserting non first item\n");
						#endif
		// pointer to actually walking trough timer
		struct timer * timer_ll_item = timer_ll->tail;
						#ifdef TIMER_DEBUG
							printk("timer_ll_item pred whilem je: %p \n",timer_ll_item);
							printk("timer_ll_item->prev pred whilem je: %p \n",timer_ll_item->prev);
							printk("timer_ll_item->stop_cycle pred whilem je: %d \n",timer_ll_item->stop_cycle);
							printk("tmr->stop_cycle pred whilem je: %d \n",tmr->stop_cycle);
						#endif
		while (timer_ll_item && (timer_ll_item->stop_cycle > tmr->stop_cycle)){ 
						#ifdef TIMER_DEBUG
							printk("timer_ll_item ve whilu je: %p \n",timer_ll_item);
						#endif
			timer_ll_item = timer_ll_item->prev;
		}
		if (timer_ll_item){
						#ifdef TIMER_DEBUG
							printk("inserting item inside list \n");
						#endif
			tmr->next = timer_ll_item->next;
			tmr->prev = timer_ll_item;
						#ifdef TIMER_DEBUG
							printk("tmr inited \n");
						#endif
			if (timer_ll_item->next)
				timer_ll_item->next->prev = tmr;
			else
				timer_ll->tail = tmr;
			timer_ll_item->next = tmr;
		}
		// inserting new item to the head of list;
		else {
						#ifdef TIMER_DEBUG
							printk("inserting item to head\n");
						#endif
			tmr->next = timer_ll->head;
			tmr->prev = NULL;
			timer_ll->head->prev = tmr;
			timer_ll->head = tmr;
						#ifdef TIMER_DEBUG
							printk("head item inserted\n");
						#endif
		}
	}
					#ifdef TIMER_DEBUG
						printk("Timer start successfull \n");
					#endif
		
}

/**
 * @brief Function deactivate timer
 * @param tmr - pointer to timer structure
 * 
 * Function walk trough list of timers and find out the timer in parametr.
 * This timer is stopped, stop cycle is set to 0, and is remove from 
 * list of timers.
 * 
 */ 
void timer_destroy (struct timer * tmr){
	// pointer to actually walking trough timer
	struct timer * timer_ll_item = timer_ll->head;
	// while not reached the tail walk trough the list
	while(timer_ll_item){
		// if the actually walking trough timer is the timer in parametr
		if (timer_ll_item==tmr){
			// stop this timer by setting stop_cycle to 0
			tmr->stop_cycle = 0;
			// now remove the timer from list
			if (timer_ll_item->prev)
				timer_ll_item->prev->next = timer_ll_item->next;
			else // if the timer is first in list, we must move head of list to next timer
				timer_ll->head = timer_ll_item->next;
			//if the timer is the last in list, we must move tail to previous timer
			if (timer_ll_item->next)
				timer_ll_item->next->prev = timer_ll_item->prev;
			else
				timer_ll->tail = timer_ll_item->prev;
			
			// set the next pointer to NULL - 3 hours of searching problem
			tmr->next = NULL;
			tmr->prev = NULL;
			
			// because we found our timer, we can stop walking trough list
			break;	
		}
		else {
			// move to the next timer in the list
			timer_ll_item = timer_ll_item->next;
		}			
	}
}


/**
 * @brief Debug walk trough
 * 
 * Walk trough list of timers and print timers pointer and next pointer.
 * 
 */
void timer_debug(void) {
	struct timer * timer_ll_item = timer_ll->head;
	printk("timer debug begin---------\n");
	while (timer_ll_item) {
		printk("timer %p ukazuje na: %p\n", timer_ll_item, timer_ll_item->next);
		timer_ll_item = timer_ll_item->next;
	}
	printk("timer debug end ---------\n");
}

/**
 * @brief Function return that the timer is pending
 * @param tmr - pointer to timer structure
 * @return - 1 if the timer is pending, otherwise 0
 * 
 * Function return 1 if the timer is pending, it means that stop cycle is great than
 * actual count of cpu cycles from cpu start. Otherwise function return 0.
 * 
 */
int timer_pending (struct timer * tmr){
	if (read_cp0_count() < tmr->stop_cycle)
		return 1;
	return 0;
}

/**
 * @brief Function is searching for timers with runned out delay
 * 
 * Function walk trough the list of timers. If the delay is runned out (count of cpu
 * cycles is less than stop_cycle) the timer is destroyed and handle function is called
 * with parametres, that point to timer and inserted data.
 * 
 */
void timer_check (){
						#ifdef TIMER_DEBUG
							printk("timer check started\n");
						#endif
	// pointer to actually walking trough timer
	timer_t * timer_ll_item = timer_ll->head;
		#ifdef TIMER_DEBUG
		timer_debug();
		#endif
	// while count of cpu cycles reached the value in timer stop_cycle
	while(timer_ll_item && (timer_ll_item->stop_cycle < read_cp0_count())){
		
		#ifdef TIMER_DEBUG
			printk("clock: %d\n", read_cp0_count());
			printk("stop_cycle: %d\n", timer_ll_item->stop_cycle);
		#endif
		timer_ll_item->stop_cycle = 0;
		timer_destroy(timer_ll_item);
		timer_ll_item->handler(timer_ll_item, timer_ll_item->data);
		timer_ll_item = timer_ll_item->next;
	}
}


/**
 * @brief Function returns count of microseconds from cpu start
 * @return Number of microseconds from cpu start
 * 
 */
unsigned get_sysutime (){
	return read_cp0_count() / (MSIM_FREQUENCY_USEC);
}


/**
 * @brief Function initialized list of timers
 * @return EOK on success or ENOMEM on system memory lack
 * 
 */
int init_timers (){
	if ((timer_ll = (timer_list *) malloc (sizeof(timer_list)))){
		// memory allocation was successfull -> set pointers to NULL
		timer_ll->head = NULL;
		timer_ll->tail = NULL;
		return EOK;
	}
	return ENOMEM;
}
