#include "python_utils.h"



class PythonMgrCreator {
public:
	PythonMgrCreator()
	{
		PythonMgr::instance();
	}
	~PythonMgrCreator()
	{
		PythonMgr::destroy();
	}
	
} PythonMgrCreatorInstance;

PythonMgr *PythonMgr::_instance = NULL;


////////////////////////////////////////////////////////////////////////////////////////////
PythonMgr::PythonMgr()
{
	_listener = NULL;
	
	if(!Py_IsInitialized()) {
		_init = true;
		Py_Initialize();
		PyEval_InitThreads();
	}
	else
		_init = false;

	_mainThreadState = PyThreadState_Get();
	_debugLevel = _py_print_errors;

	//PyEval_ReleaseLock();
}

PythonMgr::~PythonMgr()
{
	if(_init)
		Py_Finalize();	
}

PythonMgr *PythonMgr::instance()
{
	if(_instance == NULL) {
		_instance = new PythonMgr;
	}
	return _instance;
}

void PythonMgr::destroy()
{
	if(_instance) {
		delete _instance;
		_instance = NULL;
	}
}

PyThreadState *PythonMgr::createThreadInfo()
{
	//char str[512];
	//sprintf(str, "PythonMgr::createThreadInfo: %d\n", GetCurrentThreadId());
	//OutputDebugStringA(str);

	PyThreadState *threadState = PyGILState_GetThisThreadState();
	if(threadState != NULL) {
		return threadState;
		PyThreadState_Swap(NULL);
		PyThreadState_Delete(threadState);
		threadState = NULL;
	}

	if(threadState == NULL) {
		// get a reference to the PyInterpreterState
		PyInterpreterState *mainInterpreterState = _mainThreadState->interp;
		// create a thread state object for this thread
		threadState = PyThreadState_New(mainInterpreterState);
		//PyThreadState *threadState = PyThreadState_New(mainInterpreterState);
		_threadInfos.push_back(threadState);
	}
	return threadState;
}

void PythonMgr::destroyThreadInfo(PyThreadState *info)
{
	//char str[512];
	//sprintf(str, "PythonMgr::destroyThreadInfo: %d\n", info->thread_id);
	//OutputDebugStringA(str);

	for(ThreadStateList::iterator it = _threadInfos.begin(); it!=_threadInfos.end(); it++) {
		if(info == *it) {
			_threadInfos.erase(it);
			break;
		}
	}
	// clear out any cruft from thread state object
	PyThreadState_Clear(info);
	// delete my thread state object
	PyThreadState_Delete(info);
}

void PythonMgr::destroyAllThreadInfos()
{
	// don't destroy thread infos because Python keeps something and if a thread id is re-used, Python thinks that it's the
	// same thread of the previous one. To prevent this, I don't destroy the thread structs and re-use them when the thread id
	// is re-used.
	return;
	while(_threadInfos.size() > 0) {
		destroyThreadInfo(_threadInfos.front());
	}
}

void PythonMgr::swap(PythonThread *thread)
{
	PyThreadState_Swap(thread == NULL ? NULL : thread->threadState());
}

//////////////////////////////////////////////////////////////////////////////////////////////
PythonThread::PythonThread()
{
	PythonMgr *mgr = PythonMgr::instance();
	PyEval_AcquireLock();
	_threadInfo = mgr->createThreadInfo();
	mgr->swap(this);
}

PythonThread::~PythonThread()
{
	PythonMgr *mgr = PythonMgr::instance();

	mgr->swap();
	mgr->destroyThreadInfo(_threadInfo);
	PyEval_ReleaseLock();
}

//////////////////////////////////////////////////////////////////////////////////////////////
/**
\brief create a python list that represents the specified arguments.

It takes one parameters.
\param deployFile the file that will be passed as parameter to SimgridMain and should be used as deployment file
of the simulation.
*/

PyObject *PythonArgs::createArg(int argc, char** argv)
{
	PyObject* pyList = PyList_New(0);
	if(pyList) {
		for(int i=0; i<argc; i++) {
			PyObject* pyStr = PyString_FromString(argv[i]);
			if(pyStr) {
				PyList_Append(pyList, pyStr);
				Py_XDECREF(pyStr);
			}
		}
	}

	return pyList;
}

PyObject *PythonArgs::createArg(const char* arg)
{
	return PyString_FromString(arg);
}


//////////////////////////////////////////////////////////////////////////////////////////////
PythonAutoLock::PythonAutoLock(const char *name)
{
	PythonMgr *mgr = PythonMgr::instance();

	_state = PyThreadState_Get();
	_name = name;
	
	PyThreadState_Swap(NULL);
	PyEval_ReleaseLock();

	if(mgr->getDebugLevel() >= PythonMgr::_py_print_all && _name) {
		if(mgr->listener())
			mgr->listener()->entryFunction(name);
/*
#ifdef WIN32
		if(_name)
			printf("Enter %s: Thread Id: %d\n", _name, GetCurrentThreadId());
		else
			printf("Release Thread Id: %d\n", GetCurrentThreadId());
#else
		if(_name)
			printf("Enter %s\n", _name);
#endif // WIN32
*/
	}
}

PythonAutoLock::~PythonAutoLock()
{
	PythonMgr *mgr = PythonMgr::instance();

	if(mgr->getDebugLevel() >= PythonMgr::_py_print_all && _name) {
		if(mgr->listener())
			mgr->listener()->exitFunction(_name);
	
/*
#ifdef WIN32
		if(_name) 
			printf("Leave %s: Thread Id: %d\n", _name, GetCurrentThreadId());
		else
			printf("Leave: Thread Id: %d\n", GetCurrentThreadId());
#else
		if(_name)
			printf("Leave: Thread Id: %d\n", GetCurrentThreadId());
#endif // WIN32
*/
	}
	
	PyEval_AcquireLock();
	PyThreadState_Swap(_state);
}
