// K-3D
// Copyright (c) 1995-2004, Timothy M. Shead
//
// Contact: tshead@k-3d.com
//
// 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 2 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, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

/** \file
		\brief Implements Python engine, an implementation of k3d::iscript_engine that supports the Python language
		\author Anders Dahnielson (anders@dahnielson.com)
		\author Romain Behar (romainbehar@yahoo.com)
		\author Adam Hupp (hupp@cs.wisc.edu)
*/

#include <k3dsdk/application.h>
#include <k3dsdk/classes.h>
#include <k3dsdk/command_node.h>
#include <k3dsdk/file_helpers.h>
#include <k3dsdk/ideletable.h>
#include <k3dsdk/idocument.h>
#include <k3dsdk/iobject.h>
#include <k3dsdk/iscript_engine.h>
#include <k3dsdk/module.h>
#include <k3dsdk/string_modifiers.h>

#include "object_model.h"

#include <boost/filesystem/fstream.hpp>
#include <boost/filesystem/operations.hpp>
#include <boost/filesystem/path.hpp>

/// Namespace reserved for the Python scripting engine module, to protect public symbols from name clashes with other modules
namespace libk3dpython
{

const std::string magic_token("#python");

class engine :
	public k3d::iscript_engine,
	public k3d::ideletable
{
public:
	engine()
	{
		// Fire up the interpreter
#if defined K3D_PLATFORM_IS_OSX
		PyMac_Initialize();
#else
		Py_Initialize();
#endif
	}

	virtual ~engine()
	{
		// Shut down the interpreter
		if(Py_IsInitialized())
			Py_Finalize();
	}

	// k3d::iscript_engine implementation ...
	const std::string language()
	{
		return "Python";
	}

	bool can_execute(const std::string& Script)
	{
		// Look for magic token
		return Script.substr(0, magic_token.size()) == magic_token;
	}

	bool execute(const std::string& ScriptName, const std::string& Script, const context_t& Context)
	{
		// Sanity checks ...
		return_val_if_fail(Script.size(), false);
		return_val_if_fail(ScriptName.size(), false);

		// ReInitialize the Interpreter ...
		if(Py_IsInitialized())
			Py_Finalize();
		Py_Initialize();

		// Init modules
		init_modules();

		Py_XDECREF(m_document_context);
		m_document_context = 0;

		Py_XDECREF(m_object_context);
		m_object_context = 0;

		for(context_t::const_iterator i = Context.begin(); i != Context.end(); ++i)
			{
				if(k3d::idocument* j = dynamic_cast<k3d::idocument*>(*i))
					{
						m_document_context = CPythonDocument::Create(j);
						return_val_if_fail(m_document_context, false);
					}
				else if(k3d::iobject* j = dynamic_cast<k3d::iobject*>(*i))
					{
						m_object_context = CPythonObject::Create(j);
						return_val_if_fail(m_object_context, false);
					}
			}

		Py_InitModule("_My", Methods);

		PyRun_SimpleString("import _My");

		if(m_document_context)
			PyRun_SimpleString("MyDocument = DocumentClass(_My.MyDocument())");
		else
			PyRun_SimpleString("MyDocument = None");

		if(m_object_context)
			PyRun_SimpleString("MyObject = ObjectClass(_My.MyObject())");
		else
			PyRun_SimpleString("MyObject = None");

		// Evaluate the script ...
		int returncode;
		returncode = PyRun_SimpleString((char*) Script.c_str());

		return (returncode == 0) ? true : false;
	}

	bool halt()
	{
		return false;
	}

	void bless_script(std::string& Script)
	{
		// Don't keep adding magic tokens to code that already has one ...
		if(can_execute(Script))
			return;

		Script.insert(Script.begin(), magic_token.begin(), magic_token.end());
	}

	bool convert_command(k3d::icommand_node& CommandNode, const std::string& Name, const std::string& Arguments, std::string& Result)
	{
		Result = "Application.CommandNode(\"";
		Result += k3d::command_node_path(CommandNode);
		Result += "\").Command(\"";
		Result += Name;
		Result += "\", \"";
		Result += k3d::replace_all("\"", "\\\"", Arguments); // Make sure arguments are properly escaped
		Result += "\");";

		return true;
	}

private:
	static PyObject *m_document_context;
	static PyObject *m_object_context;
	static PyMethodDef Methods[];

	bool init_modules()
	{
		// Initialize the Modules ...
		Py_InitModule("_ApplicationModule", CPythonApplication::Methods);
		Py_InitModule("_PluginFactoryModule", CPythonPluginFactory::Methods);
		Py_InitModule("_CommandNodeModule", CPythonCommandNode::Methods);
		Py_InitModule("_PropertyModule", CPythonProperty::Methods);
		Py_InitModule("_DocumentModule", CPythonDocument::Methods);
		Py_InitModule("_BezierChannelModule", CPythonBezierChannel::Methods);
		Py_InitModule("_ObjectModule", CPythonObject::Methods);

		Py_InitModule("_UIModule", CPythonUserInterface::Methods);
		Py_InitModule("_ScriptEnginesModule", CPythonScriptEngines::Methods);

		Py_InitModule("_mesh_module", python_mesh::methods);
		Py_InitModule("_point_module", python_point::methods);
		Py_InitModule("_polyhedron_module", python_polyhedron::methods);
		Py_InitModule("_face_module", python_face::methods);
		Py_InitModule("_blobby_module", python_blobby_opcode::methods);

		// Initialize the classes ...
		boost::filesystem::path python_interface(k3d::application().share_path() / "python_engine/k3d_interface.py");
		if(!boost::filesystem::exists(python_interface))
			{
				std::cerr << error << "Python Engine file 'k3d_interface.py' not found" << std::endl;
				return false;
			}

		boost::filesystem::ifstream py_file(python_interface);
		return_val_if_fail(py_file.good(), false);

		// Load Python interface ...
		std::string k3d_modules;
		while(!py_file.eof())
			{
				std::string line;
				k3d::getline(py_file, line);
				k3d_modules.append(line + "\n");
			}

		PyRun_SimpleString(const_cast<char*>(k3d_modules.c_str()));

		return true;
	}

	static PyObject* MyDocument(PyObject* self, PyObject* args)
	{
		if(m_document_context)
			{
				Py_INCREF(m_document_context);
				return m_document_context;
			}

		return Py_None;
	}

	static PyObject* MyObject(PyObject* self, PyObject* args)
	{
		if(m_object_context)
			{
				Py_INCREF(m_object_context);
				return m_object_context;
			}

		return Py_None;
	}
};

PyObject* engine::m_document_context = 0;
PyObject* engine::m_object_context = 0;

PyMethodDef engine::Methods[] =
{
	{"MyDocument", MyDocument, METH_VARARGS, "Return MyDocument"},
	{"MyObject", MyObject, METH_VARARGS, "Return MyObject"},
	{NULL, NULL, 0, NULL}
};

k3d::iplugin_factory& engine_factory()
{
	static k3d::plugin_factory<k3d::application_plugin<engine>, k3d::interface_list<k3d::iscript_engine> > factory(
		k3d::classes::PythonEngine(),
		"Python",
		"Python scripting engine",
		"ScriptEngines");

	return factory;
}

} // namespace libk3dpython

K3D_MODULE_START(k3d::uuid(0x9d1e8e42, 0x3dc7422c, 0xbe1c3215, 0x99bd67c3), Registry)
	Registry.register_factory(libk3dpython::engine_factory());
K3D_MODULE_END


