/*
 * Copyright 2001-2004 Brandon Long
 * All Rights Reserved.
 *
 * ClearSilver Templating System
 *
 * This code is made available under the terms of the ClearSilver License.
 * http://www.clearsilver.net/license.hdf
 *
 */

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

#define NEO_CGI_MODULE
#include "p_neo_util.h"

static PyObject *NeoError;
static PyObject *NeoParseError;

PyObject * p_neo_error (NEOERR *err)
{
  STRING str;

  string_init (&str);
  if (nerr_match(err, NERR_PARSE))
  {
    nerr_error_string (err, &str);
    PyErr_SetString (NeoParseError, str.buf);
  }
  else
  {
    nerr_error_traceback (err, &str);
    PyErr_SetString (NeoError, str.buf);
  }
  string_clear (&str);
  return NULL;
}

#define HDFObjectCheck(a) (!(strcmp((a)->ob_type->tp_name, HDFObjectType.tp_name)))

typedef struct _HDFObject
{
   PyObject_HEAD
   HDF *data;
   int dealloc;
} HDFObject;

static PyObject *p_hdf_value_get_attr (HDFObject *self, char *name);
static void p_hdf_dealloc (HDFObject *ho);

static PyTypeObject HDFObjectType = {
  PyObject_HEAD_INIT(NULL)
    0,			             /*ob_size*/
  "HDFObjectType",	             /*tp_name*/
  sizeof(HDFObject),	     /*tp_size*/
  0,			             /*tp_itemsize*/
  /* methods */
  (destructor)p_hdf_dealloc,	     /*tp_dealloc*/ 
  0,			             /*tp_print*/
  (getattrfunc)p_hdf_value_get_attr,     /*tp_getattr*/
  0,			             /*tp_setattr*/
  0,			             /*tp_compare*/
  (reprfunc)0,                       /*tp_repr*/
  0,                                 /* tp_as_number */
  0,                                 /* tp_as_sequence */
  0,                                 /* tp_as_mapping */
  0,                                 /* tp_as_hash */
};


static void p_hdf_dealloc (HDFObject *ho)
{
  /* ne_warn("deallocating hdf: %X", ho); */
  if (ho->data && ho->dealloc)
  {
    hdf_destroy (&(ho->data));
  }
  PyObject_DEL(ho);
}

PyObject * p_hdf_to_object (HDF *data, int dealloc)
{
  PyObject *rv;

  if (data == NULL)
  {
    rv = Py_None;
    Py_INCREF (rv);
  }
  else
  {
    HDFObject *ho = PyObject_NEW (HDFObject, &HDFObjectType);
    if (ho == NULL) return NULL;
    ho->data = data;
    ho->dealloc = dealloc;
    rv = (PyObject *) ho;
    /* ne_warn("allocating hdf: %X", ho); */
  }
  return rv;
}

HDF * p_object_to_hdf (PyObject *ho)
{
  if (HDFObjectCheck(ho))
  {
    return ((HDFObject *)ho)->data;
  }
  return NULL;
}

static PyObject * p_hdf_init (PyObject *self, PyObject *args)
{
  HDF *hdf = NULL;
  NEOERR *err;

  err = hdf_init (&hdf);
  if (err) return p_neo_error (err);
  return p_hdf_to_object (hdf, 1);
}

static PyObject * p_hdf_get_int_value (PyObject *self, PyObject *args)
{
  HDFObject *ho = (HDFObject *)self;
  PyObject *rv;
  char *name;
  int r, d = 0;

  if (!PyArg_ParseTuple(args, "si:getIntValue(name, default)", &name, &d))
    return NULL;

  r = hdf_get_int_value (ho->data, name, d);
  rv = Py_BuildValue ("i", r);
  return rv;
}

static PyObject * p_hdf_get_value (PyObject *self, PyObject *args)
{
  HDFObject *ho = (HDFObject *)self;
  PyObject *rv;
  char *name;
  char *r, *d = NULL;

  if (!PyArg_ParseTuple(args, "ss:getValue(name, default)", &name, &d))
    return NULL;

  r = hdf_get_value (ho->data, name, d);
  rv = Py_BuildValue ("s", r);
  return rv;
}

static PyObject * p_hdf_get_obj (PyObject *self, PyObject *args)
{
  HDFObject *ho = (HDFObject *)self;
  PyObject *rv;
  char *name;
  HDF *r;

  if (!PyArg_ParseTuple(args, "s:getObj(name)", &name))
    return NULL;

  r = hdf_get_obj (ho->data, name);
  if (r == NULL)
  {
    rv = Py_None;
    Py_INCREF(rv);
    return rv;
  }
  rv = p_hdf_to_object (r, 0);
  return rv;
}

static PyObject * p_hdf_get_child (PyObject *self, PyObject *args)
{
  HDFObject *ho = (HDFObject *)self;
  PyObject *rv;
  char *name;
  HDF *r;

  if (!PyArg_ParseTuple(args, "s:getChild(name)", &name))
    return NULL;

  r = hdf_get_child (ho->data, name);
  if (r == NULL)
  {
    rv = Py_None;
    Py_INCREF(rv);
    return rv;
  }
  rv = p_hdf_to_object (r, 0);
  return rv;
}

static PyObject * p_hdf_get_attr (PyObject *self, PyObject *args)
{
  HDFObject *ho = (HDFObject *)self;
  PyObject *rv, *item;
  char *name;
  HDF_ATTR *attr;

  if (!PyArg_ParseTuple(args, "s:getAttrs(name)", &name))
    return NULL;

  rv = PyList_New(0);
  if (rv == NULL) return NULL;
  Py_INCREF(rv);
  attr = hdf_get_attr (ho->data, name);
  while (attr != NULL)
  {
    item = Py_BuildValue("(s,s)", attr->key, attr->value);
    if (item == NULL)
    {
      Py_DECREF(rv); 
      return NULL;
    }
    if (PyList_Append(rv, item) == -1)
    {
      Py_DECREF(rv); 
      return NULL;
    }
    attr = attr->next;
  }
  return rv;
}

static PyObject * p_hdf_obj_attr (PyObject *self, PyObject *args)
{
  HDFObject *ho = (HDFObject *)self;
  PyObject *rv, *item;
  HDF_ATTR *attr;

  rv = PyList_New(0);
  if (rv == NULL) return NULL;
  Py_INCREF(rv);
  attr = hdf_obj_attr (ho->data);
  while (attr != NULL)
  {
    item = Py_BuildValue("(s,s)", attr->key, attr->value);
    if (item == NULL)
    {
      Py_DECREF(rv); 
      return NULL;
    }
    if (PyList_Append(rv, item) == -1)
    {
      Py_DECREF(rv); 
      return NULL;
    }
    attr = attr->next;
  }
  return rv;
}

static PyObject * p_hdf_obj_child (PyObject *self, PyObject *args)
{
  HDFObject *ho = (HDFObject *)self;
  PyObject *rv;
  HDF *r;

  r = hdf_obj_child (ho->data);
  if (r == NULL)
  {
    rv = Py_None;
    Py_INCREF(rv);
    return rv;
  }
  rv = p_hdf_to_object (r, 0);
  return rv;
}

static PyObject * p_hdf_obj_next (PyObject *self, PyObject *args)
{
  HDFObject *ho = (HDFObject *)self;
  PyObject *rv;
  HDF *r;

  r = hdf_obj_next (ho->data);
  if (r == NULL)
  {
    rv = Py_None;
    Py_INCREF(rv);
    return rv;
  }
  rv = p_hdf_to_object (r, 0);
  return rv;
}

static PyObject * p_hdf_obj_top (PyObject *self, PyObject *args)
{
  HDFObject *ho = (HDFObject *)self;
  PyObject *rv;
  HDF *r;

  r = hdf_obj_top (ho->data);
  if (r == NULL)
  {
    rv = Py_None;
    Py_INCREF(rv);
    return rv;
  }
  rv = p_hdf_to_object (r, 0);
  return rv;
}

static PyObject * p_hdf_obj_name (PyObject *self, PyObject *args)
{
  HDFObject *ho = (HDFObject *)self;
  PyObject *rv;
  char *r;

  r = hdf_obj_name (ho->data);
  if (r == NULL)
  {
    rv = Py_None;
    Py_INCREF(rv);
    return rv;
  }
  rv = Py_BuildValue ("s", r);
  return rv;
}

static PyObject * p_hdf_obj_value (PyObject *self, PyObject *args)
{
  HDFObject *ho = (HDFObject *)self;
  PyObject *rv;
  char *r;

  r = hdf_obj_value (ho->data);
  if (r == NULL)
  {
    rv = Py_None;
    Py_INCREF(rv);
    return rv;
  }
  rv = Py_BuildValue ("s", r);
  return rv;
}

static PyObject * p_hdf_set_value (PyObject *self, PyObject *args)
{
  HDFObject *ho = (HDFObject *)self;
  PyObject *rv;
  char *name, *value;
  NEOERR *err;
  int nlen = 0;
  int vlen = 0;

  if (!PyArg_ParseTuple(args, "s#s#:setValue(name, value)", &name, &nlen, &value, &vlen))
    return NULL;

  err = hdf_set_value (ho->data, name, value);
  if (err) return p_neo_error(err); 

  rv = Py_None;
  Py_INCREF(rv);
  return rv;
}

static PyObject * p_hdf_set_attr (PyObject *self, PyObject *args)
{
  HDFObject *ho = (HDFObject *)self;
  PyObject *rv;
  char *name, *value, *key;
  NEOERR *err;

  if (!PyArg_ParseTuple(args, "ssO:setAttr(name, key, value)", &name, &key, &rv))
    return NULL;

  if (PyString_Check(rv))
  {
    value = PyString_AsString(rv);
  } 
  else if (rv == Py_None)
  {
    value = NULL;
  }
  else
  {
    return PyErr_Format(PyExc_TypeError, "Invalid type for value, expected None or string");
  }
  err = hdf_set_attr (ho->data, name, key, value);
  if (err) return p_neo_error(err); 

  rv = Py_None;
  Py_INCREF(rv);
  return rv;
}

static PyObject * p_hdf_read_file (PyObject *self, PyObject *args)
{
  HDFObject *ho = (HDFObject *)self;
  PyObject *rv;
  char *path;
  NEOERR *err;

  if (!PyArg_ParseTuple(args, "s:readFile(path)", &path))
    return NULL;

  err = hdf_read_file (ho->data, path);
  if (err) return p_neo_error(err); 

  rv = Py_None;
  Py_INCREF(rv);
  return rv;
}

static PyObject * p_hdf_write_file (PyObject *self, PyObject *args)
{
  HDFObject *ho = (HDFObject *)self;
  PyObject *rv;
  char *path;
  NEOERR *err;

  if (!PyArg_ParseTuple(args, "s:writeFile(path)", &path))
    return NULL;

  err = hdf_write_file (ho->data, path);
  if (err) return p_neo_error(err); 

  rv = Py_None;
  Py_INCREF(rv);
  return rv;
}

static PyObject * p_hdf_write_file_atomic (PyObject *self, PyObject *args)
{
  HDFObject *ho = (HDFObject *)self;
  PyObject *rv;
  char *path;
  NEOERR *err;

  if (!PyArg_ParseTuple(args, "s:writeFile(path)", &path))
    return NULL;

  err = hdf_write_file_atomic (ho->data, path);
  if (err) return p_neo_error(err); 

  rv = Py_None;
  Py_INCREF(rv);
  return rv;
}

static PyObject * p_hdf_remove_tree (PyObject *self, PyObject *args)
{
  HDFObject *ho = (HDFObject *)self;
  PyObject *rv;
  char *name;
  NEOERR *err;

  if (!PyArg_ParseTuple(args, "s:removeTree(name)", &name))
    return NULL;

  err = hdf_remove_tree (ho->data, name);
  if (err) return p_neo_error(err); 

  rv = Py_None;
  Py_INCREF(rv);
  return rv;
}

static PyObject * p_hdf_dump (PyObject *self, PyObject *args)
{
  HDFObject *ho = (HDFObject *)self;
  PyObject *rv;
  NEOERR *err;
  STRING str;

  string_init (&str);

  err = hdf_dump_str (ho->data, NULL, 0, &str);
  if (err) return p_neo_error(err); 
  rv = Py_BuildValue ("s", str.buf);
  string_clear (&str);
  return rv;
}

static PyObject * p_hdf_write_string (PyObject *self, PyObject *args)
{
  HDFObject *ho = (HDFObject *)self;
  PyObject *rv;
  NEOERR *err;
  char *s = NULL;

  err = hdf_write_string (ho->data, &s);
  if (err) return p_neo_error(err); 
  rv = Py_BuildValue ("s", s);
  if (s) free(s);
  return rv;
}

static PyObject * p_hdf_read_string (PyObject *self, PyObject *args)
{
  HDFObject *ho = (HDFObject *)self;
  NEOERR *err;
  char *s = NULL;
  int ignore = 0;

  if (!PyArg_ParseTuple(args, "s|i:readString(string)", &s, &ignore))
    return NULL;

  err = hdf_read_string_ignore (ho->data, s, ignore);
  if (err) return p_neo_error(err); 
  Py_INCREF (Py_None);
  return Py_None;
}

static PyObject * p_hdf_copy (PyObject *self, PyObject *args)
{
  HDFObject *ho = (HDFObject *)self;
  HDF *src = NULL;
  PyObject *rv, *o = NULL;
  char *name;
  NEOERR *err;

  if (!PyArg_ParseTuple(args, "sO:copy(name, src_hdf)", &name, &o))
    return NULL;

  src = p_object_to_hdf (o);
  if (src == NULL)
  {
    PyErr_Format(PyExc_TypeError, "second argument must be an HDFObject");
    return NULL;
  }

  err = hdf_copy (ho->data, name, src);
  if (err) return p_neo_error(err); 

  rv = Py_None;
  Py_INCREF(rv);
  return rv;
}

static PyObject * p_hdf_set_symlink (PyObject *self, PyObject *args)
{
  HDFObject *ho = (HDFObject *)self;
  PyObject *rv;
  char *src;
  char *dest;
  NEOERR *err;

  if (!PyArg_ParseTuple(args, "ss:setSymLink(src, dest)", &src, &dest))
    return NULL;

  err = hdf_set_symlink (ho->data, src, dest);
  if (err) return p_neo_error(err); 

  rv = Py_None;
  Py_INCREF(rv);
  return rv;
}

static PyObject * p_hdf_search_path (PyObject *self, PyObject *args)
{
  HDFObject *ho = (HDFObject *)self;
  PyObject *rv;
  char *path;
  char full[_POSIX_PATH_MAX];
  NEOERR *err;

  if (!PyArg_ParseTuple(args, "s:searchPath(path)", &path))
    return NULL;

  err = hdf_search_path (ho->data, path, full);
  if (err) return p_neo_error(err); 

  rv = PyString_FromString(full);
  return rv;
}

static PyMethodDef HDFMethods[] =
{
  {"getIntValue", p_hdf_get_int_value, METH_VARARGS, NULL},
  {"getValue", p_hdf_get_value, METH_VARARGS, NULL},
  {"getObj", p_hdf_get_obj, METH_VARARGS, NULL},
  {"getChild", p_hdf_get_child, METH_VARARGS, NULL},
  {"getAttrs", p_hdf_get_attr, METH_VARARGS, NULL},
  {"child", p_hdf_obj_child, METH_VARARGS, NULL},
  {"next", p_hdf_obj_next, METH_VARARGS, NULL},
  {"name", p_hdf_obj_name, METH_VARARGS, NULL},
  {"value", p_hdf_obj_value, METH_VARARGS, NULL},
  {"top", p_hdf_obj_top, METH_VARARGS, NULL},
  {"attrs", p_hdf_obj_attr, METH_VARARGS, NULL},
  {"setValue", p_hdf_set_value, METH_VARARGS, NULL},
  {"setAttr", p_hdf_set_attr, METH_VARARGS, NULL},
  {"readFile", p_hdf_read_file, METH_VARARGS, NULL},
  {"writeFile", p_hdf_write_file, METH_VARARGS, NULL},
  {"writeFileAtomic", p_hdf_write_file_atomic, METH_VARARGS, NULL},
  {"readString", p_hdf_read_string, METH_VARARGS, NULL},
  {"writeString", p_hdf_write_string, METH_VARARGS, NULL},
  {"removeTree", p_hdf_remove_tree, METH_VARARGS, NULL},
  {"dump", p_hdf_dump, METH_VARARGS, NULL},
  {"copy", p_hdf_copy, METH_VARARGS, NULL},
  {"setSymLink", p_hdf_set_symlink, METH_VARARGS, NULL},
  {"searchPath", p_hdf_search_path, METH_VARARGS, NULL},
  {NULL, NULL}
};

static PyObject * p_escape (PyObject *self, PyObject *args)
{
  PyObject *rv;
  char *s;
  char *escape;
  char *esc_char;
  int buflen;
  char *ret = NULL;
  NEOERR *err;

  if (!PyArg_ParseTuple(args, "s#ss:escape(str, char, escape)", &s, &buflen, &esc_char, &escape))
    return NULL;

  err = neos_escape(s, buflen, esc_char[0], escape, &ret);
  if (err) return p_neo_error(err); 

  rv = Py_BuildValue("s", ret);
  free(ret);
  return rv;
}

static PyObject * p_unescape (PyObject *self, PyObject *args)
{
  PyObject *rv;
  char *s;
  char *copy;
  char *esc_char;
  int buflen;

  if (!PyArg_ParseTuple(args, "s#s:unescape(str, char)", &s, &buflen, &esc_char))
    return NULL;

  copy = strdup(s);
  if (copy == NULL) return PyErr_NoMemory();
  neos_unescape(copy, buflen, esc_char[0]);

  rv = Py_BuildValue("s", copy);
  free(copy);
  return rv;
}

/* This returns the expanded version in the standard python time tuple
 * */
static PyObject * p_time_expand (PyObject *self, PyObject *args)
{
  PyObject *rv;
  int tt;
  struct tm ttm;
  char *tz;

  if (!PyArg_ParseTuple(args, "is:time_expand(time_t, timezone string)", &tt, &tz))
    return NULL;

  neo_time_expand(tt, tz, &ttm); 

  rv = Py_BuildValue("(i,i,i,i,i,i,i,i,i)", ttm.tm_year + 1900, ttm.tm_mon + 1, 
      ttm.tm_mday, ttm.tm_hour, ttm.tm_min, ttm.tm_sec, ttm.tm_wday, 0, ttm.tm_isdst);
  return rv;
}

static PyObject * p_time_compact (PyObject *self, PyObject *args)
{
  PyObject *rv;
  int tt;
  struct tm ttm;
  int junk;
  char *tz;

  memset(&ttm, 0, sizeof(struct tm));

  if (!PyArg_ParseTuple(args, "(i,i,i,i,i,i,i,i,i)s:time_compact(time tuple, timezone string)", &ttm.tm_year, &ttm.tm_mon, &ttm.tm_mday, &ttm.tm_hour, &ttm.tm_min, &ttm.tm_sec, &ttm.tm_wday, &junk, &ttm.tm_isdst, &tz))
    return NULL;

  /* fix up difference between ttm and python tup */
  ttm.tm_year -= 1900;
  ttm.tm_mon -= 1;

  tt = neo_time_compact (&ttm, tz);

  rv = Py_BuildValue("i", tt);
  return rv;
}

static PyMethodDef UtilMethods[] =
{
  {"HDF", p_hdf_init, METH_VARARGS, NULL},
  {"escape", p_escape, METH_VARARGS, NULL},
  {"unescape", p_unescape, METH_VARARGS, NULL},
  {"time_expand", p_time_expand, METH_VARARGS, NULL},
  {"time_compact", p_time_compact, METH_VARARGS, NULL},
  {NULL, NULL}
};

PyObject *p_hdf_value_get_attr (HDFObject *ho, char *name)
{
  return Py_FindMethod(HDFMethods, (PyObject *)ho, name);
}

DL_EXPORT(void) initneo_util(void)
{
  PyObject *m, *d;

  HDFObjectType.ob_type = &PyType_Type;

  m = Py_InitModule("neo_util", UtilMethods);
  d = PyModule_GetDict(m);
  NeoError = PyErr_NewException("neo_util.Error", NULL, NULL);
  NeoParseError = PyErr_NewException("neo_util.ParseError", NULL, NULL);
  PyDict_SetItemString(d, "Error", NeoError);
  PyDict_SetItemString(d, "ParseError", NeoParseError);
}