/*==LICENSE==*

CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.

Additional permissions under GNU GPL version 3 section 7

If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.

You can contact Cyan Worlds, Inc. by email legal@cyan.com
 or by snail mail at:
      Cyan Worlds, Inc.
      14617 N Newport Hwy
      Mead, WA   99021

*==LICENSE==*/
#include "hsTypes.h"
#include "PythonInterface.h"

#include "compile.h"
#include "eval.h"
#include "marshal.h"
#include "cStringIO.h"

static PyObject* stdFile;	// python object of the stdout and err file

void PythonInterface::initPython(std::string rootDir)
{
	// if haven't been initialized then do it
	if ( Py_IsInitialized() == 0 )
	{
		// initialize the Python stuff
		// let Python do some intialization...
		Py_SetProgramName("plasma");
		Py_Initialize();

		// intialize any of our special plasma python modules
//		initP2PInterface();
		// save object to the Plasma module
//		plasmaMod = PyImport_ImportModule("Plasma");

		// create the StringIO for the stdout and stderr file
		PycStringIO = (struct PycStringIO_CAPI*)PyCObject_Import("cStringIO", "cStringIO_CAPI");
		stdFile = (*PycStringIO->NewOutput)(20000);
		// if we need the builtins then find the builtin module
		PyObject* sysmod = PyImport_ImportModule("sys");
		// then add the builtin dicitionary to our module's dictionary
		if (sysmod != NULL )
		{
			// get the sys's dictionary to find the stdout and stderr
			PyObject* sys_dict = PyModule_GetDict(sysmod);
			if (stdFile != nil)
			{
				PyDict_SetItemString(sys_dict,"stdout", stdFile);
				PyDict_SetItemString(sys_dict,"stderr", stdFile);
			}
			// NOTE: we will reset the path to not include paths
			// ...that Python may have found in the registery
			PyObject* path_list = PyList_New(0);
			printf("Setting up include dirs:\n");
			printf("%s\n",rootDir.c_str());
			PyObject* more_path = PyString_FromString(rootDir.c_str());
			PyList_Append(path_list, more_path);
			// make sure that our plasma libraries are gotten before the system ones
			std::string temp = rootDir + "plasma";
			printf("%s\n",temp.c_str());
			PyObject* more_path3 = PyString_FromString(temp.c_str());
			PyList_Append(path_list, more_path3);
			temp = rootDir + "system";
			printf("%s\n\n",temp.c_str());
			PyObject* more_path2 = PyString_FromString("system");
			PyList_Append(path_list, more_path2);
			// set the path to be this one
			PyDict_SetItemString(sys_dict,"path",path_list);


			Py_DECREF(sysmod);
		}
	}
//	initialized++;
}

void PythonInterface::addPythonPath(std::string path)
{
	PyObject* sysmod = PyImport_ImportModule("sys");
	if (sysmod != NULL)
	{
		PyObject* sys_dict = PyModule_GetDict(sysmod);
		PyObject* path_list = PyDict_GetItemString(sys_dict, "path");

		printf("Adding path %s\n", path.c_str());
		PyObject* more_path = PyString_FromString(path.c_str());
		PyList_Append(path_list, more_path);

		Py_DECREF(sysmod);
	}
}

void PythonInterface::finiPython()
{
	if (Py_IsInitialized() != 0)
	{
		Py_Finalize();
	}
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : CompileString
//  PARAMETERS : command       - string of commands to execute in the...
//             : filename      - filename to say where to code came from
//
//  PURPOSE    : run a python string in a specific module name
//
PyObject* PythonInterface::CompileString(char *command, char* filename)
{
	PyObject* pycode = Py_CompileString(command, filename, Py_file_input);
	return pycode;
}


/////////////////////////////////////////////////////////////////////////////
//
//  Function   : DumpObject
//  PARAMETERS : pyobject       - string of commands to execute in the...
//
//  PURPOSE    : marshals an object into a char string
//
hsBool PythonInterface::DumpObject(PyObject* pyobj, char** pickle, Int32* size)
{
	PyObject *s;		// the python string object where the marsalled object wil go
	// convert object to a marshalled string python object
	s = PyMarshal_WriteObjectToString(pyobj);
	// did it actually do it?
	if ( s != NULL )
	{
		// yes, then get the size and the string address
		*size = PyString_Size(s);
		*pickle =  PyString_AsString(s);
		return true;
	}
	else  // otherwise, there was an error
	{
		// Yikes! errors!
		PyErr_Print();	// FUTURE: we may have to get the string to display in max...later
		return false;
	}
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : getOutputAndReset
//  PARAMETERS : none
//
//  PURPOSE    : get the Output to the error file to be displayed
//
int PythonInterface::getOutputAndReset(char** line)
{
	PyObject* pyStr = (*PycStringIO->cgetvalue)(stdFile);
	char *str = PyString_AsString( pyStr );
	int size = PyString_Size( pyStr );

	// reset the file back to zero
	PyObject_CallMethod(stdFile,"reset","");
/*
	// check to see if the debug python module is loaded
	if ( dbgOut != nil )
	{
		// then send it the new text
		if ( PyObject_CallFunction(dbgOut,"s",str) == nil )
		{
			// for some reason this function didn't, remember that and not call it again
			dbgOut = nil;
			// if there was an error make sure that the stderr gets flushed so it can be seen
			PyErr_Print();		// make sure the error is printed
			PyErr_Clear();		// clear the error
		}
	}
*/
	if (line)
		*line = str;
	return size;
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : CreateModule
//  PARAMETERS : module    - module name to create
//
//  PURPOSE    : create a new module with built-ins
//
PyObject* PythonInterface::CreateModule(char* module)
{
	PyObject *m, *d;
// first we must get rid of any old modules of the same name, we'll replace it
	PyObject *modules = PyImport_GetModuleDict();
	if ((m = PyDict_GetItemString(modules, module)) != NULL && PyModule_Check(m))
		// clear it
		_PyModule_Clear(m);

// create the module
	m = PyImport_AddModule(module);
	if (m == NULL)
		return nil;
	d = PyModule_GetDict(m);
// add in the built-ins
	// first make sure that we don't already have the builtins
	if (PyDict_GetItemString(d, "__builtins__") == NULL)
	{
		// if we need the builtins then find the builtin module
		PyObject *bimod = PyImport_ImportModule("__builtin__");
		// then add the builtin dicitionary to our module's dictionary
		if (bimod == NULL || PyDict_SetItemString(d, "__builtins__", bimod) != 0)
			return nil;
		Py_DECREF(bimod);
	}
	return m;
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : RunPYC
//  PARAMETERS : code      - compiled code
//             : module    - module name to run the code in
//
//  PURPOSE    : run a compiled python code in a specific module name
//
hsBool PythonInterface::RunPYC(PyObject* code, PyObject* module)
{
	PyObject *d, *v;
	// make sure that we're given a good module... or at least one with an address
	if ( !module )
	{
		// if no module was given then use just use the main module
		module = PyImport_AddModule("__main__");
		if (module == NULL)
			return false;
	}
	// get the dictionaries for this module
	d = PyModule_GetDict(module);
	// run the string
	v = PyEval_EvalCode((PyCodeObject*)code, d, d);
	// check for errors and print them
	if (v == NULL)
	{
		// Yikes! errors!
		PyErr_Print();
		return false;
	}
	Py_DECREF(v);
	if (Py_FlushLine())
		PyErr_Clear();
	return true;
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : GetModuleItem
//  PARAMETERS : item    - what item in the plasma module to get
//
//  PURPOSE    : get an item (probably a function) from a specific module
//
PyObject* PythonInterface::GetModuleItem(char* item, PyObject* module)
{
	if ( module )
	{
		PyObject* d = PyModule_GetDict(module);
		return PyDict_GetItemString(d, item);
	}
	return nil;
}