// 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_Mind.cpp,v 1.42 2007-12-02 23:49:07 alriddoch Exp $

#include "Py_Mind.h"
#include "Py_Thing.h"
#include "Py_Map.h"
#include "Py_Object.h"
#include "Py_Vector3D.h"
#include "Py_Point3D.h"
#include "Py_Location.h"
#include "Py_World.h"
#include "Py_WorldTime.h"
#include "PythonWrapper.h"
#include "BaseMind.h"

#include "common/inheritance.h"

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

// This method is almost profoundly pointless. Remove it soon, once we
// sort out something to do with the one placed it is used in the python
// mind code.
static PyObject * Mind_as_entity(PyMind * self)
{
#ifndef NDEBUG
    if (self->m_mind == NULL) {
        PyErr_SetString(PyExc_AssertionError, "invalid mind as_entity");
        return NULL;
    }
#endif // NDEBUG
    PyMessageElement * ret = newPyMessageElement();
    if (ret == NULL) {
        PyErr_SetString(PyExc_MemoryError, "error creating MessageElement");
        return NULL;
    }
    ret->m_obj = new Element(MapType());
    self->m_mind->addToMessage(ret->m_obj->asMap());
    return (PyObject *)ret;
}

static PyMethodDef Mind_methods[] = {
    {"as_entity",        (PyCFunction)Mind_as_entity,  METH_NOARGS},
    {NULL,               NULL}           // sentinel
};

static void Mind_dealloc(PyMind *self)
{
    Py_XDECREF(self->Mind_attr);
    PyObject_Free(self);
}

static PyObject * Mind_getattr(PyMind *self, char *name)
{
#ifndef NDEBUG
    if (self->m_mind == NULL) {
        PyErr_SetString(PyExc_AssertionError, "NULL mind in Mind.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, "id") == 0) {
        return (PyObject *)PyString_FromString(self->m_mind->getId().c_str());
    }
    if (strcmp(name, "type") == 0) {
        PyObject * list = PyList_New(0);
        if (list == NULL) {
            return NULL;
        }
        PyObject * ent = PyString_FromString(self->m_mind->getType()->name().c_str());
        PyList_Append(list, ent);
        Py_DECREF(ent);
        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 (strcmp(name, "contains") == 0) {
        PyObject * list = PyList_New(0);
        if (list == NULL) {
            return NULL;
        }
        LocatedEntitySet::const_iterator I = self->m_mind->m_contains.begin();
        LocatedEntitySet::const_iterator Iend = self->m_mind->m_contains.end();
        for (; I != Iend; ++I) {
            LocatedEntity * child = *I;
            PyObject * wrapper = wrapEntity(child);
            if (wrapper == NULL) {
                Py_DECREF(list);
                return NULL;
            }
            PyList_Append(list, wrapper);
            Py_DECREF(wrapper);
        }
        return list;
    }
    if (self->Mind_attr != NULL) {
        PyObject *v = PyDict_GetItemString(self->Mind_attr, name);
        if (v != NULL) {
            Py_INCREF(v);
            return v;
        }
    }
    LocatedEntity * mind = self->m_mind;
    Element attr;
    if (mind->getAttr(name, attr)) {
        return MessageElement_asPyObject(attr);
    }
    return Py_FindMethod(Mind_methods, (PyObject *)self, name);
}

static int Mind_setattr(PyMind *self, char *name, PyObject *v)
{
#ifndef NDEBUG
    if (self->m_mind == NULL) {
        PyErr_SetString(PyExc_AssertionError, "NULL mind in Mind.getattr");
        return -1;
    }
#endif // NDEBUG
    if (strcmp(name, "map") == 0) {
        return -1;
    }
    LocatedEntity * entity = self->m_mind;
    // Should we support removal of attributes?
    //std::string attr(name);
    //if (v == NULL) {
        //entity->attributes.erase(attr);
        //return 0;
    //}
    Element obj = PyObject_asMessageElement(v, true);
    if (!obj.isNone()) {
        assert(!obj.isMap() && !obj.isList());
        // In the Python wrapper for Entity in Py_Thing.cpp notices are issued
        // for some types.
        entity->setAttr(name, obj);
        return 0;
    }
    // Minds set a number of native Python members on themselves to store
    // important state information, which seems to be lost if we munge them
    // into Atlas data, probably because of the weird stuff that happens
    // in the wrappers when scripts manipulate complex attributes. They
    // are copied on getattr, rather than referenced.
    // If we get here, then the attribute is not Atlas compatable, so we
    // need to store it in a python dictionary
    if (self->Mind_attr == NULL) {
        self->Mind_attr = PyDict_New();
        if (self->Mind_attr == NULL) {
            return -1;
        }
    }
    return PyDict_SetItemString(self->Mind_attr, name, v);
}

static int Mind_compare(PyMind *self, PyMind *other)
{
    if (self->m_mind == NULL || other->m_mind == NULL) {
        PyErr_SetString(PyExc_AssertionError, "NULL mind in Mind.compare");
        return -1;
    }
    return (self->m_mind == other->m_mind) ? 0 : 1;
}

PyTypeObject PyMind_Type = {
    PyObject_HEAD_INIT(&PyType_Type)
    0,                              // ob_size
    "Mind",                         // tp_name
    sizeof(PyMind),                 // tp_basicsize
    0,                              // tp_itemsize
    // methods 
    (destructor)Mind_dealloc,       // tp_dealloc
    0,                              // tp_print
    (getattrfunc)Mind_getattr,      // tp_getattr
    (setattrfunc)Mind_setattr,      // tp_setattr
    (cmpfunc)Mind_compare,          // tp_compare
    0,                              // tp_repr
    0,                              // tp_as_number
    0,                              // tp_as_sequence
    0,                              // tp_as_mapping
    0,                              // tp_hash
};

PyMind * newPyMind()
{
    PyMind * self;
    self = PyObject_NEW(PyMind, &PyMind_Type);
    if (self == NULL) {
        return NULL;
    }
    self->Mind_attr = NULL;
    return self;
}


syntax highlighted by Code2HTML, v. 0.9.1