#include "domlette.h"
#include "nss.h"
#include "xmlstring.h"


/** Private Routines **************************************************/


#define Element_VerifyState(ob)                                     \
  if (!PyElement_Check(ob) ||                                       \
      ((PyElementObject *)(ob))->namespaceURI == NULL ||            \
      ((PyElementObject *)(ob))->prefix == NULL ||                  \
      ((PyElementObject *)(ob))->localName == NULL ||               \
      ((PyElementObject *)(ob))->nodeName == NULL ||                \
      ((PyElementObject *)(ob))->attributes == NULL) {              \
     DOMException_InvalidStateErr("Element in inconsistent state"); \
     return NULL;                                                   \
  }

static PyObject *buildAttrKey(PyAttrObject *attr)
{
  PyObject *key;
  PyObject *local;

  switch (PyObject_RichCompareBool(attr->namespaceURI, g_xmlnsNamespace, Py_EQ)) {
  case 0:
    /* normal attribute */
    local = attr->localName;
    break;
  case 1:
    /* namespace attribute */
    if (attr->prefix != Py_None) {
      /* xmlns:prefix = 'namespaceURI' */
      local = attr->localName;
    }
    else {
      /* xmlns = 'namespaceURI' */
      local = Py_None;
    }
    break;
  default:
    /* error */
    return NULL;
  }

  key = PyTuple_New(2);

  Py_INCREF(attr->namespaceURI);
  PyTuple_SET_ITEM(key, 0, attr->namespaceURI);

  Py_INCREF(local);
  PyTuple_SET_ITEM(key, 1, local);

  return key;
}


/** Public C API ******************************************************/


/* returns a new reference */
PyAttrObject *Element_SetAttributeNS(PyElementObject *self,
				     PyObject *namespaceURI,
                                     PyObject *qualifiedName,
				     PyObject *prefix,
				     PyObject *localName,
                                     PyObject *value)
{
  PyObject *key;
  register PyAttrObject *attr;

  Element_VerifyState(self);

  DOMString_NullCheck(prefix);
  DOMString_NullCheck(namespaceURI);

  if (Node_HasFlag(self, Node_FLAGS_SHARED_ATTRIBUTES)) {
    PyObject *attrs = PyDict_New();
    if (attrs == NULL) return NULL;
    Py_DECREF(self->attributes);
    self->attributes = attrs;
    Node_ClearFlag(self, Node_FLAGS_SHARED_ATTRIBUTES);
  }

  /* new reference */
  attr = Document_CreateAttributeNS((PyDocumentObject*)self->ownerDocument,
                                    namespaceURI, qualifiedName, prefix,
                                    localName, value);
  if (attr == NULL) return NULL;
  Node_SET_PARENT(attr, (PyNodeObject *) self);

  key = buildAttrKey(attr);
  if (key) {
    PyDict_SetItem(self->attributes, key, (PyObject *)attr);
    Py_DECREF(key);
  } else {
    Py_DECREF(attr);
    attr = NULL;
  }

  return attr;
}


PyObject *Element_GetAttributeNodeNS(PyElementObject *self,
                                     PyObject *namespaceURI,
                                     PyObject *localName)
{
  /* Returns a borrowed ref */
  PyObject *key;
  PyObject *attr;

  Element_VerifyState(self);

  Py_INCREF(namespaceURI);
  Py_INCREF(localName);
  /* steals reference */
  key = PyTuple_New(2);
  PyTuple_SetItem(key, 0, namespaceURI);
  PyTuple_SetItem(key, 1, localName);

  attr = PyDict_GetItem(self->attributes, key);
  Py_DECREF(key);

  return attr ? attr : Py_None;
}

PyElementObject *Element_CloneNode(PyObject *node, int deep,
				   PyNodeObject *newOwnerDocument)
{
  PyElementObject *element;
  PyObject *namespaceURI, *prefix, *localName, *qualifiedName;
  PyObject *attributes;
  int i, count;

  if (!PyDocument_Check(newOwnerDocument)) {
    PyErr_SetString(PyExc_TypeError, "newOwnerDocument must be a cDocument");
    return NULL;
  }

  namespaceURI = PyObject_GetAttrString(node, "namespaceURI");
  namespaceURI = DOMString_FromObjectInplace(namespaceURI);
  qualifiedName = PyObject_GetAttrString(node, "nodeName");
  qualifiedName = DOMString_FromObjectInplace(qualifiedName);
  prefix = PyObject_GetAttrString(node, "prefix");
  prefix = DOMString_FromObjectInplace(prefix);
  localName = PyObject_GetAttrString(node, "localName");
  localName = DOMString_FromObjectInplace(localName);

  /* attributes are cloned regardless of the deep argument */
  attributes = PyObject_GetAttrString(node, "attributes");
  if (attributes) {
    /* get the actual attribute nodes from the attributes mapping */
    PyObject *values = PyMapping_Values(attributes);
    Py_DECREF(attributes);
    attributes = values;
  }
  if (namespaceURI == NULL || qualifiedName == NULL || prefix == NULL ||
      localName == NULL || attributes == NULL) {
    Py_XDECREF(attributes);
    Py_XDECREF(localName);
    Py_XDECREF(prefix);
    Py_XDECREF(qualifiedName);
    Py_XDECREF(namespaceURI);
    return NULL;
  }

  /* We now have everything we need to create a shallow copy, do it */
  element = Document_CreateElementNS((PyDocumentObject *)newOwnerDocument,
                                     namespaceURI, qualifiedName, prefix,
                                     localName);

  /* Done with these */
  Py_DECREF(namespaceURI);
  Py_DECREF(qualifiedName);
  Py_DECREF(prefix);
  Py_DECREF(localName);

  if (element == NULL) {
    Py_DECREF(attributes);
    return NULL;
  }

  /* copy the attributes */
  count = PySequence_Length(attributes);
  for (i = 0; i < count; i++) {
    PyAttrObject *attr;
    PyObject *value;
    PyObject *old_attr;

    old_attr = PySequence_GetItem(attributes, i);
    if (old_attr == NULL) {
      Py_DECREF(element);
      Py_DECREF(attributes);
      return NULL;
    }

    namespaceURI = PyObject_GetAttrString(old_attr, "namespaceURI");
    namespaceURI = DOMString_FromObjectInplace(namespaceURI);
    qualifiedName = PyObject_GetAttrString(old_attr, "nodeName");
    qualifiedName = DOMString_FromObjectInplace(qualifiedName);
    prefix = PyObject_GetAttrString(old_attr, "prefix");
    prefix = DOMString_FromObjectInplace(prefix);
    localName = PyObject_GetAttrString(old_attr, "localName");
    localName = DOMString_FromObjectInplace(localName);
    value = PyObject_GetAttrString(old_attr, "value");
    value = DOMString_FromObjectInplace(value);
    Py_DECREF(old_attr);
    if (namespaceURI == NULL || prefix == NULL || localName == NULL ||
        qualifiedName == NULL ||value == NULL) {
      Py_XDECREF(value);
      Py_XDECREF(qualifiedName);
      Py_XDECREF(localName);
      Py_XDECREF(prefix);
      Py_XDECREF(namespaceURI);
      Py_DECREF(element);
      Py_DECREF(attributes);
      return NULL;
    }

    attr = Element_SetAttributeNS(element, namespaceURI, qualifiedName, prefix,
                                  localName, value);
    Py_DECREF(value);
    Py_DECREF(localName);
    Py_DECREF(prefix);
    Py_DECREF(qualifiedName);
    Py_DECREF(namespaceURI);
    if (attr == NULL) {
      Py_DECREF(element);
      Py_DECREF(attributes);
      return NULL;
    }
    Py_DECREF(attr);
  }
  Py_DECREF(attributes);

  if (deep) {
    PyObject *childNodes;

    childNodes = PyObject_GetAttrString(node, "childNodes");
    if (childNodes == NULL) {
      Py_DECREF(element);
      return NULL;
    }
    count = PySequence_Length(childNodes);
    for (i = 0; i < count; i++) {
      PyObject *child;
      PyNodeObject *cloned_child;

      child = PySequence_GetItem(childNodes, i);
      if (child == NULL) {
		Py_DECREF(childNodes);
		Py_DECREF(element);
		return NULL;
      }

      cloned_child = Node_CloneNode(child, deep, newOwnerDocument);
      Py_DECREF(child);
      if (cloned_child == NULL) {
		Py_DECREF(childNodes);
		Py_DECREF(element);
		return NULL;
      }

      Node_AppendChild((PyNodeObject *)element, cloned_child);
      Py_DECREF(cloned_child);
    }
    Py_DECREF(childNodes);
  }

  return element;
}


/** Python Methods ****************************************************/


static char element_getAttributeNodeNS_doc[] =
"Retrieves an Attr node by local name and namespace URI.";

static PyObject *element_getAttributeNodeNS(PyObject *self, PyObject *args)
{
  PyObject *namespaceURI, *localName;
  PyObject *attr;

  Element_VerifyState(self);

  if (!PyArg_ParseTuple(args, "OO:getAttributeNodeNS",
                        &namespaceURI, &localName))
    return NULL;

  namespaceURI = DOMString_ConvertArgument(namespaceURI, "namespaceURI", 1);
  if (namespaceURI == NULL) {
    return NULL;
  }

  localName = DOMString_ConvertArgument(localName, "localName", 0);
  if (localName == NULL) {
    Py_DECREF(namespaceURI);
    return NULL;
  }

  attr = Element_GetAttributeNodeNS((PyElementObject *)self, namespaceURI, localName);

  Py_DECREF(namespaceURI);
  Py_DECREF(localName);

  Py_INCREF(attr);
  return attr;
}


static char element_getAttributeNS_doc[] =
"Retrieves an attribute value by local name and namespace URI.";

static PyObject *element_getAttributeNS(PyObject *self, PyObject *args)
{
  PyObject *namespaceURI, *localName;
  PyObject *attr;

  Element_VerifyState(self);

  if (!PyArg_ParseTuple(args, "OO:getAttributeNS", &namespaceURI, &localName))
    return NULL;

  namespaceURI = DOMString_ConvertArgument(namespaceURI, "namespaceURI", 1);
  if (namespaceURI == NULL) {
    return NULL;
  }

  localName = DOMString_ConvertArgument(localName, "localName", 0);
  if (localName == NULL) {
    Py_DECREF(namespaceURI);
    return NULL;
  }

  attr = Element_GetAttributeNodeNS((PyElementObject *)self, namespaceURI, localName);

  Py_DECREF(namespaceURI);
  Py_DECREF(localName);

  if (attr == Py_None) {
    /* empty unicode string */
    return PyUnicode_FromUnicode(NULL, 0);
  } else {
    Py_INCREF(PyAttr_NODE_VALUE(attr));
    return PyAttr_NODE_VALUE(attr);
  }
}


static char element_hasAttributeNS_doc[] =
"Returns True when an attribute with a given local name and namespace URI\n\
is specified on this element or has a default value, False otherwise.";

static PyObject *element_hasAttributeNS(PyObject *self, PyObject *args)
{
  PyObject *namespaceURI, *localName;
  PyObject *attr;

  Element_VerifyState(self);

  if (!PyArg_ParseTuple(args, "OO:hasAttributeNS", &namespaceURI, &localName))
    return NULL;

  namespaceURI = DOMString_ConvertArgument(namespaceURI, "namespaceURI", 1);
  if (namespaceURI == NULL) {
    return NULL;
  }

  localName = DOMString_ConvertArgument(localName, "localName", 0);
  if (localName == NULL) {
    Py_DECREF(namespaceURI);
    return NULL;
  }

  attr = Element_GetAttributeNodeNS((PyElementObject *)self, namespaceURI, localName);

  Py_DECREF(namespaceURI);
  Py_DECREF(localName);

  if (attr == Py_None) {
    Py_INCREF(Py_False);
    return Py_False;
  }

  Py_INCREF(Py_True);
  return Py_True;
}


static char element_setAttributeNS_doc[] =
"Adds a new attribute.  If an attribute with the same local name and\n\
namespace URI is already present on the element, its prefix is changed\n\
to be the prefix part of the qualifiedName, and its value is changed to\n\
be the value parameter.";

static PyObject *element_setAttributeNS(PyObject *self, PyObject *args)
{
  PyObject *namespaceURI, *qualifiedName, *value, *prefix, *localName;
  PyAttrObject *attr;

  Element_VerifyState(self);

  if (!PyArg_ParseTuple(args, "OOO:setAttributeNS",
                        &namespaceURI, &qualifiedName, &value))
    return NULL;

  namespaceURI = DOMString_ConvertArgument(namespaceURI, "namespaceURI", 1);
  if (namespaceURI == NULL) {
    return NULL;
  }

  qualifiedName = DOMString_ConvertArgument(qualifiedName, "qualifiedName", 0);
  if (qualifiedName == NULL) {
    Py_DECREF(namespaceURI);
    return NULL;
  }

  value = DOMString_ConvertArgument(value, "value", 0);
  if (value == NULL) {
    Py_DECREF(namespaceURI);
    Py_DECREF(qualifiedName);
    return NULL;
  }

  if (!XmlString_SplitQName(qualifiedName, &prefix, &localName)) {
    Py_DECREF(namespaceURI);
    Py_DECREF(qualifiedName);
    return NULL;
  }

  /* returns new reference */
  attr = Element_SetAttributeNS((PyElementObject *)self, namespaceURI,
                                qualifiedName, prefix, localName, value);
  Py_DECREF(namespaceURI);
  Py_DECREF(qualifiedName);
  Py_DECREF(prefix);
  Py_DECREF(localName);
  Py_DECREF(value);

  return (PyObject *) attr;
}


static char element_setAttributeNodeNS_doc[] =
"Adds a new attribute. If an attribute with that local name and that\n\
namespace URI is already present in the element, it is replaced by the\n\
new one. Replacing an attribute node by itself has no effect.";

static PyObject *element_setAttributeNodeNS(PyElementObject *self,
                                            PyObject *args)
{
  PyAttrObject *attr;
  PyObject *oldAttr, *key;

  Element_VerifyState(self);

  if (!PyArg_ParseTuple(args, "O!:setAttributeNodeNS", &DomletteAttr_Type,
                        &attr))
    return NULL;

  key = buildAttrKey(attr);

  /* Set the new attribute */
  if (Node_HasFlag(self, Node_FLAGS_SHARED_ATTRIBUTES)) {
    PyObject *attrs = PyDict_New();
    if (attrs == NULL) return NULL;
    Py_DECREF(self->attributes);
    self->attributes = attrs;
    Node_ClearFlag(self, Node_FLAGS_SHARED_ATTRIBUTES);
  }

  /* Get the return value */
  oldAttr = PyDict_GetItem(PyElement_ATTRIBUTES(self), key);

  PyDict_SetItem(PyElement_ATTRIBUTES(self), key, (PyObject *)attr);
  Py_DECREF(key);

  /* Set the new attributes owner */
  Node_SET_PARENT(attr, (PyNodeObject *) self);

  if (oldAttr == NULL) {
    /* new attribute */
    oldAttr = Py_None;
  }
  else {
    /* Reset the removed attributes owner */
    Node_SET_PARENT(oldAttr, (PyNodeObject *) Py_None);
  }

  Py_INCREF(oldAttr);
  return oldAttr;
}


static char element_removeAttributeNode_doc[] =
"Removes the specified attribute node.";

static PyObject *element_removeAttributeNode(PyObject *self, PyObject *args)
{
  PyAttrObject *attr;
  PyObject *key;

  Element_VerifyState(self);

  if (!PyArg_ParseTuple(args, "O!:removeAttributeNode", &DomletteAttr_Type,
                        &attr))
    return NULL;

  key = buildAttrKey(attr);
  if (PyDict_DelItem(PyElement_ATTRIBUTES(self), key) == -1) {
    if (PyErr_ExceptionMatches(PyExc_KeyError)) {
      DOMException_NotFoundErr("attribute not found");
    }
    Py_DECREF(key);
    return NULL;
  }
  Py_DECREF(key);

  /* Reset the removed attributes owner */
  Node_SET_PARENT(attr, (PyNodeObject *) Py_None);

  Py_INCREF(Py_None);
  return Py_None;
}


static char element_removeAttributeNS_doc[] =
"Removes an attribute by local name and namespace URI.";

static PyObject *element_removeAttributeNS(PyObject *self, PyObject *args)
{
  PyObject *namespaceURI, *qualifiedName, *prefix, *localName, *key, *attr;

  Element_VerifyState(self);

  if (!PyArg_ParseTuple(args, "OO:removeAttributeNS",
                        &namespaceURI, &qualifiedName))
    return NULL;

  namespaceURI = DOMString_ConvertArgument(namespaceURI, "namespaceURI", 1);
  if (namespaceURI == NULL) {
    return NULL;
  }

  qualifiedName = DOMString_ConvertArgument(qualifiedName, "qualifiedName", 0);
  if (qualifiedName == NULL) {
    Py_DECREF(namespaceURI);
    return NULL;
  }

  if (!XmlString_SplitQName(qualifiedName, &prefix, &localName)) {
    Py_DECREF(namespaceURI);
    Py_DECREF(qualifiedName);
    return NULL;
  }

  /* Done with these */
  Py_DECREF(qualifiedName);
  Py_DECREF(prefix);

  key = PyTuple_New(2);
  /* Let the tuple own these */
  PyTuple_SetItem(key, 0, namespaceURI);
  PyTuple_SetItem(key, 1, localName);

  attr = PyDict_GetItem(PyElement_ATTRIBUTES(self), key);
  if (attr) {
    /* Ensure that this doesn't go away while we're changing it */
    Py_INCREF(attr);

    /* Remove the entry from the attributes mapping */
    if (PyDict_DelItem(PyElement_ATTRIBUTES(self), key) == -1) {
      Py_DECREF(attr);
      Py_DECREF(key);
      return NULL;
    }

    /* Unset the parent node relationship */
    Node_SET_PARENT(attr, (PyNodeObject *) Py_None);

    /* Done with our changes */
    Py_DECREF(attr);
  }

  /* Destroy the key */
  Py_DECREF(key);

  Py_INCREF(Py_None);
  return Py_None;
}

#define element_method(NAME) \
  { #NAME, (PyCFunction) element_##NAME, METH_VARARGS, element_##NAME##_doc }

static struct PyMethodDef element_methods[] = {
  element_method(getAttributeNS),
  element_method(getAttributeNodeNS),
  element_method(setAttributeNS),
  element_method(setAttributeNodeNS),
  element_method(removeAttributeNS),
  element_method(removeAttributeNode),
  element_method(hasAttributeNS),
  { NULL }      /* sentinel */
};

#undef element_method


/** Python Members ****************************************************/


#define Element_MEMBER(name, member) \
  { name, T_OBJECT, offsetof(PyElementObject, member), RO }

static struct PyMemberDef element_members[] = {
  Element_MEMBER("tagName", nodeName),
  Element_MEMBER("nodeName", nodeName),
  Element_MEMBER("localName", localName),
  Element_MEMBER("namespaceURI", namespaceURI),
  Element_MEMBER("prefix", prefix),
  Element_MEMBER("attributes", attributes),
  Element_MEMBER("baseURI", baseURI),
  /* deprecated */
  Element_MEMBER("xmlBase", baseURI),
  { NULL }
};


/** Python Computed Members *******************************************/


static PyObject *get_xpath_attributes(PyElementObject *self, void *arg)
{
  PyObject *attributes = PyList_New(0);
  if (attributes != NULL) {
    PyObject *key, *attr;
    int pos = 0;
    while (PyDict_Next(self->attributes, &pos, &key, &attr)) {
      switch (PyObject_RichCompareBool(PyAttr_NAMESPACE_URI(attr),
                                       g_xmlnsNamespace, Py_NE)) {
      case 0: /* namespace attribute */
        break;
      case 1: /* normal attribute */
        if (PyList_Append(attributes, attr) == -1) {
          Py_DECREF(attributes);
          return NULL;
        }
        break;
      default: /* error */
        Py_DECREF(attributes);
        return NULL;
      }
    }
  }
  return attributes;
}


static PyObject *get_xpath_namespaces(PyElementObject *self, void *arg)
{
  PyObject *namespaces;
  PyObject *nss = Domlette_GetNamespaces((PyNodeObject *) self);
  if (nss == NULL) return NULL;

  namespaces = PyList_New(0);
  if (namespaces != NULL) {
    PyObject *xns, *prefix, *uri;
    int pos = 0;
    while (PyDict_Next(nss, &pos, &prefix, &uri)) {
      xns = (PyObject *) XPathNamespace_New(self, prefix, uri);
      if (xns == NULL) {
        Py_DECREF(nss);
        Py_DECREF(namespaces);
        return NULL;
      }
      if (PyList_Append(namespaces, xns) == -1) {
        Py_DECREF(nss);
        Py_DECREF(namespaces);
        Py_DECREF(xns);
        return NULL;
      }
      Py_DECREF(xns);
    }
  }
  Py_DECREF(nss);
  return namespaces;
}


static struct PyGetSetDef element_getset[] = {
  /* XPath-specific accessors */
  { "xpathAttributes", (getter) get_xpath_attributes },
  { "xpathNamespaces", (getter) get_xpath_namespaces },
  { NULL }
};


/** Type Object ********************************************************/


static void element_dealloc(PyElementObject *self)
{
  PyObject_GC_UnTrack((PyObject *) self);

  Py_XDECREF(self->namespaceURI);
  self->namespaceURI = NULL;

  Py_XDECREF(self->prefix);
  self->prefix = NULL;

  Py_XDECREF(self->localName);
  self->localName = NULL;

  Py_XDECREF(self->nodeName);
  self->nodeName = NULL;

  if (self->attributes) {
    PyDict_Clear(self->attributes);
    Py_DECREF(self->attributes);
    self->attributes = NULL;
  }

  Node_Del(self);
}


static PyObject *element_repr(PyElementObject *element)
{
  PyObject *repr;
  PyObject *name = PyObject_Repr(element->nodeName);
  if (name == NULL) return NULL;

  repr = PyString_FromFormat("<Element at %p: name %s, %d attributes, %d children>",
                             element,
                             PyString_AS_STRING(name),
                             PyDict_Size(element->attributes),
                             ContainerNode_GET_COUNT(element));
  Py_DECREF(name);
  return repr;
}


static int element_traverse(PyElementObject *self, visitproc visit, void *arg)
{
  PyTypeObject *base = self->ob_type->tp_base;
  int rt;

  if (!Node_HasFlag(self, Node_FLAGS_SHARED_ATTRIBUTES) &&
      self->attributes != NULL) {
    rt = visit(self->attributes, arg);
    if (rt) return rt;
  }

  if (base && PyType_IS_GC(base))
    if (base->tp_traverse)
      return base->tp_traverse((PyObject *)self, visit, arg);

  return 0;
}


static int element_clear(PyElementObject *self)
{
  PyTypeObject *base = self->ob_type->tp_base;
  PyObject *tmp;

  if (self->attributes != NULL) {
    tmp = self->attributes;
    self->attributes = NULL;
    Py_DECREF(tmp);
  }

  if (base && PyType_IS_GC(base))
    if (base->tp_clear)
      return base->tp_clear((PyObject *)self);

  return 0;
}


static char element_doc[] = "\
The Element interface represents an element in an XML document.";

PyTypeObject DomletteElement_Type = {
  /* PyObject_HEAD     */ PyObject_HEAD_INIT(NULL)
  /* ob_size           */ 0,
  /* tp_name           */ "Ft.Xml.cDomlette.Element",
  /* tp_basicsize      */ sizeof(PyElementObject),
  /* tp_itemsize       */ 0,
  /* tp_dealloc        */ (destructor) element_dealloc,
  /* tp_print          */ (printfunc) 0,
  /* tp_getattr        */ (getattrfunc) 0,
  /* tp_setattr        */ (setattrfunc) 0,
  /* tp_compare        */ (cmpfunc) 0,
  /* tp_repr           */ (reprfunc) element_repr,
  /* tp_as_number      */ (PyNumberMethods *) 0,
  /* tp_as_sequence    */ (PySequenceMethods *) 0,
  /* tp_as_mapping     */ (PyMappingMethods *) 0,
  /* tp_hash           */ (hashfunc) 0,
  /* tp_call           */ (ternaryfunc) 0,
  /* tp_str            */ (reprfunc) 0,
  /* tp_getattro       */ (getattrofunc) 0,
  /* tp_setattro       */ (setattrofunc) 0,
  /* tp_as_buffer      */ (PyBufferProcs *) 0,
  /* tp_flags          */ (Py_TPFLAGS_DEFAULT |
                           Py_TPFLAGS_HAVE_GC),
  /* tp_doc            */ (char *) element_doc,
  /* tp_traverse       */ (traverseproc) element_traverse,
  /* tp_clear          */ (inquiry) element_clear,
  /* tp_richcompare    */ (richcmpfunc) 0,
  /* tp_weaklistoffset */ 0,
  /* tp_iter           */ (getiterfunc) 0,
  /* tp_iternext       */ (iternextfunc) 0,
  /* tp_methods        */ (PyMethodDef *) element_methods,
  /* tp_members        */ (PyMemberDef *) element_members,
  /* tp_getset         */ (PyGetSetDef *) element_getset,
  /* tp_base           */ (PyTypeObject *) 0,
  /* tp_dict           */ (PyObject *) 0,
  /* tp_descr_get      */ (descrgetfunc) 0,
  /* tp_descr_set      */ (descrsetfunc) 0,
  /* tp_dictoffset     */ 0,
  /* tp_init           */ (initproc) 0,
  /* tp_alloc          */ (allocfunc) 0,
  /* tp_new            */ (newfunc) 0,
  /* tp_free           */ 0,
};


/** Module Setup & Teardown *******************************************/


int DomletteElement_Init(PyObject *module)
{
  PyObject *value;

  XmlString_IMPORT;

  DomletteElement_Type.tp_base = &DomletteNode_Type;
  if (PyType_Ready(&DomletteElement_Type) < 0)
    return -1;

  value = PyInt_FromLong(ELEMENT_NODE);
  if (value == NULL)
    return -1;
  if (PyDict_SetItemString(DomletteElement_Type.tp_dict, "nodeType", value))
    return -1;
  Py_DECREF(value);

  return 0;
}


void DomletteElement_Fini(void)
{
  PyDict_Clear(DomletteElement_Type.tp_dict);
}
