// Cyphesis Online RPG Server and AI Engine
// Copyright (C) 2000 Alistair Riddoch
//
// 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA

// $Id: Py_CreatorClient.cpp,v 1.37 2007-12-02 23:49:05 alriddoch Exp $

#include "Py_CreatorClient.h"

#include "CreatorClient.h"

#include "rulesets/Py_Operation.h"
#include "rulesets/Py_RootEntity.h"
#include "rulesets/Py_WorldTime.h"
#include "rulesets/Py_Point3D.h"
#include "rulesets/Py_Location.h"
#include "rulesets/Py_Object.h"
#include "rulesets/Py_Thing.h"
#include "rulesets/Py_Map.h"

#include "common/debug.h"
#include "common/inheritance.h"

using Atlas::Message::Element;
using Atlas::Message::MapType;

static const bool debug_flag = false;

static PyObject * CreatorClient_as_entity(PyCreatorClient * self, PyObject *)
{
#ifndef NDEBUG
    if (self->m_mind == NULL) {
        PyErr_SetString(PyExc_AssertionError, "NULL CreatorClient in CreatorClient.as_entity");
        return NULL;
    }
#endif // NDEBUG
    PyMessageElement * ret = newPyMessageElement();
    if (ret == NULL) {
        return NULL;
    }
    ret->m_obj = new Element(MapType());
    self->m_mind->addToMessage(ret->m_obj->asMap());
    return (PyObject *)ret;
}

static PyObject * CreatorClient_make(PyCreatorClient * self,
                                     PyRootEntity * entity)
{
#ifndef NDEBUG
    if (self->m_mind == NULL) {
        PyErr_SetString(PyExc_AssertionError, "NULL CreatorClient in CreatorClient.make");
        return NULL;
    }
#endif // NDEBUG
    if (!PyRootEntity_Check(entity)) {
        PyErr_SetString(PyExc_TypeError, "Can only make Atlas entity");
        return NULL;
    }
    LocatedEntity * retval = self->m_mind->make(entity->entity);
    if (retval == NULL) {
        PyErr_SetString(PyExc_RuntimeError, "Entity creation failed");
        return NULL;
    }
    PyLocatedEntity * ret = newPyLocatedEntity();
    ret->m_entity = retval;
    return (PyObject *)ret;
}

static PyObject * CreatorClient_set(PyCreatorClient * self, PyObject * args)
{
#ifndef NDEBUG
    if (self->m_mind == NULL) {
        PyErr_SetString(PyExc_AssertionError, "NULL CreatorClient in CreatorClient.set");
        return NULL;
    }
#endif // NDEBUG
    PyRootEntity * entity = NULL;
    char * id = NULL;
    if (!PyArg_ParseTuple(args, "sO", &id, &entity)) {
        return NULL;
    }
    if (!PyRootEntity_Check(entity)) {
        PyErr_SetString(PyExc_TypeError, "Can only set Atlas entity");
        return NULL;
    }
    self->m_mind->sendSet(id, entity->entity);
    Py_INCREF(Py_None);
    return Py_None;
}

static PyObject * CreatorClient_look(PyCreatorClient * self, PyObject * py_id)
{
#ifndef NDEBUG
    if (self->m_mind == NULL) {
        PyErr_SetString(PyExc_AssertionError, "NULL CreatorClient in CreatorClient.look");
        return NULL;
    }
#endif // NDEBUG
    if (!PyString_CheckExact(py_id)) {
        PyErr_SetString(PyExc_TypeError, "CreatorClient.look must be a string");
        return NULL;
    }
    char * id = PyString_AsString(py_id);
    LocatedEntity * retval = self->m_mind->look(id);
    if (retval == NULL) {
        PyErr_SetString(PyExc_RuntimeError, "Entity look failed");
        return NULL;
    }
    PyLocatedEntity * ret = newPyLocatedEntity();
    ret->m_entity = retval;
    return (PyObject *)ret;
}
static PyObject * CreatorClient_look_for(PyCreatorClient * self,
                                         PyRootEntity * ent)
{
#ifndef NDEBUG
    if (self->m_mind == NULL) {
        PyErr_SetString(PyExc_AssertionError, "NULL CreatorClient in CreatorClient.look_for");
        return NULL;
    }
#endif // NDEBUG
    if (!PyRootEntity_Check(ent)) {
        PyErr_SetString(PyExc_TypeError, "Can only look for Atlas description");
        return NULL;
    }
    LocatedEntity * retval = self->m_mind->lookFor(ent->entity);
    if (retval == NULL) {
        PyErr_SetString(PyExc_RuntimeError, "Entity look_for failed");
        return NULL;
    }
    PyLocatedEntity * ret = newPyLocatedEntity();
    ret->m_entity = retval;
    return (PyObject *)ret;
}

static PyObject * CreatorClient_send(PyCreatorClient * self, PyOperation * op)
{
#ifndef NDEBUG
    if (self->m_mind == NULL) {
        PyErr_SetString(PyExc_AssertionError, "NULL CreatorClient in CreatorClient.send");
        return NULL;
    }
#endif // NDEBUG
    if (!PyOperation_Check(op)) {
        PyErr_SetString(PyExc_TypeError, "Can only send Atlas operation");
        return NULL;
    }
    self->m_mind->send(op->operation);
    Py_INCREF(Py_None);
    return Py_None;
}

static PyMethodDef CreatorClient_methods[] = {
	{"as_entity",      (PyCFunction)CreatorClient_as_entity, METH_NOARGS},
	{"make",           (PyCFunction)CreatorClient_make,      METH_O},
	{"set",            (PyCFunction)CreatorClient_set,       METH_VARARGS},
	{"look",           (PyCFunction)CreatorClient_look,      METH_O},
	{"look_for",       (PyCFunction)CreatorClient_look_for,  METH_O},
	{"send",           (PyCFunction)CreatorClient_send,      METH_O},
	{NULL,          NULL}           /* sentinel */
};

static void CreatorClient_dealloc(PyCreatorClient *self)
{
    //if (self->m_mind != NULL) {
        //delete self->m_mind;
    //}
    Py_XDECREF(self->CreatorClient_attr);
    PyMem_DEL(self);
}

static PyObject * CreatorClient_getattr(PyCreatorClient *self, char *name)
{
    // Fairly major re-write of this to use operator[] of CreatorClient base class
#ifndef NDEBUG
    if (self->m_mind == NULL) {
        PyErr_SetString(PyExc_AssertionError, "NULL CreatorClient in CreatorClient.getattr");
        return NULL;
    }
#endif // NDEBUG
    // If operation search gets to here, it goes no further
    if (strstr(name, "_operation") != NULL) {
        PyErr_SetString(PyExc_AttributeError, name);
        return NULL;
    }
    if (strcmp(name, "type") == 0) {
        PyObject * list = PyList_New(0);
        if (list == NULL) {
            return NULL;
        }
        PyList_Append(list, PyString_FromString(self->m_mind->getType()->name().c_str()));
        return list;
    }
    if (strcmp(name, "map") == 0) {
        PyMap * map = newPyMap();
        map->m_map = self->m_mind->getMap();
        return (PyObject *)map;
    }
    if (strcmp(name, "location") == 0) {
        PyLocation * loc = newPyLocation();
        loc->location = &self->m_mind->m_location;
        loc->owner = self->m_mind;
        return (PyObject *)loc;
    }
    if (strcmp(name, "time") == 0) {
        PyWorldTime * worldtime = newPyWorldTime();
        worldtime->time = self->m_mind->getTime();
        return (PyObject *)worldtime;
    }
    if (self->CreatorClient_attr != NULL) {
        PyObject *v = PyDict_GetItemString(self->CreatorClient_attr, name);
        if (v != NULL) {
            Py_INCREF(v);
            return v;
        }
    }
    LocatedEntity * thing = self->m_mind;
    Element attr;
    if (!thing->getAttr(name, attr)) {
        return Py_FindMethod(CreatorClient_methods, (PyObject *)self, name);
    }
    PyObject * ret = MessageElement_asPyObject(attr);
    if (ret == NULL) {
        return Py_FindMethod(CreatorClient_methods, (PyObject *)self, name);
    }
    return ret;
}

static int CreatorClient_setattr(PyCreatorClient *self, char *name, PyObject *v)
{
    if (self->m_mind == NULL) {
        return -1;
    }
    if (self->CreatorClient_attr == NULL) {
        self->CreatorClient_attr = PyDict_New();
        if (self->CreatorClient_attr == NULL) {
            return -1;
        }
    }
    if (strcmp(name, "map") == 0) {
        return -1;
    }
    LocatedEntity * thing = self->m_mind;
    //std::string attr(name);
    //if (v == NULL) {
        //thing->attributes.erase(attr);
        //return 0;
    //}
    Element obj = PyObject_asMessageElement(v);
    if (!obj.isNone() && !obj.isMap() && !obj.isList()) {
        thing->setAttr(name, obj);
        return 0;
    }
    // If we get here, then the attribute is not Atlas compatable, so we
    // need to store it in a python dictionary
    return PyDict_SetItemString(self->CreatorClient_attr, name, v);
}

static int CreatorClient_compare(PyCreatorClient *self, PyCreatorClient *other)
{
    if (self->m_mind == NULL || other->m_mind == NULL) {
        return -1;
    }
    return (self->m_mind == other->m_mind) ? 0 : 1;
}

PyTypeObject PyCreatorClient_Type = {
	PyObject_HEAD_INIT(&PyType_Type)
	0,					/*ob_size*/
	"cppCreatorClient",			/*tp_name*/
	sizeof(PyCreatorClient),		/*tp_basicsize*/
	0,					/*tp_itemsize*/
	/* methods */
	(destructor)CreatorClient_dealloc,	/*tp_dealloc*/
	0,					/*tp_print*/
	(getattrfunc)CreatorClient_getattr,	/*tp_getattr*/
	(setattrfunc)CreatorClient_setattr,	/*tp_setattr*/
	(cmpfunc)CreatorClient_compare,		/*tp_compare*/
	0,					/*tp_repr*/
	0,					/*tp_as_number*/
	0,					/*tp_as_sequence*/
	0,					/*tp_as_mapping*/
	0,					/*tp_hash*/
};

PyCreatorClient * newPyCreatorClient()
{
    PyCreatorClient * self;
    self = PyObject_NEW(PyCreatorClient, &PyCreatorClient_Type);
    if (self == NULL) {
        return NULL;
    }
    self->CreatorClient_attr = NULL;
    return self;
}

int runClientScript(CreatorClient * c, const std::string & package,
                                       const std::string & func)
{
    PyObject * package_name = PyString_FromString(package.c_str());
    PyObject * mod_dict = PyImport_Import(package_name);
    Py_DECREF(package_name);
    if (mod_dict == NULL) {
        std::cerr << "Cld not find python module " << package
                  << std::endl << std::flush;
        PyErr_Print();
        return -1;
    }
    PyObject * function = PyObject_GetAttrString(mod_dict,
                                                 (char *)func.c_str());
    Py_DECREF(mod_dict);
    if (function == NULL) {
        std::cerr << "Could not find " << func << " function" << std::endl
                  << std::flush;
        PyErr_Print();
        return -1;
    }
    if (PyCallable_Check(function) == 0) {
        std::cerr << "It does not seem to be a function at all" << std::endl
                  << std::flush;
        Py_DECREF(function);
        return -1;
    }
    PyCreatorClient * editor = newPyCreatorClient();
    editor->m_mind = c;
    PyObject * pyob = PyEval_CallFunction(function, "(O)", editor);

    if (pyob == NULL) {
        if (PyErr_Occurred() == NULL) {
            std::cerr << "Could not call function" << std::endl << std::flush;
        } else {
            std::cerr << "Reporting python error" << std::endl << std::flush;
            PyErr_Print();
        }
    }
    Py_DECREF(function);
    return 0;

}


syntax highlighted by Code2HTML, v. 0.9.1