#include "Python.h"
#include "stackless_api.h"
#include "LinkedList.h"

PyObject *my_scheduler_error;

ListNode *ReinsertPendingPointer = NULL;

ListNode *list_create(void)
{
	ListNode* node = NULL;
	node = malloc(sizeof(ListNode));
	if(node == NULL)
	{
		errno = ENOMEM;
		return -1;
	}
	node->Data = NULL;
	node->Next = NULL;
	return node;	
}

int list_add(ListNode *head, void* listEntry)
{
	ListNode* node;
	
	if(head == NULL)
	{
		errno = EINVAL;
		return -1;
	}
	
	node = head;
	while(node->Next != NULL)
	{
		node = node->Next;
	}
	
	// Create and insert the new node into the linked-list
	node->Next = malloc(sizeof(ListNode));
	if(node->Next == NULL)
	{
		errno = ENOMEM;
		return -1;
	}

	node = node->Next;
	node->Data = listEntry;	
	node->Next = NULL;

	/*===== Add the pending structure to the list -----*/

	return 0;
}

int list_remove(ListNode *head, void* listEntry)
{
	ListNode* prev;
	ListNode* current;
	
	prev = head;
	current = prev->Next;
	
	while(current != NULL && current->Data != listEntry)
	{
		prev = current;
		current = current->Next;
	}
	
	if(current == NULL || current->Data != listEntry)
	{
		return -1;
	}
	
	prev->Next = current->Next;
	free(current);
	
	return 0;
}

void ReinsertPendingScripts(void)
{
	ListNode *prevNode = NULL;
	ListNode *currentNode = NULL;
	PyTaskletObject *tasklet = NULL;
	if(ReinsertPendingPointer != NULL)
	{
		prevNode = ReinsertPendingPointer; 
		currentNode = prevNode->Next; 
		while(currentNode != NULL)
		{
			tasklet = (PyTaskletObject *)currentNode->Data;
			slp_current_insert(tasklet); 
		 	Py_DECREF(tasklet);	

			list_remove(ReinsertPendingPointer, tasklet);
			currentNode->Data = NULL;
			currentNode = prevNode;
			prevNode = currentNode;
			currentNode = currentNode->Next;
		}
	}
	else
	{
		ReinsertPendingPointer = list_create();
	}
}


static PyObject * scheduler_run(PyObject *self, PyObject *args)
{
	int retval = -1;
	PyObject *RetTasklet = NULL;
	PyObject *module_dict=NULL;


	/* Declarations for Stackless Module */
	PyObject *stacklessmod=NULL;
	PyObject *sm_dict=NULL;
	PyObject *sm_runcall=NULL;

	do
	{
		/*----- Calls to get the stackless.run(100) call in the namespace -----*/ 
		stacklessmod = PyImport_AddModule("stackless");
		if(stacklessmod == NULL)
		{
			break;
		}

		sm_dict = PyModule_GetDict(stacklessmod);
		if(sm_dict == NULL)
		{
			break;
		}

		sm_runcall = PyDict_GetItemString(sm_dict, "run");
		if(sm_runcall == NULL)
		{
			break;
		}

		

		while(1)
		{

			ReinsertPendingScripts();

			// run next tasklet for 100

			RetTasklet = PyEval_CallFunction(sm_runcall, "(i)", 100);
			if(RetTasklet == NULL)
			{
				PyErr_Print();
			}
			else if(RetTasklet != Py_None)
			{
				Py_INCREF(RetTasklet);
				list_add(ReinsertPendingPointer, RetTasklet);
			}
		}

		/* This shouldn't happen, but need to handle the case */
		Py_XDECREF(sm_runcall);
		Py_XDECREF(sm_dict);
		Py_XDECREF(stacklessmod);

		
	}while(0);
	
}



static PyMethodDef my_scheduler_methods[] = {
	{"run",scheduler_run,METH_VARARGS
		,"Run my scheduler"},
	{NULL,NULL,0,NULL}
};

PyMODINIT_FUNC
initmy_scheduler(void)
{
	PyObject *m;
	m = Py_InitModule("my_scheduler",my_scheduler_methods);
	if(my_scheduler_error == NULL) {
		my_scheduler_error = PyErr_NewException("my_scheduler.error", NULL, NULL);
		if (my_scheduler_error == NULL)
			return;
	}
	Py_INCREF(my_scheduler_error);
	PyModule_AddObject(m, "error", my_scheduler_error);
}



