#include "pylcm_subscription.h"

#ifndef Py_RETURN_NONE
#define Py_RETURN_NONE      \
    do {                    \
        Py_INCREF(Py_None); \
        return Py_None;     \
    } while (0)
#endif

// to support python 2.5 and earlier
#ifndef Py_TYPE
#define Py_TYPE(ob) (((PyObject *) (ob))->ob_type)
#endif

// to support python 3 where all ints are long
#if PY_MAJOR_VERSION >= 3
#define PyInt_AsLong PyLong_AsLong
#endif

PyDoc_STRVAR(_class_doc,
             "\
The LCMSubscription class represents a single subscription of a message\n\
handler to an LCM channel.\n\
\n\
This class should never be instantiated by the programmer.\n\
\n\
@undocumented: __new__, __getattribute__, __init__\n\
");

// =============== LCMSubscription class methods ==============

static PyObject *_set_queue_capacity(PyLCMSubscriptionObject *sobj, PyObject *arg)
{
    int num_messages = PyInt_AsLong(arg);
    if (num_messages == -1 && PyErr_Occurred())
        return NULL;

    int status;
    Py_BEGIN_ALLOW_THREADS;
    status = lcm_subscription_set_queue_capacity(sobj->subscription, num_messages);
    Py_END_ALLOW_THREADS;

    if (0 != status) {
        PyErr_SetFromErrno(PyExc_IOError);
        return NULL;
    }

    Py_RETURN_NONE;
}
PyDoc_STRVAR(pylcm_set_queue_capacity_doc,
             "\
set_queue_capacity(num_messages) -> None\n\
Sets the maximum number of received but unhandled messages to queue for this\n\
subscription.  If messages start arriving faster than they are handled, then\n\
they will be discarded after more than this number start piling up.\n\
\n\
@param num_messages: Maximum number of messages to queue for this subscription.\n\
A number less than or equal to zero indicates no limit (very dangerous!).\n\
");

static PyMethodDef _methods[] = {
    {"set_queue_capacity", (PyCFunction) _set_queue_capacity, METH_O, pylcm_set_queue_capacity_doc},
    {NULL, NULL}, /* sentinel */
};

// ==================== class administrative methods ====================

static void _dealloc(PyLCMSubscriptionObject *s)
{
    if (s->handler) {
        Py_DECREF(s->handler);
        s->handler = NULL;
    }
    // ignore s->subscription and s->lcm_obj
    Py_TYPE(s)->tp_free((PyObject *) s);
}

static PyObject *_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
    PyObject *obj = type->tp_alloc(type, 0);
    return obj;
}

static int _init(PyObject *self, PyObject *args, PyObject *kwargs)
{
    PyLCMSubscriptionObject *s = (PyLCMSubscriptionObject *) self;
    s->subscription = NULL;
    s->handler = NULL;
    s->lcm_obj = NULL;
    return 0;
}

/* Type object for socket objects. */
PyTypeObject pylcm_subscription_type = {
#if PY_MAJOR_VERSION >= 3
    PyVarObject_HEAD_INIT(0, 0) /* size is now part of macro */
#else
    PyObject_HEAD_INIT(0) /* Must fill in type value later */
    0,                    /* ob_size */
#endif
    "LCMSubscription",                        /* tp_name */
    sizeof(PyLCMSubscriptionObject),          /* tp_basicsize */
    0,                                        /* tp_itemsize */
    (destructor) _dealloc,                    /* 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 */
    PyObject_GenericGetAttr,                  /* tp_getattro */
    0,                                        /* tp_setattro */
    0,                                        /* tp_as_buffer */
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
    _class_doc,                               /* tp_doc */
    0,                                        /* tp_traverse */
    0,                                        /* tp_clear */
    0,                                        /* tp_richcompare */
    0,                                        /* tp_weaklistoffset */
    0,                                        /* tp_iter */
    0,                                        /* tp_iternext */
    _methods,                                 /* tp_methods */
    0,                                        /* tp_members */
    0,                                        /* tp_getset */
    0,                                        /* tp_base */
    0,                                        /* tp_dict */
    0,                                        /* tp_descr_get */
    0,                                        /* tp_descr_set */
    0,                                        /* tp_dictoffset */
    _init,                                    /* tp_init */
    PyType_GenericAlloc,                      /* tp_alloc */
    _new,                                     /* tp_new */
    PyObject_Del,                             /* tp_free */
};
