/* ====================================================================
 * Copyright (c) 2004-2006 Open Source Applications Foundation.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions: 
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software. 
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 * ====================================================================
 */

#ifndef _macros_h
#define _macros_h

PyObject *make_descriptor(PyObject *value);
PyObject *make_descriptor(PyTypeObject *value);
PyObject *make_descriptor(PyObject *(*get)(PyObject *));
PyObject *make_descriptor(jboolean b);

#if PY_VERSION_HEX < 0x02040000
#define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None
#define Py_RETURN_TRUE return Py_INCREF(Py_True), Py_True
#define Py_RETURN_FALSE return Py_INCREF(Py_False), Py_False
#endif


#define OBJ_CALL(action)                                                \
    {                                                                   \
        try {                                                           \
            PythonThreadState state;                                    \
            action;                                                     \
        } catch (org::osafoundation::util::PythonException *e) {        \
            return NULL;                                                \
        } catch (java::lang::Throwable *e) {                            \
            return PyErr_SetJavaError(e);                               \
        }                                                               \
    }

#define EXC_OBJ_CALL(action, exc, exc_action)                           \
    {                                                                   \
        try {                                                           \
            PythonThreadState state;                                    \
            action;                                                     \
        } catch (org::osafoundation::util::PythonException *e) {        \
            return NULL;                                                \
        } catch (exc *e) {                                              \
            exc_action;                                                 \
        } catch (java::lang::Throwable *e) {                            \
            return PyErr_SetJavaError(e);                               \
        }                                                               \
    }

#define SYNC_OBJ_CALL(obj, action)                                      \
    {                                                                   \
        try {                                                           \
            PythonThreadState state;                                    \
            JvSynchronize lock (obj);                                   \
            action;                                                     \
        } catch (org::osafoundation::util::PythonException *e) {        \
            return NULL;                                                \
        } catch (java::lang::Throwable *e) {                            \
            return PyErr_SetJavaError(e);                               \
        }                                                               \
    }

#define INT_CALL(action)                                                \
    {                                                                   \
        try {                                                           \
            PythonThreadState state;                                    \
            action;                                                     \
        } catch (org::osafoundation::util::PythonException *e) {        \
            return -1;                                                  \
        } catch (java::lang::Throwable *e) {                            \
            PyErr_SetJavaError(e);                                      \
            return -1;                                                  \
        }                                                               \
    }

#define DECLARE_METHOD(type, name, flags)               \
    { #name, (PyCFunction) type##_##name, flags, "" }

#define DECLARE_TYPE(name, j_name, base, javaClass, init)                   \
PyTypeObject name##Type = {                                                 \
    PyObject_HEAD_INIT(NULL)                                                \
    /* ob_size            */   0,                                           \
    /* tp_name            */   "PyLucene."#name,                            \
    /* tp_basicsize       */   sizeof(j_name),                              \
    /* tp_itemsize        */   0,                                           \
    /* tp_dealloc         */   0,                                           \
    /* tp_print           */   0,                                           \
    /* tp_getattr         */   0,                                           \
    /* tp_setattr         */   0,                                           \
    /* tp_compare         */   0,                                           \
    /* tp_repr            */   0,                                           \
    /* tp_as_number       */   0,                                           \
    /* tp_as_sequence     */   0,                                           \
    /* tp_as_mapping      */   0,                                           \
    /* tp_hash            */   0,                                           \
    /* tp_call            */   0,                                           \
    /* tp_str             */   0,                                           \
    /* tp_getattro        */   0,                                           \
    /* tp_setattro        */   0,                                           \
    /* tp_as_buffer       */   0,                                           \
    /* tp_flags           */   Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,    \
    /* tp_doc             */   #j_name" objects",                           \
    /* tp_traverse        */   0,                                           \
    /* tp_clear           */   0,                                           \
    /* tp_richcompare     */   0,                                           \
    /* tp_weaklistoffset  */   0,                                           \
    /* tp_iter            */   0,                                           \
    /* tp_iternext        */   0,                                           \
    /* tp_methods         */   j_name##_methods,                            \
    /* tp_members         */   0,                                           \
    /* tp_getset          */   0,                                           \
    /* tp_base            */   &base##Type,                                 \
    /* tp_dict            */   0,                                           \
    /* tp_descr_get       */   0,                                           \
    /* tp_descr_set       */   0,                                           \
    /* tp_dictoffset      */   0,                                           \
    /* tp_init            */   (initproc)init,                              \
    /* tp_alloc           */   0,                                           \
    /* tp_new             */   0,                                           \
};                                                                          \
PyObject *wrap_##name(javaClass *object)                                \
{                                                                       \
    if (object)                                                         \
    {                                                                   \
        j_name *self = (j_name *) name##Type.tp_alloc(&name##Type, 0);  \
        if (self)                                                       \
            self->object = (javaClass *) refObject(object);             \
        return (PyObject *) self;                                       \
    }                                                                   \
    Py_RETURN_NONE;                                                     \
}

#define INSTALL_TYPE(name, module)                                   \
    if (PyType_Ready(&name##Type) == 0)                              \
    {                                                                \
        Py_INCREF(&name##Type);                                      \
        PyModule_AddObject(module, #name, (PyObject *) &name##Type); \
    }

#define INSTALL_INNER_TYPE(type, name, inner)                        \
    if (PyType_Ready(&inner##Type) == 0)                             \
    {                                                                \
        Py_INCREF(&inner##Type);                                     \
        PyDict_SetItemString(type##Type.tp_dict, #name,              \
                             make_descriptor(&inner##Type));         \
    }

#define INSTALL_STATIC(type, name, varType, cls)                        \
    PyDict_SetItemString(type##Type.tp_dict, #name,                     \
                         make_descriptor(wrap_##varType(cls::name)))

#define INSTALL_STATIC_INT(type, name, cls)                             \
    PyDict_SetItemString(type##Type.tp_dict, #name,                     \
                         make_descriptor(PyInt_FromLong(cls::name)))

#define INSTALL_STATIC_LONG(type, name, cls)                            \
    PyDict_SetItemString(type##Type.tp_dict, #name,                     \
                         make_descriptor(PyLong_FromLongLong(cls::name)))

#define INSTALL_STATIC_LONG_rename(type, newname, oldname, cls)         \
    PyDict_SetItemString(type##Type.tp_dict, #newname,                  \
                         make_descriptor(PyLong_FromLongLong(cls::oldname)))

#define INSTALL_STATIC_BOOL(type, name, cls)                            \
    PyDict_SetItemString(type##Type.tp_dict, #name,                     \
                         make_descriptor((jboolean) cls::name))

#define INSTALL_STATIC_STRING(type, name, cls)                          \
    PyDict_SetItemString(type##Type.tp_dict, #name,                     \
                         make_descriptor(j2p(cls::name)))

#define INSTALL_STATIC_STRINGS(type, name, cls)                         \
    PyDict_SetItemString(type##Type.tp_dict, #name,                     \
                         make_descriptor(jsa2psl(cls::name)))


#define Py_RETURN_BOOL(b) \
    if (b)                \
        Py_RETURN_TRUE;   \
    else                  \
        Py_RETURN_FALSE;


#define DEFINE_BOOL_GETTER(name, j_type)                          \
    static PyObject *j_type##_is##name(j_type *self)              \
    {                                                             \
        jboolean b;                                               \
        OBJ_CALL(b = self->object->is##name());                   \
        Py_RETURN_BOOL(b);                                        \
    }

#define DEFINE_INT_GETTER(name, j_type)                           \
    static PyObject *j_type##_get##name(j_type *self)             \
    {                                                             \
        jint n;                                                   \
        OBJ_CALL(n = self->object->get##name());                  \
        return PyInt_FromLong(n);                                 \
    }

#define DEFINE_FLOAT_GETTER(name, j_type)                         \
    static PyObject *j_type##_get##name(j_type *self)             \
    {                                                             \
        jfloat f;                                                 \
        OBJ_CALL(f = self->object->get##name());                  \
        return PyFloat_FromDouble((double) f);                    \
    }

#define DEFINE_STRING_GETTER(name, j_type)                        \
    static PyObject *j_type##_get##name(j_type *self)             \
    {                                                             \
        jstring s;                                                \
        OBJ_CALL(s = self->object->get##name());                  \
        return j2p(s);                                            \
    }

#define DEFINE_OBJ_GETTER(name, j_type, type, javaClass)          \
    static PyObject *j_type##_get##name(j_type *self)             \
    {                                                             \
        javaClass *obj;                                           \
        OBJ_CALL(obj = self->object->get##name());                \
        return wrap_##type(obj);                                  \
    }

#define DEFINE_BOOL_GETSET(name, j_type)                                \
    static PyObject *j_type##_get##name(j_type *self)                   \
    {                                                                   \
        jboolean b;                                                     \
        OBJ_CALL(b = self->object->get##name());                        \
        Py_RETURN_BOOL(b);                                              \
    }                                                                   \
    static PyObject *j_type##_set##name(j_type *self, PyObject *arg)    \
    {                                                                   \
        jboolean b;                                                     \
        if (!parseArg(arg, "b", &b))                                    \
        {                                                               \
            OBJ_CALL(self->object->set##name(b));                       \
            Py_RETURN_NONE;                                             \
        }                                                               \
        return PyErr_SetArgsError((PyObject *) self, "set"#name, arg);  \
    }

#define DEFINE_BOOL_ISSET(name, j_type)                                 \
    static PyObject *j_type##_is##name(j_type *self)                    \
    {                                                                   \
        jboolean b;                                                     \
        OBJ_CALL(b = self->object->is##name());                         \
        Py_RETURN_BOOL(b);                                              \
    }                                                                   \
    static PyObject *j_type##_set##name(j_type *self, PyObject *arg)    \
    {                                                                   \
        jboolean b;                                                     \
        if (!parseArg(arg, "b", &b))                                    \
        {                                                               \
            OBJ_CALL(self->object->set##name(b));                       \
            Py_RETURN_NONE;                                             \
        }                                                               \
        return PyErr_SetArgsError((PyObject *) self, "set"#name, arg);  \
    }

#define DEFINE_INT_GETSET(name, j_type)                                 \
    static PyObject *j_type##_get##name(j_type *self)                   \
    {                                                                   \
        jint n;                                                         \
        OBJ_CALL(n = self->object->get##name());                        \
        return PyInt_FromLong(n);                                       \
    }                                                                   \
    static PyObject *j_type##_set##name(j_type *self, PyObject *arg)    \
    {                                                                   \
        jint n;                                                         \
        if (!parseArg(arg, "i", &n))                                    \
        {                                                               \
            OBJ_CALL(self->object->set##name(n));                       \
            Py_RETURN_NONE;                                             \
        }                                                               \
        return PyErr_SetArgsError((PyObject *) self, "set"#name, arg);  \
    }

#define DEFINE_LONG_GETSET(name, j_type)                                \
    static PyObject *j_type##_get##name(j_type *self)                   \
    {                                                                   \
        jlong n;                                                        \
        OBJ_CALL(n = self->object->get##name());                        \
        return PyLong_FromLongLong(n);                                  \
    }                                                                   \
    static PyObject *j_type##_set##name(j_type *self, PyObject *arg)    \
    {                                                                   \
        jlong n;                                                        \
        if (!parseArg(arg, "L", &n))                                    \
        {                                                               \
            OBJ_CALL(self->object->set##name(n));                       \
            Py_RETURN_NONE;                                             \
        }                                                               \
        return PyErr_SetArgsError((PyObject *) self, "set"#name, arg);  \
    }

#define DEFINE_FLOAT_GETSET(name, j_type)                               \
    static PyObject *j_type##_get##name(j_type *self)                   \
    {                                                                   \
        jfloat f;                                                       \
        OBJ_CALL(f = self->object->get##name());                        \
        return PyFloat_FromDouble((double) f);                          \
    }                                                                   \
    static PyObject *j_type##_set##name(j_type *self, PyObject *arg)    \
    {                                                                   \
        jdouble d;                                                      \
        if (!parseArg(arg, "d", &d))                                    \
        {                                                               \
            OBJ_CALL(self->object->set##name((jfloat) d));              \
            Py_RETURN_NONE;                                             \
        }                                                               \
        return PyErr_SetArgsError((PyObject *) self, "set"#name, arg);  \
    }

#define DEFINE_STRING_GETSET(name, j_type)                              \
    static PyObject *j_type##_get##name(j_type *self)                   \
    {                                                                   \
        jstring s;                                                      \
        OBJ_CALL(s = self->object->get##name());                        \
        return j2p(s);                                                  \
    }                                                                   \
    static PyObject *j_type##_set##name(j_type *self, PyObject *arg)    \
    {                                                                   \
        jstring s;                                                      \
        if (!parseArg(arg, "s", &s))                                    \
        {                                                               \
            OBJ_CALL(self->object->set##name(s));                       \
            Py_RETURN_NONE;                                             \
        }                                                               \
        return PyErr_SetArgsError((PyObject *) self, "set"#name, arg);  \
    }

#define DEFINE_OBJ_GETSET(name, j_type, type, javaClass)                \
    static PyObject *j_type##_get##name(j_type *self)                   \
    {                                                                   \
        javaClass *obj;                                                 \
        OBJ_CALL(obj = self->object->get##name());                      \
        return wrap_##type(obj);                                        \
    }                                                                   \
    static PyObject *j_type##_set##name(j_type *self, PyObject *arg)    \
    {                                                                   \
        javaClass *obj;                                                 \
        if (!parseArg(arg, "J", &javaClass::class$, &obj))              \
        {                                                               \
            OBJ_CALL(self->object->set##name(obj));                     \
            Py_RETURN_NONE;                                             \
        }                                                               \
        return PyErr_SetArgsError((PyObject *) self, "set"#name, arg);  \
    }

#define DEFINE_POBJ_GETSET(name, j_type, type, javaClass)               \
    static PyObject *j_type##_get##name(j_type *self)                   \
    {                                                                   \
        javaClass *obj;                                                 \
        OBJ_CALL(obj = self->object->get##name());                      \
        return wrap_##type(obj);                                        \
    }                                                                   \
    static PyObject *j_type##_set##name(j_type *self, PyObject *arg)    \
    {                                                                   \
        javaClass *obj;                                                 \
        if (!parseArg(arg, "P", &javaClass::class$,                     \
                      check_##type, &obj, make_##type))                 \
        {                                                               \
            OBJ_CALL(self->object->set##name(obj));                     \
            Py_RETURN_NONE;                                             \
        }                                                               \
        return PyErr_SetArgsError((PyObject *) self, "set"#name, arg);  \
    }

#define DEFINE_0_INIT(j_type, javaClass)                                \
    static int j_type##_init(j_type *self,                              \
                             PyObject *args, PyObject *kwds)            \
    {                                                                   \
        javaClass *object;                                              \
        switch (PyTuple_GET_SIZE(args)) {                               \
          case 0:                                                       \
            INT_CALL(object = new javaClass());                         \
            self->object = object;                                      \
            break;                                                      \
          default:                                                      \
            PyErr_SetArgsError((PyObject *) self, "__init__", args);    \
            return -1;                                                  \
        }                                                               \
        if (self->object)                                               \
        {                                                               \
            refObject(self->object);                                    \
            return 0;                                                   \
        }                                                               \
        return -1;                                                      \
    }

#define DECLARE_DOWNCAST(j_type, type)                                  \
    static PyObject *j_type##_to##type(PyObject *self);                 \
    static PyObject *j_type##_is##type(PyObject *self);
    
#define DECLARE_DOWNCAST_METHODS(j_type, type)                          \
    DECLARE_METHOD(j_type, to##type, METH_NOARGS),                      \
    DECLARE_METHOD(j_type, is##type, METH_NOARGS)
    
#define DEFINE_DOWNCAST(j_type, type, javaClass)                        \
    static PyObject *j_type##_to##type(PyObject *self)                  \
    {                                                                   \
        javaClass *obj;                                                 \
        if (!parseArg(self, "J", &javaClass::class$, &obj))             \
            return wrap_##type(obj);                                    \
        PyErr_SetObject(PyExc_TypeError, (PyObject *) self);            \
        return NULL;                                                    \
    }                                                                   \
    static PyObject *j_type##_is##type(PyObject *self)                  \
    {                                                                   \
        javaClass *obj;                                                 \
        if (!parseArg(self, "J", &javaClass::class$, &obj))             \
            Py_RETURN_TRUE;                                             \
        Py_RETURN_FALSE;                                                \
    }

#endif /* _macros_h */
