/**
 * @file tqueue.c
 * @brief Implementation of the cyclic queue
 * 
 * This file is based on Kalisto, Development Kernel copyrighted (c) to 
 * Distributed Systems Research Group MFF UK, Czech republic.
 */
 
#include "tqueue.h"
#include "malloc.h"
#include "io.h"



/**
 * @brief Initialize thread queue - empty queue is simply NULL
 * @param queue thread queue pointer
 */ 
void thread_queue_init(thread_queue_t *queue) {
	(*queue) = NULL;	
}

/**
 * @brief Check the emptyness of the queue
 * @return EEMPTY on empty list, ENOTEMPTY else
 */ 

int thread_queue_empty(thread_queue_t *queue) {
	return (((*queue) == NULL) ? EEMPTY : ENOTEMPTY);	
}

/**
 * @brief Add the item into thread_queue
 * @param queue - thread queue pointer
 * @param qt type of queue to be removed from 
 * @param item - pointer to insert
 */
void thread_queue_add(thread_queue_t *queue, thread_t *item, enum queue_types qt) {
	if (queue != NULL) {
		if ((*queue) == NULL) {	//queue is empty
			(*queue) = (*item);	//the head of the queue is inserted item
			(*queue)->queues[qt].next = (*queue); //next item is the only one - head
			(*queue)->queues[qt].prev = (*queue); //prev item is the only one - head
			if (qt == GENERAL)
				(*item)->actual_queue = &(*queue);
		} else { //non-empty queue (if you don't understand - draw it!)
			(*queue)->queues[qt].prev->queues[qt].next = (*item);
			(*item)->queues[qt].next = (*queue);
			(*item)->queues[qt].prev = (*queue)->queues[qt].prev;
			(*queue)->queues[qt].prev = (*item);
			if (qt == GENERAL)
				(*item)->actual_queue = &(*queue);
		}
	}
	return;
}

/**
 * @brief fetch the first item from thread queue - remove from the queue
 * 
 * @param queue thread queue pointer
 * @param item pointer - fetched item
 * @param qt type of queue to be fetched from 
 * @return EEMPTY on empty queue, EOK else 
 */
int thread_queue_fetch(thread_queue_t *queue, thread_t *item, enum queue_types qt) {
	if (queue != NULL) {
		if ((*queue) == NULL)
			return EEMPTY;
		else {
			(*item) = (*queue); //first item in the queue
			if ((*queue)->queues[qt].next == (*queue)) //in the queue is only one item
				(*queue) = NULL;
			else {	//more items in the list
				(*queue)->queues[qt].next->queues[qt].prev = (*queue)->queues[qt].prev;	//ommit the fetched item *queue
				(*queue)->queues[qt].prev->queues[qt].next = (*queue)->queues[qt].next;  // -//-
				(*queue) = (*queue)->queues[qt].next;
			}
			(*item)->queues[qt].next = (*item);	//the *item points to itself - it's a queue 
			(*item)->queues[qt].prev = (*item);    
		}
		return EOK;
	}
	return EINVAL;
}

/**
 * @brief remove item from the thread queue
 * 
 * @param queue - thread queue pointer
 * @param item pointer - item to remove
 * @param qt type of queue to be removed from
 * @return EEMPTY on empty queue, EOK if removed, ENOTEXISTS if the item isn't in the queue
 */
int thread_queue_remove_item(thread_queue_t *queue, thread_t *item, enum queue_types qt) {
	if (queue != NULL) {
		thread_t walk_through = (*queue);
		if (walk_through == NULL)
			return EEMPTY;
		
		if (walk_through == (*item)) {
			thread_queue_fetch(queue, item, qt);
			return EOK;
		}
		
		do {
			if (walk_through == (*item)) {
		   		walk_through->queues[qt].prev->queues[qt].next = walk_through->queues[qt].next;
		   		walk_through->queues[qt].next->queues[qt].prev = walk_through->queues[qt].prev;
		   		(*item)->queues[qt].next = (*item);
		   		(*item)->queues[qt].prev = (*item);
		   		return EOK;
		   	}
		   	walk_through = walk_through->queues[qt].next;
		} while (walk_through != (*queue));
		return ENOTEXISTS;
	}
	return EINVAL;
}


/**
 * @brief remove item from the thread queue, by id
 * 
 * @param queue - thread queue pointer
 * @param thread_id - id of the thread to be remove
 * @param qt type of queue to be removed from
 * @return EEMPTY on empty queue, EOK if removed, ENOTEXISTS if the item isn't in the queue
 */
int thread_queue_remove_item_by_id(thread_queue_t *queue, int thread_id, enum queue_types qt) {
	thread_t walk_through = (*queue);
	if (walk_through == NULL)
		return EEMPTY;
	
	if (walk_through->id == thread_id) {
		thread_queue_fetch(queue, &walk_through, qt);
		return EOK;
	}
	
	while ((walk_through = walk_through->queues[qt].next) != (*queue)) {
		if (walk_through->id == thread_id) {
	   		walk_through->queues[qt].prev->queues[qt].next = walk_through->queues[qt].next;
	   		walk_through->queues[qt].next->queues[qt].prev = walk_through->queues[qt].prev;
	   		return EOK;
	   	}
	}
	return ENOTEXISTS;
}

/**
 * @brief Rotate the thread_queue one item to the right (on empty queue no effect)
 * @param queue  - Pointer to the head of queue
 * @param qt type of queue to be removed from 
 */
void thread_queue_rotate(thread_queue_t *queue, enum queue_types qt){
	if ((*queue) != NULL)
		(*queue) = (*queue)->queues[qt].next;
}


/**
 * @brief enqueue thread to the queue, sort threads by wakeup time.
 * 
 * @param queue  - Pointer to the head of queue
 * @param item - thread to e enqueued
 * @param qt type of queue to be removed from
 * @return EEMPTY on empty queue, EOK if removed, ENOTEXISTS if the item isn't in the queue
 */
void thread_queue_enqueue(thread_queue_t *queue, thread_t *item, enum queue_types qt) {
	thread_t walk_through = (*queue); 
	unsigned wakeup_time = thread_get_wakeup_time_in_ticks((*item));
	
	
	if ((walk_through) == NULL) {
		thread_queue_add(queue, item, qt);
		return;
	} 
	
	if ((walk_through)->queues[qt].next == (*queue)) { //only one item in the list
		(*item)->queues[qt].next = walk_through;
		(*item)->queues[qt].prev = walk_through;
		walk_through->queues[qt].prev = (*item);
		walk_through->queues[qt].next = (*item);
		if (wakeup_time <= thread_get_wakeup_time_in_ticks(walk_through)) 
			(*queue) = (*item);
		else
			(*queue) = walk_through;
		return;
	}
	
	do {
		if (wakeup_time <= thread_get_wakeup_time_in_ticks(walk_through)) { //enqueue before walk_through
			(*item)->queues[qt].prev = (walk_through)->queues[qt].prev;
			(*item)->queues[qt].next = (walk_through);
			(walk_through)->queues[qt].prev->queues[qt].next = (*item);
			(walk_through)->queues[qt].prev = (*item);
			if (walk_through == (*queue))
				(*queue) = (*item);
			return;			
		}
		walk_through = walk_through->queues[qt].next;
	} while ((walk_through)!= (*queue));

	//now we are on the end of the queue
	(*item)->queues[qt].next = (*queue);
	(*item)->queues[qt].prev = (*queue)->queues[qt].prev;
	(*queue)->queues[qt].prev->queues[qt].next = (*item);
	(*queue)->queues[qt].prev = (*item);
}
/**
 * Created for debuging (internal) purposes only, prit memebers of the queue
 * @param queue  - Pointer to the head of queue to be printed,
 * @param qt type of queue to be removed from.
 * 
 */
void thread_queue_debug_print(thread_queue_t *queue, enum queue_types qt) {
	thread_t walk_through = (*queue);
	if (walk_through == NULL) {
		printk ("Thread queue empty.\n");
		return;
	}
	do {
		printk("[%d],%s,%d | ", walk_through->id, ts(walk_through->state), thread_get_wakeup_time_in_ticks(walk_through));
		walk_through = walk_through->queues[qt].next;
	} while ((walk_through) != (*queue));
		printk("||\n");
	return;
}

/** 
 * @brief Checks, if the item is member of this queue 
 * @param queue  - Pointer to the head of queue to be printed,
 * @param item item to be checked
 * @param qt type of queue to be removed from.
*/
int thread_queue_exists(thread_queue_t *queue, thread_t item, enum queue_types qt) {
//			dprintk1("item = %p\n", item);
	thread_t walk_through = (*queue);
	if (walk_through == NULL)
		return ENOTEXISTS;
	
	do {
//			dprintk1("walk_throught = %p\n", walk_through);
		if (walk_through == item) {
//			dprintk("vracim eok\n");
	   		return EOK;
	   	}
//			dprintk2("walk_through: %p item: %p \n", walk_through, item);
	   	walk_through = walk_through->queues[qt].next;
	} while (walk_through != (*queue));
	return ENOTEXISTS;
}
