/** @file lsup_rdf_mod.c
 *
 * @brief LSUP_RDF package.
 *
 * This "package" module includes all RDF modules in a monolithic compiled
 * object, in order to resolve internal symbol dependencies. Individual modules
 * are extracted and exposed by the package __init__.py .
 */

#define PY_SSIZE_T_CLEAN

#include <Python.h>

#include "py_graph.h"
#include "py_namespace.h"


static PyObject *
env_init (PyObject *self)
{
    if (LSUP_init() != LSUP_OK) {
        PyErr_SetString (PyExc_SystemError, "Error initializing environment.");
        return NULL;
    }

    Py_RETURN_NONE;
}


static PyMethodDef lsup_rdf_methods[] = {
    {
        "env_init", (PyCFunction)env_init, METH_NOARGS,
        "Initialize the LSUP_RDF environment."
    },
    {NULL}
};

static PyModuleDef _lsup_rdf_pkg = {
    PyModuleDef_HEAD_INIT,
    .m_name = "_lsup_rdf",
    .m_doc = "Lakesuperior RDF package.",
    .m_size = -1,
    .m_methods = lsup_rdf_methods,
};

PyMODINIT_FUNC
PyInit__lsup_rdf (void)
{ return PyModule_Create (&_lsup_rdf_pkg); }


static PyModuleDef term_mod = {
    PyModuleDef_HEAD_INIT,
    .m_name = "term",
    .m_doc = "RDF term module.",
    .m_size = -1,
};


PyMODINIT_FUNC
PyInit_term()
{
    if (
        PyType_Ready (&TermType) < 0
        || PyType_Ready (&IRIRefType) < 0
        || PyType_Ready (&LiteralType) < 0
        || PyType_Ready (&BNodeType) < 0
    ) return NULL;

    PyObject *m = PyModule_Create(&term_mod);
    if (m == NULL) return NULL;

    if (
        PyModule_AddIntConstant (m, "TERM_UNDEFINED", LSUP_TERM_UNDEFINED) < 0
        || PyModule_AddIntConstant (m, "TERM_IRIREF", LSUP_TERM_IRIREF) < 0
        || PyModule_AddIntConstant (m, "TERM_BNODE", LSUP_TERM_BNODE) < 0
        || PyModule_AddIntConstant (m, "TERM_LITERAL", LSUP_TERM_LITERAL) < 0
        || PyModule_AddIntConstant (
                m, "TERM_LT_LITERAL", LSUP_TERM_LT_LITERAL) < 0
    ) return NULL;

    Py_INCREF(&TermType);
    if (PyModule_AddObject(m, "Term", (PyObject *) &TermType) < 0) {
        Py_DECREF(&TermType);
        Py_DECREF(m);
        return NULL;
    }

    Py_INCREF(&IRIRefType);
    if (PyModule_AddObject(m, "IRIRef", (PyObject *) &IRIRefType) < 0) {
        Py_DECREF(&IRIRefType);
        Py_DECREF(m);
        return NULL;
    }

    Py_INCREF(&LiteralType);
    if (PyModule_AddObject(m, "Literal", (PyObject *) &LiteralType) < 0) {
        Py_DECREF(&LiteralType);
        Py_DECREF(m);
        return NULL;
    }

    Py_INCREF(&BNodeType);
    if (PyModule_AddObject(m, "BNode", (PyObject *) &BNodeType) < 0) {
        Py_DECREF(&BNodeType);
        Py_DECREF(m);
        return NULL;
    }

    return m;
}


static PyModuleDef triple_mod = {
    PyModuleDef_HEAD_INIT,
    .m_name = "triple",
    .m_doc = "RDF triple module.",
    .m_size = -1,
};


PyMODINIT_FUNC
PyInit_triple()
{
    if (PyType_Ready (&TripleType) < 0) return NULL;

    PyObject *m = PyModule_Create(&triple_mod);
    if (m == NULL) return NULL;

    Py_INCREF(&TripleType);
    if (PyModule_AddObject(m, "Triple", (PyObject *) &TripleType) < 0) {
        Py_DECREF(&TripleType);
        Py_DECREF(m);
        return NULL;
    }

    return m;
}


static PyModuleDef namespace_mod = {
    PyModuleDef_HEAD_INIT,
    .m_name = "namespace",
    .m_doc = "RDF namespace module.",
    .m_size = -1,
};


PyMODINIT_FUNC
PyInit_namespace()
{
    if (PyType_Ready (&NSMapType) < 0) return NULL;

    PyObject *m = PyModule_Create(&namespace_mod);
    if (m == NULL) return NULL;

    Py_INCREF(&NSMapType);
    if (PyModule_AddObject(m, "NSMap", (PyObject *) &NSMapType) < 0) {
        Py_DECREF(&NSMapType);
        Py_DECREF(m);
        return NULL;
    }

    return m;
}


static PyModuleDef graph_mod = {
    PyModuleDef_HEAD_INIT,
    .m_name = "graph",
    .m_doc = "RDF graph module.",
    .m_size = -1,
};


PyMODINIT_FUNC
PyInit_graph()
{
    if (PyType_Ready (&GraphType) < 0) return NULL;

    PyObject *m = PyModule_Create(&graph_mod);
    if (!m) return NULL;

#define ENTRY(a, b) \
    if (PyModule_AddIntConstant (m, "STORE_" #a, b) < 0) return NULL;
    BACKEND_TBL
#undef ENTRY

    Py_INCREF(&GraphType);
    if (PyModule_AddObject(m, "Graph", (PyObject *) &GraphType) < 0) {
        Py_DECREF(&GraphType);
        Py_DECREF(m);
        return NULL;
    }

    return m;
}