#include "domlette.h"


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


/* The maximum number of characters of the nodeValue to use when
   creating the repr string.
*/
#define CHARACTERDATA_REPR_LIMIT 20


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


PyObject *CharacterData_SubstringData(PyCharacterDataObject *self, int index, 
                                      int count)
{
  PyObject *newValue;

  newValue = PyUnicode_FromUnicode(NULL, count);
  if (!newValue) return NULL;

  Py_UNICODE_COPY(PyUnicode_AS_UNICODE(newValue),
                  PyUnicode_AS_UNICODE(self->nodeValue) + index,
                  count);
  return newValue;
}


int CharacterData_AppendData(PyCharacterDataObject *self, PyObject *arg)
{
  PyObject *oldValue = self->nodeValue;
  PyObject *newValue;

  newValue = PyUnicode_FromUnicode(NULL,
                                   PyUnicode_GET_SIZE(oldValue) + \
                                   PyUnicode_GET_SIZE(arg));
  if (!newValue) return -1;
  
  Py_UNICODE_COPY(PyUnicode_AS_UNICODE(newValue),
                  PyUnicode_AS_UNICODE(oldValue),
                  PyUnicode_GET_SIZE(oldValue));
  Py_UNICODE_COPY(PyUnicode_AS_UNICODE(newValue) + PyUnicode_GET_SIZE(oldValue),
                  PyUnicode_AS_UNICODE(arg),
                  PyUnicode_GET_SIZE(arg));

  Py_DECREF(oldValue);
  self->nodeValue = newValue;
  return 0;
}


int CharacterData_InsertData(PyCharacterDataObject *self, int offset, 
                             PyObject *arg)
{
  PyObject *oldValue = self->nodeValue;
  PyObject *newValue;

  newValue = PyUnicode_FromUnicode(NULL, 
                                   PyUnicode_GET_SIZE(oldValue) + \
                                   PyUnicode_GET_SIZE(arg));
  if (!newValue) return -1;

  Py_UNICODE_COPY(PyUnicode_AS_UNICODE(newValue), 
                  PyUnicode_AS_UNICODE(oldValue),
                  offset);
  Py_UNICODE_COPY(PyUnicode_AS_UNICODE(newValue) + offset, 
                  PyUnicode_AS_UNICODE(arg),
                  PyUnicode_GET_SIZE(arg));
  Py_UNICODE_COPY(PyUnicode_AS_UNICODE(newValue) + offset + PyUnicode_GET_SIZE(arg), 
                  PyUnicode_AS_UNICODE(oldValue) + offset,
                  PyUnicode_GET_SIZE(oldValue) - offset);

  Py_DECREF(oldValue);
  self->nodeValue = newValue;
  return 0;
}


int CharacterData_DeleteData(PyCharacterDataObject *self, int offset, 
                             int count)
{
  PyObject *oldValue = self->nodeValue;
  PyObject *newValue;

  newValue = PyUnicode_FromUnicode(NULL, PyUnicode_GET_SIZE(oldValue) - count);
  if (!newValue) return -1;
  
  Py_UNICODE_COPY(PyUnicode_AS_UNICODE(newValue),
                  PyUnicode_AS_UNICODE(oldValue),
                  offset);
  Py_UNICODE_COPY(PyUnicode_AS_UNICODE(newValue) + offset,
                  PyUnicode_AS_UNICODE(oldValue) + offset + count,
                  PyUnicode_GET_SIZE(oldValue) - offset - count);

  Py_DECREF(oldValue);
  self->nodeValue = newValue;
  return 0;
}


int CharacterData_ReplaceData(PyCharacterDataObject *self, int offset, 
                              int count, PyObject *arg)
{
  PyObject *oldValue = self->nodeValue;
  PyObject *newValue;

  newValue = PyUnicode_FromUnicode(NULL,
                                   PyUnicode_GET_SIZE(oldValue) - count + \
                                   PyUnicode_GET_SIZE(arg));
  if (!newValue) return -1;

  Py_UNICODE_COPY(PyUnicode_AS_UNICODE(newValue), 
                  PyUnicode_AS_UNICODE(oldValue),
                  offset);
  Py_UNICODE_COPY(PyUnicode_AS_UNICODE(newValue) + offset, 
                  PyUnicode_AS_UNICODE(arg),
                  PyUnicode_GET_SIZE(arg));
  Py_UNICODE_COPY(PyUnicode_AS_UNICODE(newValue) + offset + PyUnicode_GET_SIZE(arg),
                  PyUnicode_AS_UNICODE(oldValue) + offset + count,
                  PyUnicode_GET_SIZE(oldValue) - offset - count);

  Py_DECREF(oldValue);
  self->nodeValue = newValue;
  return 0;
}


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


static char substring_doc[] = "\
Extracts a range of data from the node.";

static PyObject *characterdata_substring(PyObject *self, PyObject *args)
{
  int offset, count;

  if (!PyArg_ParseTuple(args, "ii:substringData", &offset, &count))
    return NULL;

  return CharacterData_SubstringData((PyCharacterDataObject *)self, offset, count);
}


static char append_doc[] = "\
Append the string to the end of the character data of the node.";

static PyObject *characterdata_append(PyObject *self, PyObject *args)
{
  PyObject *data;

  if (!PyArg_ParseTuple(args, "O:appendData", &data))
    return NULL;

  if ((data = DOMString_ConvertArgument(data, "data", 0)) == NULL)
    return NULL;

  if (CharacterData_AppendData((PyCharacterDataObject *)self, data) == -1) {
    Py_DECREF(data);
    return NULL;
  }
  Py_DECREF(data);

  Py_INCREF(Py_None);
  return Py_None;
}


static char insert_doc[] = "\
Insert a string at the specified unicode unit offset.";

static PyObject *characterdata_insert(PyObject *self, PyObject *args)
{
  int offset;
  PyObject *data;

  if (!PyArg_ParseTuple(args, "iO:insertData", &offset, &data))
    return NULL;

  if ((data = DOMString_ConvertArgument(data, "data", 0)) == NULL)
    return NULL;

  if (CharacterData_InsertData((PyCharacterDataObject *)self, offset, data) == -1) {
    Py_DECREF(data);
    return NULL;
  }

  Py_DECREF(data);
  Py_INCREF(Py_None);
  return Py_None;
}


static char delete_doc[] = "\
Remove a range of unicode units from the node.";

static PyObject *characterdata_delete(PyObject *self, PyObject *args)
{
  int offset, count;

  if (!PyArg_ParseTuple(args, "ii:deleteData", &offset, &count))
    return NULL;

  if (CharacterData_DeleteData((PyCharacterDataObject *)self, offset, count) == -1)
    return NULL;

  Py_INCREF(Py_None);
  return Py_None;
}


static char replace_doc[] = "\
Replace the characters starting at the specified unicode unit offset with\n\
the specified string.";

static PyObject *characterdata_replace(PyObject *self, PyObject *args)
{
  int offset, count;
  PyObject *data;

  if (!PyArg_ParseTuple(args, "iiO:replaceData", &offset, &count, &data))
    return NULL;

  if ((data = DOMString_ConvertArgument(data, "data", 0)) == NULL)
    return NULL;

  if (CharacterData_DeleteData((PyCharacterDataObject *)self, offset, count) == -1) {
    Py_DECREF(data);
    return NULL;
  }
  
  Py_DECREF(data);
  Py_INCREF(Py_None);
  return Py_None;
}


static struct PyMethodDef characterdata_methods[] = {
  {"substringData", characterdata_substring, METH_VARARGS, substring_doc },
  {"appendData",    characterdata_append,    METH_VARARGS, append_doc },
  {"insertData",    characterdata_insert,    METH_VARARGS, insert_doc },
  {"deleteData",    characterdata_delete,    METH_VARARGS, delete_doc },
  {"replaceData",   characterdata_replace,   METH_VARARGS, replace_doc },
  { NULL }
};


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


/* No additional interface members defined */


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


static PyObject *get_data(PyCharacterDataObject *self, void *arg)
{
  Py_INCREF(self->nodeValue);
  return self->nodeValue;
}


static int set_data(PyCharacterDataObject *self, PyObject *v, void *arg)
{
  PyObject *nodeValue = DOMString_ConvertArgument(v, (char *)arg, 0);
  if (nodeValue == NULL) return -1;

  Py_DECREF(self->nodeValue);
  self->nodeValue = nodeValue;
  return 0;
}


static PyObject *get_length(PyCharacterDataObject *self, void *arg)
{
    return PyInt_FromLong(PyUnicode_GET_SIZE(self->nodeValue));
}


static PyGetSetDef characterdata_getset[] = {
  { "data",      (getter)get_data,   (setter)set_data, NULL, "data" },
  { "nodeValue", (getter)get_data,   (setter)set_data, NULL, "nodeValue" },
  { "length",    (getter)get_length },
  { NULL }
};


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


static void characterdata_dealloc(PyCharacterDataObject *self)
{
  PyObject_GC_UnTrack((PyObject *) self);

  Py_XDECREF(self->nodeValue);
  self->nodeValue = NULL;
  Node_Del(self);
}


static PyObject *characterdata_repr(PyCharacterDataObject *self)
{
  PyObject *obj, *repr, *name;

  if (PyUnicode_GET_SIZE(self->nodeValue) > CHARACTERDATA_REPR_LIMIT) {
    Py_UNICODE dots[] = { '.', '.', '.' };
    PyObject *slice, *ellipsis;
    slice = PyUnicode_FromUnicode(PyUnicode_AS_UNICODE(self->nodeValue),
                                  CHARACTERDATA_REPR_LIMIT - sizeof(dots));
    ellipsis = PyUnicode_FromUnicode(dots, sizeof(dots));
    if (slice == NULL || ellipsis == NULL) {
      Py_XDECREF(slice);
      Py_XDECREF(ellipsis);
      return NULL;
    }
    obj = PyUnicode_Concat(slice, ellipsis);
    Py_DECREF(slice);
    Py_DECREF(ellipsis);
    if (obj == NULL) return NULL;
  } else {
    obj = self->nodeValue;
    Py_INCREF(obj);
  }

  repr = PyObject_Repr(obj);
  Py_DECREF(obj);
  if (repr == NULL) return NULL;

  name = PyObject_GetAttrString((PyObject *)self->ob_type, "__name__");
  if (name == NULL) {
    Py_DECREF(repr);
    return NULL;
  }

  obj = PyString_FromFormat("<%s at %p: %s>", PyString_AS_STRING(name), self,
                            PyString_AS_STRING(repr));
  Py_DECREF(name);
  Py_DECREF(repr);
  return obj;
}


static char characterdata_doc[] = "\
This interface represents the content of a comment, i.e., all the characters\n\
between the starting '<!--' and ending '-->'.";

PyTypeObject DomletteCharacterData_Type = {
  /* PyObject_HEAD     */ PyObject_HEAD_INIT(NULL)
  /* ob_size           */ 0,
  /* tp_name           */ "Ft.Xml.cDomlette.CharacterData",
  /* tp_basicsize      */ sizeof(PyCharacterDataObject),
  /* tp_itemsize       */ 0,
  /* tp_dealloc        */ (destructor) characterdata_dealloc,
  /* tp_print          */ (printfunc) 0,
  /* tp_getattr        */ (getattrfunc) 0,
  /* tp_setattr        */ (setattrfunc) 0,
  /* tp_compare        */ (cmpfunc) 0,
  /* tp_repr           */ (reprfunc) characterdata_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_BASETYPE),
  /* tp_doc            */ (char *) characterdata_doc,
  /* tp_traverse       */ (traverseproc) 0,
  /* tp_clear          */ (inquiry) 0,
  /* tp_richcompare    */ (richcmpfunc) 0,
  /* tp_weaklistoffset */ 0,
  /* tp_iter           */ (getiterfunc) 0,
  /* tp_iternext       */ (iternextfunc) 0,
  /* tp_methods        */ (PyMethodDef *) characterdata_methods,
  /* tp_members        */ (PyMemberDef *) 0,
  /* tp_getset         */ (PyGetSetDef *) characterdata_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 DomletteCharacterData_Init(PyObject *module)
{
  DomletteCharacterData_Type.tp_base = &DomletteNode_Type;
  if (PyType_Ready(&DomletteCharacterData_Type) < 0)
    return -1;

  return 0;
}


void DomletteCharacterData_Fini(void)
{
  PyDict_Clear(DomletteCharacterData_Type.tp_dict);
}
