/* ====================================================================
 * 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.
 * ====================================================================
 */

#include <gcj/cni.h>
#include <java/lang/System.h>
#include <java/util/Set.h>

#include <Python.h>
#include "structmember.h"

#include "functions.h"
#include "java.h"
#include "lucene.h"


/* const variable descriptor */

class t_descriptor {
public:
    PyObject_HEAD
    int flags;
    union {
        PyObject *value;
        PyObject *(*get)(PyObject *);
    } access;
};
    
#define DESCRIPTOR_STATIC 0x1

static void t_descriptor_dealloc(t_descriptor *self);
static PyObject *t_descriptor___get__(t_descriptor *self,
                                      PyObject *obj, PyObject *type);

static PyMemberDef t_descriptor_members[] = {
    { NULL, 0, 0, 0, NULL }
};

static PyMethodDef t_descriptor_methods[] = {
    { NULL, NULL, 0, NULL }
};


PyTypeObject ConstVariableDescriptorType = {
    PyObject_HEAD_INIT(NULL)
    0,                                   /* ob_size */
    "PyLucene.ConstVariableDescriptor",  /* tp_name */
    sizeof(t_descriptor),                /* tp_basicsize */
    0,                                   /* tp_itemsize */
    (destructor)t_descriptor_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 */
    0,                                   /* tp_getattro */
    0,                                   /* tp_setattro */
    0,                                   /* tp_as_buffer */
    Py_TPFLAGS_DEFAULT,                  /* tp_flags */
    "const variable descriptor",         /* tp_doc */
    0,                                   /* tp_traverse */
    0,                                   /* tp_clear */
    0,                                   /* tp_richcompare */
    0,                                   /* tp_weaklistoffset */
    0,                                   /* tp_iter */
    0,                                   /* tp_iternext */
    t_descriptor_methods,                /* tp_methods */
    t_descriptor_members,                /* tp_members */
    0,                                   /* tp_getset */
    0,                                   /* tp_base */
    0,                                   /* tp_dict */
    (descrgetfunc)t_descriptor___get__,  /* tp_descr_get */
    0,                                   /* tp_descr_set */
    0,                                   /* tp_dictoffset */
    0,                                   /* tp_init */
    0,                                   /* tp_alloc */
    0,                                   /* tp_new */
};

static void t_descriptor_dealloc(t_descriptor *self)
{
    if (self->flags & DESCRIPTOR_STATIC)
        Py_DECREF(self->access.value);
    self->ob_type->tp_free((PyObject *) self);
}

PyObject *make_descriptor(PyObject *value)
{
    t_descriptor *self = (t_descriptor *)
        ConstVariableDescriptorType.tp_alloc(&ConstVariableDescriptorType, 0);

    if (self)
    {
        self->access.value = value;
        self->flags = DESCRIPTOR_STATIC;
    }
    else
        Py_DECREF(value);

    return (PyObject *) self;
}

PyObject *make_descriptor(PyTypeObject *value)
{
    t_descriptor *self = (t_descriptor *)
        ConstVariableDescriptorType.tp_alloc(&ConstVariableDescriptorType, 0);

    if (self)
    {
        Py_INCREF(value);
        self->access.value = (PyObject *) value;
        self->flags = DESCRIPTOR_STATIC;
    }

    return (PyObject *) self;
}

PyObject *make_descriptor(PyObject *(*get)(PyObject *))
{
    t_descriptor *self = (t_descriptor *)
        ConstVariableDescriptorType.tp_alloc(&ConstVariableDescriptorType, 0);

    if (self)
    {
        self->access.get = get;
        self->flags = 0;
    }

    return (PyObject *) self;
}

static PyObject *t_descriptor___get__(t_descriptor *self,
                                      PyObject *obj, PyObject *type)
{
    if (self->flags & DESCRIPTOR_STATIC)
    {
        Py_INCREF(self->access.value);
        return self->access.value;
    }
    else if (obj == NULL || obj == Py_None)
    {
        Py_INCREF(self);
        return (PyObject *) self;
    }

    return self->access.get(obj);
}

static PyObject *dumpRefs(PyObject *args)
{
    jstring className = NULL;

    switch (PyTuple_GET_SIZE(args)) {
      case 0:
        break;
      case 1:
        if (!parseArgs(args, "s", &className))
            break;
      default:
        return PyErr_SetArgsError("dumpRefs", args);
    }

    java::util::Iterator *keys = pythonRefs->keySet()->iterator();

    while (keys->hasNext()) {
        jobject key = keys->next();
        jobject value = pythonRefs->get(key);
        jstring name = key->getClass()->getName();

        if (!className || className->equals(name))
        {
            java::lang::System::out->print(value);
            java::lang::System::out->print((jchar) ':');
            java::lang::System::out->print((jchar) ' ');
            java::lang::System::out->println(key->toString());
            java::lang::System::out->println(name);
            java::lang::System::out->println();
        }
    }

    return PyInt_FromLong(pythonRefs->size());
}

static PyMethodDef pylucene_funcs[] = {
    { "dumpRefs", (PyCFunction) dumpRefs, 0, "" },
    { NULL, NULL, 0, NULL }
};


extern "C" {

    void init_PyLucene(void)
    {
        PyObject *m = Py_InitModule3("_PyLucene", pylucene_funcs, "_PyLucene");
        PyObject *ver;

        PyType_Ready(&ConstVariableDescriptorType);
        Py_INCREF(&ConstVariableDescriptorType);

        ver = PyString_FromString(PYLUCENE_VER);
        PyObject_SetAttrString(m, "VERSION", ver); Py_DECREF(ver);

        ver = PyString_FromString(LUCENE_VER);
        PyObject_SetAttrString(m, "LUCENE_VERSION", ver); Py_DECREF(ver);

#ifdef _WITH_DB_DIRECTORY
        ver = PyString_FromString(DB_LIB_VER);
        PyObject_SetAttrString(m, "DB_VERSION", ver); Py_DECREF(ver);
#endif

        if (!initVM())
        {
            _init_java(m);
            _init_lucene(m);
        }
    }
}
