/**
 * @file condvar.c
 * @brief Implementation of conditional variables
 * 
 * Conditional variables are implemented as a queue of threads waiting for the condition, that allows 
 * to wakeup them on cond_wait and cond signal calls.
 * 
 * This file is based on Kalisto, Development Kernel copyrighted (c) to 
 * Distributed Systems Research Group MFF UK, Czech republic.
 */ 
 
#include "condvar.h"
#include "errnums.h"
#include "debug.h"
#include "int.h"
#include "timers.h"
#include "io.h"
#include "mutex.h"

/**
 * @brief Initialize condition variable
 * @param cvar condition variable struct pointer
 */
void cond_init (cond_t * cvar){
	thread_queue_init(&(cvar->tq));
}

/**
 * @brief Destroys conditional variable
 * @param cvar condition variable struct pointer
 */
void cond_destroy (cond_t * cvar){
	/*  destroying conditional variable containing non-empty queue of waiting threads cause panic */
	if (thread_queue_empty(&(cvar->tq)) == ENOTEMPTY) 
		panic("An attempt to destroy conditional variable with non-empy queue of waiting threads.");
}

/**
 * @brief Unlock one of threads waiting on the condition variable
 * @param cvar condition variable struct pointer
 */
void cond_signal (cond_t * cvar){
	thread_t thread;
	save_and_disable_interrupts();
	if ( thread_queue_fetch(&(cvar->tq), &thread, GENERAL) == EOK) {
		thread_wakeup(thread);
	}
	restore_interrupts();
}

/**
 * @brief Unlock all of threads waiting on the condition variable
 * 
 * @param cvar  condition variable struct pointer
 */
void cond_broadcast (cond_t * cvar){
	thread_t thread;
	save_and_disable_interrupts();
	while (thread_queue_fetch(&(cvar->tq), &thread, GENERAL) == EOK) {
		thread_wakeup(thread);	
	}
	restore_interrupts();
}

/**
 * @brief Suspend calling thread on a condition variable waiting for condition
 * Atomically unlocks mutex and suspends calling thread on conditional variable
 * after waking up locks mutex. 
 * 
 * @param cvar condition variable pointer
 * @param mtx mutex pointer 
*/
void cond_wait (cond_t * cvar, struct mutex* mtx){
   thread_t thread;
   save_and_disable_interrupts();
   thread = thread_get_current();
   mutex_unlock(mtx);
   thread_set_state(thread, THRS_SUSPENDED);
   thread_queue_add(&(cvar->tq), &thread, GENERAL);
   thread_suspend();
   mutex_lock(mtx);
   restore_interrupts();
}
/**
 * @brief Atomically unlocks mutex and suspends calling thread at most on \
 * usec microseconds. 
 * 
 * If 0 msec is wanted thread suspend is ommitted, but the
 * thread yield one times before next mutex lock
 * 
 * @param cvar condition variable struct pointer
 * @param mtx mutex pointer
 * @param usec time limit to wait
 * @return EOK on thread woken up durint the wanted time period, ETIMEDOUT else
 */
int cond_wait_timeout (cond_t * cvar, mutex_t * mtx, const unsigned int usec){
	
	thread_t thread = thread_get_current();
	save_and_disable_interrupts();
	mutex_unlock(mtx);
	
	if (usec == 0) {
		thread_yield();
		mutex_lock(mtx);
		restore_interrupts();
		return EOK;	
	} else {
		thread_set_state(thread, THRS_SLEEPING);
		thread_queue_add(&(cvar->tq), &thread, GENERAL);
		thread_usleep(usec);
	}
	//now the thread is woken up
	if (current_thread->woken_up == WOKEN_BY_TIMER) {
		//thread_queue_remove_item(&(cvar->tq), &current_thread, GENERAL);
		mutex_lock(mtx);
		restore_interrupts(); 
		return ETIMEDOUT;
	} else {
		//the thread was woken in cond_signal or cond_broadcast
		//threrefore it isn't present the cvar->tq no more
		mutex_lock(mtx);
		restore_interrupts();
		return EOK; 	
	}
	
//	if (current_thread->woken_up == WOKEN_UNDEFINED) {
//		panic("Unknown unhandled error in condition variables");	
//	}
	return EOK;
}
