// Copyright David Abrahams 2002.
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#include <boost/python/object/life_support.hpp>
#include <boost/python/detail/none.hpp>
#include <boost/python/refcount.hpp>

namespace boost { namespace python { namespace objects { 

struct life_support
{
    PyObject_HEAD
    PyObject* patient;
};

extern "C"
{
    static void
    life_support_dealloc(PyObject* self)
    {
        Py_XDECREF(((life_support*)self)->patient);
        self->ob_type->tp_free(self);
    }

    static PyObject *
    life_support_call(PyObject *self, PyObject *arg, PyObject * /*kw*/)
    {
        // Let the patient die now
        Py_XDECREF(((life_support*)self)->patient);
        ((life_support*)self)->patient = 0;
        // Let the weak reference die. This probably kills us.
        Py_XDECREF(PyTuple_GET_ITEM(arg, 0));
        return ::boost::python::detail::none();
    }
}

PyTypeObject life_support_type = {
    PyVarObject_HEAD_INIT(NULL, 0)//(&PyType_Type)
    const_cast<char*>("Boost.Python.life_support"),
    sizeof(life_support),
    0,
    life_support_dealloc,               /* tp_dealloc */
    0,                                  /* tp_print */
    0,                                  /* tp_getattr */
    0,                                  /* tp_setattr */
    0,                                  /* tp_compare */
    0, //(reprfunc)func_repr,                   /* tp_repr */
    0,                                  /* tp_as_number */
    0,                                  /* tp_as_sequence */
    0,                                  /* tp_as_mapping */
    0,                                  /* tp_hash */
    life_support_call,                  /* tp_call */
    0,                                  /* tp_str */
    0, // PyObject_GenericGetAttr,            /* tp_getattro */
    0, // PyObject_GenericSetAttr,            /* tp_setattro */
    0,                                  /* tp_as_buffer */
    Py_TPFLAGS_DEFAULT /* | Py_TPFLAGS_HAVE_GC */,/* tp_flags */
    0,                                  /* tp_doc */
    0, // (traverseproc)func_traverse,          /* tp_traverse */
    0,                                  /* tp_clear */
    0,                                  /* tp_richcompare */
    0, //offsetof(PyLife_SupportObject, func_weakreflist), /* tp_weaklistoffset */
    0,                                  /* tp_iter */
    0,                                  /* tp_iternext */
    0,                                  /* tp_methods */
    0, // func_memberlist,                      /* tp_members */
    0, //func_getsetlist,                       /* tp_getset */
    0,                                  /* tp_base */
    0,                                  /* tp_dict */
    0,                                  /* tp_descr_get */
    0,                                  /* tp_descr_set */
    0, //offsetof(PyLife_SupportObject, func_dict),      /* tp_dictoffset */
    0,                                      /* tp_init */
    0,                                      /* tp_alloc */
    0,                                      /* tp_new */
    0,                                      /* tp_free */
    0,                                      /* tp_is_gc */
    0,                                      /* tp_bases */
    0,                                      /* tp_mro */
    0,                                      /* tp_cache */
    0,                                      /* tp_subclasses */
    0,                                      /* tp_weaklist */
#if PYTHON_API_VERSION >= 1012
    0                                       /* tp_del */
#endif
};

PyObject* make_nurse_and_patient(PyObject* nurse, PyObject* patient)
{
    if (nurse == Py_None || nurse == patient)
        return nurse;
    
    if (Py_TYPE(&life_support_type) == 0)
    {
        Py_SET_TYPE(&life_support_type, &PyType_Type);
        PyType_Ready(&life_support_type);
    }
    
    life_support* system = PyObject_New(life_support, &life_support_type);
    if (!system)
        return 0;

    system->patient = 0;
    
    // We're going to leak this reference, but don't worry; the
    // life_support system decrements it when the nurse dies.
    PyObject* weakref = PyWeakref_NewRef(nurse, (PyObject*)system);

    // weakref has either taken ownership, or we have to release it
    // anyway
    Py_DECREF(system);
    if (!weakref)
        return 0;
    
    system->patient = patient;
    Py_XINCREF(patient); // hang on to the patient until death
    return weakref;
}

}}} // namespace boost::python::objects
