#include <sys/stat.h>
#include <Python.h>
#include <ygNode.h>
#include <ygNodeDB.h>
#include <ygUtil.h>
#include <ygWorld.h>
#include "pyInterface.h"

using namespace std;

struct _pyInterfacePrivateData
	{
	ygNode * node;
	ygString fileName, pyClassName;
	bool autoReset;
	time_t lastModified;
	};


static PyObject* emb_eventOccurred(PyObject *self, PyObject *args)
	{
	char *nodeName, *eventName, *eventArgs;
	if (!PyArg_ParseTuple(args, "sss", &nodeName, &eventName, &eventArgs))
        	return NULL;
	ygNode * node = ygNodeDB::find(nodeName);
	if (node)
		node->eventOccurred(eventName,eventArgs);
	Py_INCREF(Py_None);
	return Py_None;
	}


static PyObject* emb_sendMessage(PyObject *self, PyObject *args)
	{
	char *messageString;
	if (!PyArg_ParseTuple(args, "s", &messageString))
        	return NULL;
	ygMessage msg;
	msg.parseString(messageString);
	ygWorld::World->scheduleMessage(msg);
	Py_INCREF(Py_None);
	return Py_None;
	}


static PyObject* emb_frameTime(PyObject *self, PyObject *args)
	{
	if (!PyArg_ParseTuple(args, ""))
        	return NULL;
	return Py_BuildValue("f", ygWorld::FrameTime);
	}


static PyObject* emb_frameDeltaTime(PyObject *self, PyObject *args)
	{
	if (!PyArg_ParseTuple(args, ""))
        	return NULL;
	return Py_BuildValue("f", ygWorld::FrameDeltaTime);
	}


static PyObject* emb_numChildren(PyObject *self, PyObject *args)
	{
	char *nodeName;
	int value=0;
	if (!PyArg_ParseTuple(args, "s", &nodeName))
        	return NULL;
	ygNode * node = ygNodeDB::find(nodeName);
	if (node)
		value = node->numChildren();
	return Py_BuildValue("i", value);
	}


static PyObject* emb_childName(PyObject *self, PyObject *args)
	{
	char *nodeName;
	int num;
	if (!PyArg_ParseTuple(args, "si", &nodeName, &num))
        	return NULL;
	ygNode * node = ygNodeDB::find(nodeName);
	if ((node) && (node->child(num)))
		return Py_BuildValue("s", node->child(num)->name().c_str());
	else
		return Py_BuildValue("s","");
	}


static PyObject* emb_parentName(PyObject *self, PyObject *args)
	{
	char *nodeName;
	if (!PyArg_ParseTuple(args, "s", &nodeName))
        	return NULL;
	ygNode * node = ygNodeDB::find(nodeName);
	if ((node) && (node->parent()))
		return Py_BuildValue("s", node->parent()->name().c_str());
	else
		return Py_BuildValue("s","");
	}


static PyObject* emb_origin(PyObject *self, PyObject *args)
	{
	char *nodeName, *otherName;
	if (!PyArg_ParseTuple(args, "ss", &nodeName, &otherName))
        	return NULL;
	ygNode * node = ygNodeDB::find(nodeName);
	ygNode * otherNode=NULL;
	if ((otherName) && (strlen(otherName) > 0))
		otherNode = ygNodeDB::find(nodeName);
	if (node)
		{
		pfVec3 pos = node->origin(otherNode);
		return Py_BuildValue("(fff)", pos[0], pos[1], pos[2]);
		}
	else
		return Py_BuildValue("(fff)",0,0,0);
	}


static PyMethodDef EmbMethods[] =
	{
	{"eventOccurred", emb_eventOccurred, METH_VARARGS,
		"Generate an Ygdrasil event."},
	{"sendMessage", emb_sendMessage, METH_VARARGS,
		"Send an Ygdrasil message."},
	{"frameTime", emb_frameTime, METH_VARARGS,
		"Return the current Ygdrasil frame time, in seconds."},
	{"frameDeltaTime", emb_frameDeltaTime, METH_VARARGS,
		"Return the current Ygdrasil frame duration, in seconds."},
	{"numChildren", emb_numChildren, METH_VARARGS,
		"Return the number of children of an Ygdrasil node."},
	{"childName", emb_childName, METH_VARARGS,
		"Return the name of an Ygdrasil node's child."},
	{"parentName", emb_parentName, METH_VARARGS,
		"Return the name of an Ygdrasil node's parent."},
	{"origin", emb_origin, METH_VARARGS,
		"Return the x/y/z origin of an Ygdrasil node."},
	{NULL, NULL, 0, NULL}
	};


static void initializePython(void)
	{
	static bool initted = false;
	if (initted)
		return;
	initted = true;
	Py_Initialize();
	Py_InitModule("ygdrasil", EmbMethods);
	PyRun_SimpleString("import ygdrasil\n");
	PyRun_SimpleString(
		"class ygNode:\n"
		"	def __init__(self):\n"
		"		self.ygNodeName = ''\n"
		"	def name(self):\n"
		"		return self.ygNodeName\n"
		"	def eventOccurred(self,e,args=''):\n"
		"		ygdrasil.eventOccurred(self.ygNodeName,e,args)\n"
		"	def numChildren(self):\n"
		"		return ygdrasil.numChildren(self.ygNodeName)\n"
		"	def childName(self,n):\n"
		"		return ygdrasil.childName(self.ygNodeName,n)\n"
		"	def parentName(self):\n"
		"		return ygdrasil.parentName(self.ygNodeName)\n"
		"	def origin(self,otherNode=''):\n"
		"		return ygdrasil.origin(self.ygNodeName,otherNode)\n"
		"	def app(self):\n"
		"		pass\n"
		);
	}


pyInterface::pyInterface(ygNode *node)
	{
	p_ = new struct _pyInterfacePrivateData;
	p_->node = node;
	p_->autoReset = false;
	p_->lastModified = 0;
	initializePython();
	}

pyInterface::~pyInterface(void)
	{
	delete p_;
	}


bool pyInterface::message(const ygMessage& msg)
	{
	if (msg == "script")
		{
		if (msg.args.size() < 1)
			msg.error(p_->node->name(),"(wrong number of arguments)");
		else if (msg.args.size() < 2)
			loadScript(msg.args[0],"");
		else
			loadScript(msg.args[0],msg.args[1]);
		}
	else if (msg == "reload")
		{
		reloadScript();
		}			
	else if (msg == "autoReset")
		{
		if (msg.args.size() > 0)
			setAutoReset(msg.boolArg(0));
		else
			setAutoReset(true);
		}
	else if (msg == "call")
		{
		if (msg.args.size() < 1)
			msg.error(p_->node->name(),"(insufficient arguments)");
		else
			callFunc(msg);
		}
	else
		return false;
	return true;
	}


void pyInterface::loadScript(const ygString& file,const ygString& clName)
	{
	ygString fullPath;
	if (!ygFindFileInYgPath(file,fullPath))
		{
		fprintf(stderr,"ERROR: %s: could not find Python script %s\n",
			p_->node->name().c_str(), file.c_str());
		return;
		}
	FILE *fp = fopen(fullPath.c_str(),"r");
	if (!fp)
		{
		fprintf(stderr,"ERROR: %s: could not open Python script %s\n",
			p_->node->name().c_str(), file.c_str());
		perror(file.c_str());
		return;
		}
	PyRun_SimpleFile(fp,fullPath.c_str());
	fclose(fp);
	struct stat info;
	if (stat(fullPath.c_str(),&info) != -1)
		p_->lastModified = info.st_mtime;
	p_->fileName = fullPath;
	if (clName.length() == 0)
		{
		p_->pyClassName = file;
		int lastSlash = p_->pyClassName.find_last_of("/");
		if (lastSlash != ygString::npos)
			p_->pyClassName.erase(0,lastSlash+1);
		p_->pyClassName.erase(p_->pyClassName.find('.'));
		}
	else
		p_->pyClassName = clName;
	ygString cmd(p_->node->name());
	cmd += " = ";
	cmd += p_->pyClassName;
	cmd += "()\n";
	PyRun_SimpleString(cmd.c_str());
	cmd = p_->node->name();
	cmd += ".ygNodeName = '";
	cmd += p_->node->name();
	cmd += "'\n";
	PyRun_SimpleString(cmd.c_str());
	}


void pyInterface::reloadScript(void)
	{
	if (p_->fileName.length() > 0)
		loadScript(p_->fileName, p_->pyClassName);
	}


void pyInterface::callFunc(const ygMessage& msg)
	{
	ygString cmd(p_->node->name());
	cmd += ".";
	cmd += msg.args[0];
	cmd += "\n";
	PyRun_SimpleString(cmd.c_str());
	}


void pyInterface::setAutoReset(bool val)
	{
	p_->autoReset = val;
	}


void pyInterface::checkFile(void)
	{
	struct stat info;
	if (stat(p_->fileName.c_str(),&info) != -1)
		{
		if (p_->lastModified != info.st_mtime)
			{
			ygMessage msg;
			msg.parseString("reset",p_->node);
			ygWorld::World->scheduleMessage(msg);
			}
		}
	}


void pyInterface::app(void)
	{
	if (p_->fileName.length() > 0)
		{
		if (p_->autoReset)
			checkFile();
		ygString cmd(p_->node->name());
		cmd += ".app()\n";
		PyRun_SimpleString(cmd.c_str());
		}
	}
