#ifndef _PY_NAMESPACE_OBJ_H
#define _PY_NAMESPACE_OBJ_H

#define PY_SSIZE_T_CLEAN

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

#include "namespace.h"


typedef struct {
    PyObject_HEAD
    LSUP_NSMap      *ob_struct;
} NSMapObject;


static int
NSMap_init (NSMapObject *self, PyObject *args)
{
    self->ob_struct = LSUP_nsmap_new();
    if (!self->ob_struct) {
        PyErr_SetString (PyExc_SystemError, "Could not create namespace.");
        return -1;
    }

    return 0;
}


static void
NSMap_dealloc (NSMapObject *self)
{
    LSUP_nsmap_free (self->ob_struct);
    Py_TYPE (self)->tp_free ((PyObject *) self);
}


static PyObject *
NSMap_add (PyObject *self, PyObject *args)
{
    const char *pfx, *ns;
    if (! PyArg_ParseTuple (args, "ss", &pfx, &ns)) return NULL;

    if (LSUP_nsmap_add (((NSMapObject *)self)->ob_struct, pfx, ns) < 0) {
        PyErr_SetString (PyExc_ValueError, "Error adding namespace.");
        return NULL;
    }

    Py_RETURN_NONE;
}


static PyObject *
NSMap_remove (PyObject *self, PyObject *pfx_obj)
{
    if (PyUnicode_READY (pfx_obj) < 0) return NULL;
    const char *pfx = PyUnicode_AsUTF8 (pfx_obj);

    if (LSUP_nsmap_remove (((NSMapObject *)self)->ob_struct, pfx) < 0) {
        PyErr_SetString (PyExc_ValueError, "Error removing namespace.");
        return NULL;
    }

    Py_RETURN_NONE;
}


static PyObject *
NSMap_get (PyObject *self, PyObject *pfx_obj)
{
    if (PyUnicode_READY (pfx_obj) < 0) return NULL;
    const char *pfx = PyUnicode_AsUTF8 (pfx_obj);

    const char *ns = LSUP_nsmap_get_ns (((NSMapObject *)self)->ob_struct, pfx);
    if (!ns) Py_RETURN_NONE;

    PyObject *ns_obj = PyUnicode_FromString (ns);

    Py_INCREF (ns_obj);
    return (ns_obj);
}


static PyObject *
NSMap_denormalize_uri (PyObject *self, PyObject *fq_uri_obj)
{
    if (PyUnicode_READY (fq_uri_obj) < 0) return NULL;
    const char *fq_uri = PyUnicode_AsUTF8 (fq_uri_obj);

    char *pfx_uri;
    LSUP_rc rc = LSUP_nsmap_denormalize_uri (
            ((NSMapObject *)self)->ob_struct, fq_uri, &pfx_uri);
    if (rc < 0)  {
        PyErr_SetString (PyExc_ValueError, "Error normalizing URI.");
        return NULL;
    } else if (rc == LSUP_NORESULT) Py_RETURN_NONE;

    PyObject *uri_obj = PyUnicode_FromString (pfx_uri);

    Py_INCREF (uri_obj);
    return (uri_obj);
}


static PyObject *
NSMap_normalize_uri (PyObject *self, PyObject *pfx_uri_obj)
{
    if (PyUnicode_READY (pfx_uri_obj) < 0) return NULL;
    const char *pfx_uri = PyUnicode_AsUTF8 (pfx_uri_obj);

    char *fq_uri;
    LSUP_rc rc = LSUP_nsmap_normalize_uri (
            ((NSMapObject *)self)->ob_struct, pfx_uri, &fq_uri);
    if (rc < 0)  {
        PyErr_SetString (PyExc_ValueError, "Error denormalizing URI.");
        return NULL;
    }

    PyObject *uri_obj = PyUnicode_FromString (fq_uri);

    Py_INCREF (uri_obj);
    return (uri_obj);
}


static PyObject *
NSMap_as_dict (PyObject *self)
{
    const char ***data = LSUP_nsmap_dump (((NSMapObject *)self)->ob_struct);

    PyObject *data_obj = PyDict_New();
    // TODO PyDictProxy_New(PyObject *mapping)

    for (size_t i = 0; data[i] != NULL; i++) {
        PyObject *val_obj = PyUnicode_FromString(data[i][1]);
        if (PyDict_SetItemString (data_obj, data[i][0], val_obj) < 0)
            return NULL;
    }

    Py_INCREF (data_obj);
    return data_obj;
}


static PyMethodDef NSMap_methods[] = {
    {
        "add", (PyCFunction) NSMap_add, METH_VARARGS,
        "Add a namespace prefix."
    },
    {
        "remove", (PyCFunction) NSMap_remove, METH_O,
        "Remove a namespace prefix."
    },
    {
        "get", (PyCFunction) NSMap_get, METH_O,
        "Get the fully qualified namespace for a prefix."
    },
    {
        "normalize_uri", (PyCFunction) NSMap_normalize_uri, METH_O,
        "Normalize a URI (i.e. convert from prefixed to fully qualified)."
    },
    {
        "denormalize_uri", (PyCFunction) NSMap_denormalize_uri, METH_O,
        "Denormalize a URI (i.e. convert from fully qualified to prefixed)."
    },
    {
        "as_dict", (PyCFunction) NSMap_as_dict, METH_NOARGS,
        "Namespace map as a dictionary of prefixes and namespaces.",
    },
    {NULL},
};


PyTypeObject NSMapType = {
    PyVarObject_HEAD_INIT(NULL, 0)
    .tp_name            = "namespace.NSMap",
    .tp_doc             = "Namespace prefix map.",
    .tp_basicsize       = sizeof(NSMapObject),
    .tp_itemsize        = 0,
    .tp_flags           = Py_TPFLAGS_DEFAULT,
    .tp_new             = PyType_GenericNew,
    .tp_init            = (initproc) NSMap_init,
    .tp_dealloc         = (destructor) NSMap_dealloc,
    .tp_methods         = NSMap_methods,
};


#endif  /* _PY_NAMESPACE_OBJ */