/*
swiplmodule.c is a Python extension module for SWI-Prolog.
Copyright (C) 2001  Glen Wilder <gwilder@best.com>

This file is part of PyProlog.
PyProlog is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#include "Python.h"
#include <SWI-Stream.h>
#include <SWI-Prolog.h>

static PyObject *ErrorObject;

typedef struct {
  PyObject_HEAD;
  atom_t	handle;
} PAtom;

typedef struct {
  PyObject_HEAD;
  functor_t	handle;
} PFunctor;

typedef struct {
  PyObject_HEAD;
  term_t	handle; /* integer, acting as opaque pointer */
} PTerm;

typedef struct {
  PyObject_HEAD;
  term_t	handle; /* integer, acting as opaque pointer */
  int quantity;  /* number of alloc'd references */
} PTermArray;

typedef struct {
  PyObject_HEAD;
  predicate_t	handle; 
} PPredicate;

typedef struct {
  PyObject_HEAD;
  module_t	handle; 
} PModule;

typedef struct {
  PyObject_HEAD;
  qid_t	handle; 
} PQuery;

staticforward PyTypeObject PAtom_Type;
staticforward PyTypeObject PModule_Type;
staticforward PyTypeObject PTerm_Type;
staticforward PyTypeObject PTermArray_Type;
staticforward PyTypeObject PFunctor_Type;
staticforward PyTypeObject PPredicate_Type;
staticforward PyTypeObject PQuery_Type;

#define PAtom_Check(v)	((v)->ob_type == &PAtom_Type)
#define PFunctor_Check(v)	((v)->ob_type == &PFunctor_Type)
#define PTerm_Check(v)	((v)->ob_type == &PTerm_Type)
#define PTermArray_Check(v)	((v)->ob_type == &PTermArray_Type)
#define PModule_Check(v)	((v)->ob_type == &PModule_Type)
#define PPredicate_Check(v)	((v)->ob_type == &PPredicate_Type)
#define PQuery_Check(v)	((v)->ob_type == &PQuery_Type)


/* *********************************************************** */
/* **                  PAtom Type                           ** */
/* *********************************************************** */


/* Both Python and SWI-Prolog handle strings with embedded nulls, so we
need not bother with ordinary c-strings. */

static PAtom *
newPAtom(const char *name, int len)
{
  PAtom *self;
  
  self = PyObject_New(PAtom, &PAtom_Type);
  if (self == NULL)
	return NULL;
  
  self->handle = PL_new_atom_nchars(len, name);
  
  return (PAtom *)self;
}

/* PAtom methods */

static PyObject *
PAtom_atom_nchars(PAtom *self, PyObject *args)
{
  const char *text;
  int len;
  
  if (!PyArg_ParseTuple(args, ":atom_nchars"))
	return NULL;
  
  text = PL_atom_nchars(self->handle, &len);
  
  return Py_BuildValue("(i, s#)", len, text, len);
}

static PyMethodDef PAtom_methods[] = {
  {"atom_nchars",	(PyCFunction)PAtom_atom_nchars,	METH_VARARGS},
  {NULL,		NULL}	  
};

static void
PAtom_dealloc(PAtom *self)
{
  PyObject_Del(self);
}

static PyObject *
PAtom_getattr(PAtom *self, char *name)
{
  if (strcmp(name, "handle")==0)
	return Py_BuildValue("l", (long)self->handle);
  else
	return Py_FindMethod(PAtom_methods, (PyObject *)self, name);
}

static PyObject *
PAtom_repr(PAtom *self) 
{
  PyObject *rv;
  const char *text;
  char *buf, *bufp;
  int len;
  
  text = PL_atom_nchars(self->handle, &len);

  buf = (char *)malloc((size_t)len+13);
  if (buf==NULL) {
	PyErr_SetString(PyExc_SystemError, "no memory");
	return NULL;
  }

  bufp = buf;
  memcpy(bufp, "new_atom(\"", 10);
  bufp += 10;
  memcpy(bufp, text, len);
  bufp += len;
  memcpy(bufp, "\")", 2);
  bufp += 2;
  // *bufp = 0;
  rv = Py_BuildValue("s#", buf, 12+len);
  free((void *)buf);
  return rv;
}

statichere PyTypeObject PAtom_Type = {
	PyObject_HEAD_INIT(NULL)
	0,			/*ob_size*/
	"PAtom",			/*tp_name*/
	sizeof(PAtom),	/*tp_basicsize*/
	0,			/*tp_itemsize*/
	/* methods */
	(destructor)PAtom_dealloc, /*tp_dealloc*/
	0,			/*tp_print*/
	(getattrfunc)PAtom_getattr, /*tp_getattr*/
	(setattrfunc)0, /*tp_setattr*/
	0,			/*tp_compare*/
	(reprfunc)PAtom_repr,			/*tp_repr*/
	0,			/*tp_as_number*/
	0,			/*tp_as_sequence*/
	0,			/*tp_as_mapping*/
	0,			/*tp_hash*/
};


/* *********************************************************** */
/* **                  PFunctor Type                        ** */
/* *********************************************************** */

static PFunctor *
newPFunctor(atom_t name, int arity)
{
	PFunctor *self;
	
	self = PyObject_New(PFunctor, &PFunctor_Type);
	if (self == NULL)
		return NULL;
	
	self->handle = PL_new_functor(name, arity);
	
	return (PFunctor *)self;
}

/* PFunctor methods */

static PyObject *
PFunctor_functor_name(PFunctor *self, PyObject *args)
{
  const char *name;
  int len;
  
  if (!PyArg_ParseTuple(args, ":functor_name"))
	return NULL;
  
  name = PL_atom_nchars(PL_functor_name(self->handle), &len);
  
  return Py_BuildValue("s#", name, len);
}

static PyObject *
PFunctor_functor_arity(PFunctor *self, PyObject *args)
{
  int arity;
  
  if (!PyArg_ParseTuple(args, ":functor_arity"))
	return NULL;
  
  arity = PL_functor_arity(self->handle);
  
  return Py_BuildValue("i", arity);
}

static PyMethodDef PFunctor_methods[] = {
  {"functor_name",	(PyCFunction)PFunctor_functor_name,	METH_VARARGS},
  {"functor_arity",	(PyCFunction)PFunctor_functor_arity,	METH_VARARGS},
  {NULL,		NULL}	  
};

static void
PFunctor_dealloc(PFunctor *self)
{
  PyObject_Del(self);
}

static PyObject *
PFunctor_getattr(PFunctor *self, char *name)
{
  if (strcmp(name, "handle")==0)
	return Py_BuildValue("l", (long)self->handle);
  else
	return Py_FindMethod(PFunctor_methods, (PyObject *)self, name);
}

static PyObject *
PFunctor_repr(PFunctor *self) 
{
  PyObject *rv;
  const char *name;
  int len, arity, cplen;
  char *buf, *bufp;
  char intbuf[30];
  
  name = PL_atom_nchars(PL_functor_name(self->handle), &len);
  arity = PL_functor_arity(self->handle);
  
  buf = (char *)malloc((size_t)len+50);
  if (buf==NULL) {
	PyErr_SetString(PyExc_SystemError, "no memory");
	return NULL;
  }
  
  bufp = buf;
  
  memcpy(bufp, "new_functor(", 12);
  bufp += 12;
  
  memcpy(bufp, name, len);
  bufp += len;
  
  memcpy(bufp, ", ", 2);
  bufp += 2;
  
  cplen = sprintf(intbuf, "%d", arity);
  if (cplen < 1 || cplen > 4) {
	free((void *)buf);
	PyErr_SetString(PyExc_SystemError, "error on sprintf");
	return NULL;
  }
  memcpy(bufp, intbuf, cplen);
  bufp += cplen;
  
  memcpy(bufp++, ")", 1);
  
  *bufp = 0;
  
  rv = Py_BuildValue("s#", buf, bufp-buf);
  free((void *)buf);
  return rv;
}

statichere PyTypeObject PFunctor_Type = {
	PyObject_HEAD_INIT(NULL)
	0,			/*ob_size*/
	"PFunctor",			/*tp_name*/
	sizeof(PFunctor),	/*tp_basicsize*/
	0,			/*tp_itemsize*/
	/* methods */
	(destructor)PFunctor_dealloc, /*tp_dealloc*/
	0,			/*tp_print*/
	(getattrfunc)PFunctor_getattr, /*tp_getattr*/
	(setattrfunc)0, /*tp_setattr*/
	0,			/*tp_compare*/
	(reprfunc)PFunctor_repr,			/*tp_repr*/
	0,			/*tp_as_number*/
	0,			/*tp_as_sequence*/
	0,			/*tp_as_mapping*/
	0,			/*tp_hash*/
};


/* *********************************************************** */
/* **                  PTerm Type                           ** */
/* *********************************************************** */

static PTerm *
newPTerm(void)
{
	PTerm *self;
	
	self = PyObject_NEW(PTerm, &PTerm_Type);
	if (self == NULL)
		return NULL;

	self->handle = PL_new_term_ref();

	return self;
}

/* PTerm methods */

static PyObject *
PTerm_term_type(PTerm *self, PyObject *args)
{
  if (!PyArg_ParseTuple(args, ":term_type"))
	return NULL;
  return Py_BuildValue("i", PL_term_type(self->handle));
}

static PyObject *
PTerm_is_variable(PTerm *self, PyObject *args)
{
  if (!PyArg_ParseTuple(args, ":is_variable"))
	return NULL;
  return Py_BuildValue("i", PL_is_variable(self->handle));
}

static PyObject *
PTerm_is_atom(PTerm *self, PyObject *args)
{
  if (!PyArg_ParseTuple(args, ":is_atom"))
	return NULL;
  return Py_BuildValue("i", PL_is_atom(self->handle));
}

static PyObject *
PTerm_is_string(PTerm *self, PyObject *args)
{
  if (!PyArg_ParseTuple(args, ":is_string"))
	return NULL;
  return Py_BuildValue("i", PL_is_string(self->handle));
}

static PyObject *
PTerm_is_integer(PTerm *self, PyObject *args)
{
  if (!PyArg_ParseTuple(args, ":is_integer"))
	return NULL;
  return Py_BuildValue("i", PL_is_integer(self->handle));
}

static PyObject *
PTerm_is_float(PTerm *self, PyObject *args)
{
  if (!PyArg_ParseTuple(args, ":is_float"))
	return NULL;
  return Py_BuildValue("i", PL_is_float(self->handle));
}

static PyObject *
PTerm_is_compound(PTerm *self, PyObject *args)
{
  if (!PyArg_ParseTuple(args, ":is_compound"))
	return NULL;
  return Py_BuildValue("i", PL_is_compound(self->handle));
}

static PyObject *
PTerm_is_functor(PTerm *self, PyObject *args)
{
  PyObject *functor;
  functor_t ft;
  
  if (!PyArg_ParseTuple(args, "O:is_functor", &functor))
	return NULL;
  
  if (!PFunctor_Check(functor)) {
	PyErr_BadArgument();
	return NULL;
  }
  
  ft = ((PFunctor *)functor)->handle;
  return Py_BuildValue("i", PL_is_functor(self->handle, ft));
}

static PyObject *
PTerm_is_list(PTerm *self, PyObject *args)
{
  if (!PyArg_ParseTuple(args, ":is_list"))
	return NULL;
  return Py_BuildValue("i", PL_is_list(self->handle));
}

static PyObject *
PTerm_is_atomic(PTerm *self, PyObject *args)
{
  if (!PyArg_ParseTuple(args, ":is_atomic"))
	return NULL;
  return Py_BuildValue("i", PL_is_atomic(self->handle));
}

static PyObject *
PTerm_is_number(PTerm *self, PyObject *args)
{
  if (!PyArg_ParseTuple(args, ":is_number"))
	return NULL;
  return Py_BuildValue("i", PL_is_number(self->handle));
}

static PyObject *
PTerm_get_atom(PTerm *self, PyObject *args)
{
  atom_t atom;
  PAtom *rv;
  
  if (!PyArg_ParseTuple(args, ":get_atom"))
	return NULL;
  
  if (!PL_get_atom(self->handle, &atom)) {
	PyErr_SetString(ErrorObject, "PL_get_atom failed");
	return NULL;
  }
  
  rv = PyObject_NEW(PAtom, &PAtom_Type);
  if (rv == NULL)
	return NULL;
  
  rv->handle = atom;
  
  return (PyObject *)rv;
}

static PyObject *
PTerm_get_chars(PTerm *self, PyObject *args)
{
  PyObject *rv;
  int flags = CVT_ALL | CVT_VARIABLE | BUF_RING;
  char *buffer=NULL;
  
  if (!PyArg_ParseTuple(args, "|i:get_chars", &flags))
	return NULL;

  if (!PL_get_chars(self->handle, &buffer, flags)) {
	PyErr_SetString(PyExc_SystemError, "PL_get_chars");
	return NULL;
  }

  rv = Py_BuildValue("s", buffer);
  if (flags & BUF_MALLOC) 
	free(buffer);
  return rv;
}

static PyObject *
PTerm_get_integer(PTerm *self, PyObject *args)
{
  int i;
  
  if (!PyArg_ParseTuple(args, ":get_integer"))
	return NULL;
  
  if (!PL_get_integer(self->handle, &i)) {
	PyErr_SetString(ErrorObject, "PL_get_integer failed");
	return NULL;
  }

  return Py_BuildValue("i", i);
}

static PyObject *
PTerm_get_long(PTerm *self, PyObject *args)
{
  long l;
  
  if (!PyArg_ParseTuple(args, ":get_long"))
	return NULL;
  
  if (!PL_get_long(self->handle, &l)) {
	PyErr_SetString(ErrorObject, "PL_get_long failed");
	return NULL;
  }

  return Py_BuildValue("l", l);
}

static PyObject *
PTerm_get_float(PTerm *self, PyObject *args)
{
  double d;
  
  if (!PyArg_ParseTuple(args, ":get_float"))
	return NULL;
  
  if (!PL_get_float(self->handle, &d)) {
	PyErr_SetString(ErrorObject, "PL_get_float failed");
	return NULL;
  }
  
  return Py_BuildValue("d", d);
}

static PyObject *
PTerm_get_functor(PTerm *self, PyObject *args)
{
  PFunctor *rv;
  functor_t func;
  
  if (!PyArg_ParseTuple(args, ":get_functor"))
	return NULL;
  
  if (!PL_get_functor(self->handle, &func)) {
	PyErr_SetString(ErrorObject, "PL_get_functor failed");
	return NULL;
  }
  
  rv = PyObject_New(PFunctor, &PFunctor_Type);
  if (rv == NULL)
	return NULL;

  rv->handle = func;
  
  return (PyObject *)rv;
}

static PyObject *
PTerm_get_name_arity(PTerm *self, PyObject *args)
{
  PAtom *rv;
  atom_t name;
  int arity;
  
  if (!PyArg_ParseTuple(args, ":get_name_arity"))
	return NULL;
  
  if (!PL_get_name_arity(self->handle, &name, &arity)) {
	PyErr_SetString(ErrorObject, "PL_get_name_arity failed");
	return NULL;
  }

  rv = PyObject_New(PAtom, &PAtom_Type);
  if (rv == NULL)
	return NULL;
  
  rv->handle = name;
  
  return Py_BuildValue("Oi", (PyObject *)rv, arity);
}

static PyObject *
PTerm_get_module(PTerm *self, PyObject *args)
{
  PModule *rv;
  module_t module;
  
  if (!PyArg_ParseTuple(args, ":get_module"))
	return NULL;
  
  if (!PL_get_module(self->handle, &module)) {
	PyErr_SetString(ErrorObject, "PL_get_module failed");
	return NULL;
  }
  
  rv = PyObject_New(PModule, &PModule_Type);
  if (rv == NULL)
	return NULL;
  
  rv->handle = module;
  
  return (PyObject *)rv;
}

static PyObject *
PTerm_get_arg(PTerm *self, PyObject *args)
{
  PTerm *rv;
  term_t argument;
  int index;
  
  if (!PyArg_ParseTuple(args, "i:get_arg", &index))
	return NULL;
  
  argument = PL_new_term_ref();
  
  if (!PL_get_arg(index, self->handle, argument)) {
	PyErr_SetString(ErrorObject, "PL_get_arg failed");
	return NULL;
  }
  
  /* TBD: what if argument is a term array? */
  rv = PyObject_New(PTerm, &PTerm_Type);
  if (rv == NULL)
	return NULL;
  
  rv->handle = argument;
  
  return (PyObject *)rv;
  
}

static PyObject *
PTerm_get_list(PTerm *self, PyObject *args)
{
  PTerm *r_head;
  PTerm *r_tail;
  term_t head;
  
  if (!PyArg_ParseTuple(args, ":get_list"))
	return NULL;
  
  head = PL_new_term_refs(2);
  
  if (!PL_get_list(self->handle, head, head+1)) {
	PyErr_SetString(ErrorObject, "PL_get_list failed");
	return NULL;
  }
  
  r_head = PyObject_New(PTerm, &PTerm_Type);
  if (r_head == NULL) {
	PL_reset_term_refs(head);
	return NULL;
  }
  r_head->handle = head;
  
  r_tail = PyObject_New(PTerm, &PTerm_Type);
  if (r_tail == NULL) {
	PL_reset_term_refs(head);
	Py_DECREF(r_head);
	return NULL;
  }
  r_tail->handle = head+1;
  
  return Py_BuildValue("OO", (PyObject *)r_head, (PyObject *)r_tail);
}

static PyObject *
PTerm_get_head(PTerm *self, PyObject *args)
{
  PTerm *r_head;
  term_t head;
  
  if (!PyArg_ParseTuple(args, ":get_head"))
	return NULL;
  
  head = PL_new_term_ref();
  
  if (!PL_get_head(self->handle, head)) {
	PyErr_SetString(ErrorObject, "PL_get_head failed");
	return NULL;
  }
  
  r_head = PyObject_New(PTerm, &PTerm_Type);
  if (r_head == NULL) {
	PL_reset_term_refs(head);
	return NULL;
  }
  r_head->handle = head;
  
  return (PyObject *)r_head;
}

static PyObject *
PTerm_get_tail(PTerm *self, PyObject *args)
{
  PTerm *r_tail;
  term_t tail;
  
  if (!PyArg_ParseTuple(args, ":get_tail"))
	return NULL;
  
  tail = PL_new_term_ref();
  
  if (!PL_get_tail(self->handle, tail)) {
	PyErr_SetString(ErrorObject, "PL_get_tail failed");
	return NULL;
  }
  
  r_tail = PyObject_New(PTerm, &PTerm_Type);
  if (r_tail == NULL) {
	PL_reset_term_refs(tail);
	return NULL;
  }
  r_tail->handle = tail;
  
  return (PyObject *)r_tail;
}

static PyObject *
PTerm_get_nil(PTerm *self, PyObject *args)
{
  
  if (!PyArg_ParseTuple(args, ":get_nil"))
	return NULL;
  
  return Py_BuildValue("i", PL_get_nil(self->handle));
}

static PyObject *
PTerm_put_variable(PTerm *self, PyObject *args)
{
  if (!PyArg_ParseTuple(args, ":put_variable"))
	return NULL;
  
  PL_put_variable(self->handle);
  
  Py_INCREF(Py_None);
  return Py_None;
}

static PyObject *
PTerm_put_atom(PTerm *self, PyObject *args)
{
  PyObject *atom;
  
  if (!PyArg_ParseTuple(args, "O:put_atom", &atom))
	return NULL;
  
  if (!PAtom_Check(atom)) {
	PyErr_BadArgument();
	return NULL;
  }
  
  PL_put_atom(self->handle, ((PAtom *)atom)->handle);
  
  Py_INCREF(Py_None);
  return Py_None;
}

static PyObject *
PTerm_put_integer(PTerm *self, PyObject *args)
{
  int value;
  
  if (!PyArg_ParseTuple(args, "i:put_integer", &value))
	return NULL;
  
  PL_put_integer(self->handle, value);
  
  Py_INCREF(Py_None);
  return Py_None;
}

static PyObject *
PTerm_put_float(PTerm *self, PyObject *args)
{
  double value;
  
  if (!PyArg_ParseTuple(args, "d:put_float", &value))
	return NULL;
  
  PL_put_float(self->handle, value);
  
  Py_INCREF(Py_None);
  return Py_None;
}

static PyObject *
PTerm_put_string(PTerm *self, PyObject *args)
{
  char *value;
  int leng;
  
  if (!PyArg_ParseTuple(args, "s#:put_string", &value, &leng))
	return NULL;
	
  PL_put_string_nchars(self->handle, leng, value);
	
  Py_INCREF(Py_None);
  return Py_None;
}

static PyObject *
PTerm_put_functor(PTerm *self, PyObject *args)
{
  PyObject *functor;
  
  if (!PyArg_ParseTuple(args, "O:put_functor", &functor))
	return NULL;
  
  if (! PFunctor_Check(functor)) {
	PyErr_BadArgument();
	return NULL;
  }
  
  PL_put_functor(self->handle, ((PFunctor *)functor)->handle);
  
  Py_INCREF(Py_None);
  return Py_None;
}

static PyObject *
PTerm_put_term(PTerm *self, PyObject *args)
{
  PyObject *term;
  
  if (!PyArg_ParseTuple(args, "O:put_term", &term))
	return NULL;
  
  if (! PTerm_Check(term)) {
	PyErr_BadArgument();
	return NULL;
  }
  
  PL_put_term(self->handle, ((PTerm *)term)->handle);
  
  Py_INCREF(Py_None);
  return Py_None;
}

static PyObject *
PTerm_unify(PTerm *self, PyObject *args) 
{
	PyObject *other;
	int rc;
	
	if (!PyArg_ParseTuple(args, "O:unify", &other))
		return NULL;
	
	if (! PTerm_Check(other)) {
	   PyErr_BadArgument();
	   return NULL;
	}
	
	rc = PL_unify(self->handle, ((PTerm *)other)->handle);
	
	return Py_BuildValue("i", rc);
}

static PyObject *
PTerm_call(PTerm *self, PyObject *args) 
{
	PyObject *module=NULL;
	int rc;
	
	if (!PyArg_ParseTuple(args, "|O:call", &module))
		return NULL;
	
	if (module) {
		if (! PModule_Check(module)) {
			PyErr_BadArgument();
			return NULL;
		}
		rc = PL_call(self->handle, ((PModule *)module)->handle);
	} else
		rc = PL_call(self->handle, NULL);
	
	return Py_BuildValue("i", rc);
}

static PyObject *
PTerm_put_nil(PTerm *self, PyObject *args) 
{
	
	if (!PyArg_ParseTuple(args, ":put_nil"))
		return NULL;
	
	PL_put_nil(self->handle);
	
	Py_INCREF(Py_None);
	return Py_None;
}

static PyObject *
PTerm_put_list(PTerm *self, PyObject *args)
{
  
  if (!PyArg_ParseTuple(args, ":put_list"))
	return NULL;
  
  PL_put_list(self->handle);
  
  Py_INCREF(Py_None);
  return Py_None;
}

/*
new = new_term()
new.cons_list(head, tail)
*/
static PyObject *
PTerm_cons_list(PTerm *self, PyObject *args)
{
  PyObject *head, *tail;
  
  if (!PyArg_ParseTuple(args, "OO:cons_list", &head, &tail))
	return NULL;
  
  if (! PTerm_Check(head)) {
	  PyErr_BadArgument();
	  return NULL;
  }
  
  if (! PTerm_Check(tail)) {
	  PyErr_BadArgument();
	  return NULL;
  }
  
  PL_cons_list(self->handle,((PTerm *)head)->handle, ((PTerm *)tail)->handle);
  
  Py_INCREF(Py_None);
  return Py_None;
}

static PyObject *
PTerm_cons_functor(PTerm *self, PyObject *args)
{
  PyObject *functor, *term_array;
  
  if (!PyArg_ParseTuple(args, "OO:cons_functor", &functor, &term_array))
	return NULL;
  
  if (! PFunctor_Check(functor)) {
	  PyErr_BadArgument();
	  return NULL;
  }
  
  if (! PTermArray_Check(term_array)) {
	  PyErr_BadArgument();
	  return NULL;
  }
  
  PL_cons_functor_v(self->handle,
			   ((PFunctor *)functor)->handle, 
			   ((PTermArray *)term_array)->handle);
  
  Py_INCREF(Py_None);
  return Py_None;
}

static PyMethodDef PTerm_methods[] = {
	{"term_type",	(PyCFunction)PTerm_term_type,	METH_VARARGS},
	{"is_variable",	(PyCFunction)PTerm_is_variable,	METH_VARARGS},
	{"is_atom",	(PyCFunction)PTerm_is_atom,	METH_VARARGS},
	{"is_string",	(PyCFunction)PTerm_is_string,	METH_VARARGS},
	{"is_integer",	(PyCFunction)PTerm_is_integer,	METH_VARARGS},
	{"is_float",	(PyCFunction)PTerm_is_float,	METH_VARARGS},
	{"is_compound",	(PyCFunction)PTerm_is_compound,	METH_VARARGS},
	{"is_functor",	(PyCFunction)PTerm_is_functor,	METH_VARARGS},
	{"is_list",	(PyCFunction)PTerm_is_list,	METH_VARARGS},
	{"is_number",	(PyCFunction)PTerm_is_number,	METH_VARARGS},
	{"is_atomic",	(PyCFunction)PTerm_is_atomic,	METH_VARARGS},
	{"get_atom",	(PyCFunction)PTerm_get_atom,	METH_VARARGS},
	{"get_chars",	(PyCFunction)PTerm_get_chars,	METH_VARARGS},
	{"get_integer",	(PyCFunction)PTerm_get_integer,	METH_VARARGS},
	{"get_long",	(PyCFunction)PTerm_get_long,	METH_VARARGS},
	{"get_float",	(PyCFunction)PTerm_get_float,	METH_VARARGS},
	{"get_functor",	(PyCFunction)PTerm_get_functor,	METH_VARARGS},
	{"get_name_arity",	(PyCFunction)PTerm_get_name_arity,	METH_VARARGS},
	{"get_module",	(PyCFunction)PTerm_get_module,	METH_VARARGS},
	{"get_arg",	(PyCFunction)PTerm_get_arg,	METH_VARARGS},
	{"get_list",	(PyCFunction)PTerm_get_list,	METH_VARARGS},
	{"get_head",	(PyCFunction)PTerm_get_head,	METH_VARARGS},
	{"get_tail",	(PyCFunction)PTerm_get_tail,	METH_VARARGS},
	{"get_nil",	(PyCFunction)PTerm_get_nil,	METH_VARARGS},
	{"put_variable",	(PyCFunction)PTerm_put_variable,	METH_VARARGS},
	{"put_atom",	(PyCFunction)PTerm_put_atom,	METH_VARARGS},
	{"put_integer",	(PyCFunction)PTerm_put_integer,	METH_VARARGS},
	{"put_float",	(PyCFunction)PTerm_put_float,	METH_VARARGS},
	{"put_string",	(PyCFunction)PTerm_put_string,	METH_VARARGS},
	{"put_functor",	(PyCFunction)PTerm_put_functor,	METH_VARARGS},
	{"put_term",	(PyCFunction)PTerm_put_term,	METH_VARARGS},
	{"unify",	(PyCFunction)PTerm_unify,	METH_VARARGS},
	{"call",	(PyCFunction)PTerm_call,	METH_VARARGS},
	{"put_nil",	(PyCFunction)PTerm_put_nil,	METH_VARARGS},
	{"put_list",	(PyCFunction)PTerm_put_list,	METH_VARARGS},
	{"cons_list",	(PyCFunction)PTerm_cons_list,	METH_VARARGS},
	{"cons_functor",	(PyCFunction)PTerm_cons_functor,	METH_VARARGS},
	{NULL,		NULL}	  
};

static void
PTerm_dealloc(PTerm *self)
{
	PyObject_Del(self);
}

static PyObject *
PTerm_getattr(PTerm *self, char *name)
{
  if (strcmp(name, "type") == 0) {
	if (PL_is_atom(self->handle))
	  return Py_BuildValue("s", "prolog atom");
	if (PL_is_variable(self->handle))
	  return Py_BuildValue("s", "prolog variable");
	if (PL_is_integer(self->handle))
	  return Py_BuildValue("s", "prolog integer");
	if (PL_is_string(self->handle))
	  return Py_BuildValue("s", "prolog string");
	if (PL_is_float(self->handle))
	  return Py_BuildValue("s", "prolog float");
	if (PL_is_compound(self->handle))
	  return Py_BuildValue("s", "prolog compound");
	return Py_BuildValue("s", "prolog unknown");
  } else if (strcmp(name, "handle") == 0) {
	return Py_BuildValue("i", self->handle);
  } else
	return Py_FindMethod(PTerm_methods, (PyObject *)self, name);
}

static PyObject *
PTerm_repr(PTerm *self) 
{
  //  int precedence, flags;
  int sizep = 0;
  unsigned flags = 0;
  char *buffer=NULL;
  IOSTREAM *stream;
  
#if 1
  stream = Sopenmem(&buffer, &sizep, "w");
  PL_write_term(stream, self->handle, 1200, flags);
  if (Sclose(stream)) {
	PyErr_SetString(PyExc_SystemError, "problem closing stream");
	return NULL;
  }
#else
  /* CVT_WRITE does not work */
  flags = flags | CVT_ALL | CVT_WRITE | CVT_VARIABLE | BUF_DISCARDABLE;
  PL_get_chars(self->handle, &buffer, flags);
#endif
  return Py_BuildValue("s", buffer);
}

int
PTerm_compare(PTerm *self, PTerm *other) 
{
  return PL_compare(self->handle, other->handle);
}

statichere PyTypeObject PTerm_Type = {
	/* The ob_type field must be initialized in the module init function
	 * to be portable to Windows without using C++. */
	PyObject_HEAD_INIT(NULL)
	0,			/*ob_size*/
	"PTerm",			/*tp_name*/
	sizeof(PTerm),	/*tp_basicsize*/
	0,			/*tp_itemsize*/
	/* methods */
	(destructor)PTerm_dealloc, /*tp_dealloc*/
	0,			/*tp_print*/
	(getattrfunc)PTerm_getattr, /*tp_getattr*/
	(setattrfunc)0, /*tp_setattr*/
	(cmpfunc)PTerm_compare,			/*tp_compare*/
	(reprfunc)PTerm_repr,			/*tp_repr*/
	0,			/*tp_as_number*/
	0,			/*tp_as_sequence*/
	0,			/*tp_as_mapping*/
	0,			/*tp_hash*/
};

/* *********************************************************** */
/* **                  PTermArray Type                      ** */
/* *********************************************************** */

static PTermArray *
newPTermArray(int quantity)
{
	PTermArray *self;
	
	self = PyObject_NEW(PTermArray, &PTermArray_Type);
	if (self == NULL)
		return NULL;

	self->handle = PL_new_term_refs(quantity);
	self->quantity = quantity;
	
	return self;
}

/* PTermArray methods */

/* get ref from contiguous list */
static PyObject *
PTermArray_item(PTermArray *self, PyObject *args) 
{
	PTerm *rv;
	int index;
	
	if (!PyArg_ParseTuple(args, "i:item", &index))
		return NULL;
	
	if (0 > index || self->quantity < index) {
	   PyErr_BadArgument();
	   return NULL;
	}
	
	rv = PyObject_NEW(PTerm, &PTerm_Type);
	if (rv == NULL)
		return NULL;
	
	/* PL_put_term(rv->handle, self->handle+index); */
	rv->handle = PL_copy_term_ref(self->handle+index);
	
	return (PyObject *)rv;
}

static PyMethodDef PTermArray_methods[] = {
	{"item",	(PyCFunction)PTermArray_item,	METH_VARARGS},
	{NULL,		NULL}	  
};

static void
PTermArray_dealloc(PTermArray *self)
{
  PyObject_Del(self);
}

static PyObject *
PTermArray_getattr(PTermArray *self, char *name)
{
  if (strcmp(name, "handle") == 0) {
	return Py_BuildValue("i", self->handle);
  } else if (strcmp(name, "quantity") == 0) {
	return Py_BuildValue("i", self->quantity);
  } else
	return Py_FindMethod(PTermArray_methods, (PyObject *)self, name);
}

static PyObject *
PTermArray_repr(PTermArray *self) 
{
  char buf[100];
  
  sprintf(buf, "PTermArray[%d]", self->quantity);
  return Py_BuildValue("s", buf);
}

statichere PyTypeObject PTermArray_Type = {
	/* The ob_type field must be initialized in the module init function
	 * to be portable to Windows without using C++. */
	PyObject_HEAD_INIT(NULL)
	0,			/*ob_size*/
	"PTermArray",			/*tp_name*/
	sizeof(PTermArray),	/*tp_basicsize*/
	0,			/*tp_itemsize*/
	/* methods */
	(destructor)PTermArray_dealloc, /*tp_dealloc*/
	0,			/*tp_print*/
	(getattrfunc)PTermArray_getattr, /*tp_getattr*/
	(setattrfunc)0, /*tp_setattr*/
	(cmpfunc)0,			/*tp_compare*/
	(reprfunc)PTermArray_repr,			/*tp_repr*/
	0,			/*tp_as_number*/
	0,			/*tp_as_sequence*/
	0,			/*tp_as_mapping*/
	0,			/*tp_hash*/
};

/* *********************************************************** */
/* **                  PPredicate Type                      ** */
/* *********************************************************** */

static PPredicate *
newPPredicate(char *name, int arity, char *module)
{
	PPredicate *self;
	self = PyObject_NEW(PPredicate, &PPredicate_Type);
	if (self == NULL)
		return NULL;
	self->handle = PL_predicate(name, arity, module);
	return self;
}

/* PPredicate methods */

static PyObject *
PPredicate_call_predicate(PPredicate *self, PyObject *args)
{
  PyObject *terms;
  PyObject *module = NULL;
  int flags;
  int rv;
  
  module_t m = NULL;
  
  if (!PyArg_ParseTuple(args, "iO|O:call_predicate", &flags, &terms, &module))
	return NULL;
  
  if (! PTermArray_Check(terms)) {
	PyErr_BadArgument();
	return NULL;
  }
  
#if 0
  if (module)
	m = ((PyModule *)module)->handle;
#endif
  
  rv = PL_call_predicate(m, flags, self->handle, ((PTerm *)terms)->handle);
  
  return Py_BuildValue("i", rv);
}

static PyObject *
PPredicate_predicate_info(PPredicate *self, PyObject *args)
{
  PModule *module;
  PAtom *atom;
  
  atom_t n;
  int a;
  module_t m=NULL;
  
  if (!PyArg_ParseTuple(args, ":predicate_info"))
	return NULL;
  
  if (!PL_predicate_info(self->handle, &n, &a, &m)) {
	PyErr_SetString(PyExc_SystemError, "problem calling PL_predicate_info");
	return NULL;
  }
  
  atom = PyObject_New(PAtom, &PAtom_Type);
  if (atom == NULL)
	return NULL;
  atom->handle = n;
  
  module = PyObject_New(PModule, &PModule_Type);
  if (module == NULL){
	Py_DECREF(atom);
	return NULL;
  }
  module->handle = m;
  
  return Py_BuildValue("NiN", atom, a, module);
}

static PyMethodDef PPredicate_methods[] = {
  {"call_predicate",	(PyCFunction)PPredicate_call_predicate,	METH_VARARGS},
  {"predicate_info",	(PyCFunction)PPredicate_predicate_info,	METH_VARARGS},
  {NULL, NULL}
};

static void
PPredicate_dealloc(PPredicate *self)
{
	PyObject_Del(self);
}

static PyObject *
PPredicate_getattr(PPredicate *self, char *name)
{
  if (strcmp(name, "handle")==0) {
	return Py_BuildValue("i", self->handle);
  } else
	return Py_FindMethod(PPredicate_methods, (PyObject *)self, name);
}


static PyObject *
PPredicate_repr(PPredicate *self) 
{
  char buf[200];
  atom_t n;
  int arity;
  module_t m;
  
  if (!PL_predicate_info(self->handle, &n, &arity, &m)) {
	PyErr_SetString(PyExc_SystemError, "problem calling PL_predicate_info");
	return NULL;
  }
  
  sprintf(buf, "%s:%s/%d", PL_atom_chars(PL_module_name(m)), PL_atom_chars(n), arity);
  
  return Py_BuildValue("s", buf);
}
 
/* void PL_predicate_info(predicate_t p, atom_t *n, int *a, module_t *m) */

statichere PyTypeObject PPredicate_Type = {
	/* The ob_type field must be initialized in the module init function
	 * to be portable to Windows without using C++. */
	PyObject_HEAD_INIT(NULL)
	0,			/*ob_size*/
	"PPredicate",			/*tp_name*/
	sizeof(PPredicate),	/*tp_basicsize*/
	0,			/*tp_itemsize*/
	/* methods */
	(destructor)PPredicate_dealloc, /*tp_dealloc*/
	0,			/*tp_print*/
	(getattrfunc)PPredicate_getattr, /*tp_getattr*/
	(setattrfunc)0, /*tp_setattr*/
	0,			/*tp_compare*/
	(reprfunc)PPredicate_repr,			/*tp_repr*/
	0,			/*tp_as_number*/
	0,			/*tp_as_sequence*/
	0,			/*tp_as_mapping*/
	0,			/*tp_hash*/
};


/* *********************************************************** */
/* **                  PModule Type                         ** */
/* *********************************************************** */


static PModule *
newPModule(atom_t module_name)
{
	PModule *self;
	
	self = PyObject_NEW(PModule, &PModule_Type);
	if (self == NULL)
		return NULL;
	
	self->handle = PL_new_module(module_name);
	
	return self;
}

/* PModule methods */

static PyObject *
PModule_module_name(PModule *self, PyObject *args)
{
  PAtom *rv;
  atom_t atom;
  
  if (!PyArg_ParseTuple(args, ":module_name"))
	return NULL;
  
  atom = PL_module_name(self->handle);
  
  rv = PyObject_New(PAtom, &PAtom_Type);
  if (self == NULL)
	return NULL;
  
  rv->handle = atom;
  return (PyObject *)rv;
}

static PyMethodDef PModule_methods[] = {
  {"module_name",	(PyCFunction)PModule_module_name,	METH_VARARGS},
  {NULL, NULL}
};

static void
PModule_dealloc(PModule *self)
{
	PyObject_Del(self);
}

static PyObject *
PModule_getattr(PModule *self, char *name)
{
  if (strcmp(name, "handle")==0) {
	return Py_BuildValue("i", self->handle);
  } else
	return Py_FindMethod(PModule_methods, (PyObject *)self, name);
}

static PyObject *
PModule_repr(PModule *self) 
{
  atom_t atom;
  char buf[100];
  
  atom = PL_module_name(self->handle);
  sprintf(buf, "new_module(new_atom(%s))", PL_atom_chars(atom)); 
  return Py_BuildValue("s", buf);
}

statichere PyTypeObject PModule_Type = {
	/* The ob_type field must be initialized in the module init function
	 * to be portable to Windows without using C++. */
	PyObject_HEAD_INIT(NULL)
	0,			/*ob_size*/
	"PModule",			/*tp_name*/
	sizeof(PModule),	/*tp_basicsize*/
	0,			/*tp_itemsize*/
	/* methods */
	(destructor)PModule_dealloc, /*tp_dealloc*/
	0,			/*tp_print*/
	(getattrfunc)PModule_getattr, /*tp_getattr*/
	(setattrfunc)0, /*tp_setattr*/
	0,			/*tp_compare*/
	(reprfunc)PModule_repr,			/*tp_repr*/
	0,			/*tp_as_number*/
	0,			/*tp_as_sequence*/
	0,			/*tp_as_mapping*/
	0,			/*tp_hash*/
};

/* *********************************************************** */
/* **                  PQuery Type                          ** */
/* *********************************************************** */


static PQuery *
openPQuery(module_t ctx, int flags, predicate_t p, term_t t0)
{
	PQuery *self;
	
	self = PyObject_NEW(PQuery, &PQuery_Type);
	if (self == NULL)
		return NULL;
	
	self->handle = PL_open_query(ctx, flags, p, t0);
	
	return self;
}

/* PQuery methods */

static PyObject *
PQuery_next_solution(PQuery *self, PyObject *args)
{
  if (!PyArg_ParseTuple(args, ":next_solution"))
	return NULL;
  
  return Py_BuildValue("i", PL_next_solution(self->handle));
}

static PyObject *
PQuery_cut_query(PQuery *self, PyObject *args)
{
  if (!PyArg_ParseTuple(args, "cut_query"))
	return NULL;
  
  PL_cut_query(self->handle);
  
  Py_INCREF(Py_None);
  return Py_None;
}

static PyObject *
PQuery_close_query(PQuery *self, PyObject *args)
{
  if (!PyArg_ParseTuple(args, ":close_query"))
	return NULL;
  
  PL_close_query(self->handle);
  
  Py_INCREF(Py_None);
  return Py_None;
}

static PyMethodDef PQuery_methods[] = {
  {"next_solution",	(PyCFunction)PQuery_next_solution,	METH_VARARGS},
  {"cut_query",	(PyCFunction)PQuery_cut_query,	METH_VARARGS},
  {"close_query",	(PyCFunction)PQuery_close_query,	METH_VARARGS},
  {NULL, NULL}
};

static void
PQuery_dealloc(PQuery *self)
{
	PyObject_Del(self);
}

static PyObject *
PQuery_getattr(PQuery *self, char *name)
{
  if (strcmp(name, "handle")==0) {
	return Py_BuildValue("i", self->handle);
  } else
	return Py_FindMethod(PQuery_methods, (PyObject *)self, name);
}

static PyObject *
PQuery_repr(PQuery *self) 
{
  char buf[100];
  
  sprintf(buf, "query:%ld", self->handle); 
  return Py_BuildValue("s", buf);
}

statichere PyTypeObject PQuery_Type = {
	/* The ob_type field must be initialized in the module init function
	 * to be portable to Windows without using C++. */
	PyObject_HEAD_INIT(NULL)
	0,			/*ob_size*/
	"PQuery",			/*tp_name*/
	sizeof(PQuery),	/*tp_basicsize*/
	0,			/*tp_itemsize*/
	/* methods */
	(destructor)PQuery_dealloc, /*tp_dealloc*/
	0,			/*tp_print*/
	(getattrfunc)PQuery_getattr, /*tp_getattr*/
	(setattrfunc)0, /*tp_setattr*/
	0,			/*tp_compare*/
	(reprfunc)PQuery_repr,			/*tp_repr*/
	0,			/*tp_as_number*/
	0,			/*tp_as_sequence*/
	0,			/*tp_as_mapping*/
	0,			/*tp_hash*/
};

/* *********************************************************** */
/* **                  PForiegn Type                        ** */
/* *********************************************************** */


/* *********************************************************** */
/* **                  PRecord Type                         ** */
/* *********************************************************** */


/* *********************************************************** */
/* **            swipl Module Definitions                   ** */
/* *********************************************************** */


static PyObject *
swipl_new_atom(PyObject *self, PyObject *args)
{
	PAtom *rv;
	char *name;
	int leng;
	  
	if (!PyArg_ParseTuple(args, "s#:new_atom", &name, &leng))
		return NULL;

	rv = newPAtom(name, leng);
	if (rv == NULL)
	    return NULL;

	return (PyObject *)rv;
}

static PyObject *
swipl_new_functor(PyObject *self, PyObject *args)
{
	PFunctor *rv;
	PyObject *name;
	int arity;
	
	if (!PyArg_ParseTuple(args, "Oi:new_functor", &name, &arity))
		return NULL;
	
	if (!PAtom_Check(name)) {
	   PyErr_BadArgument();
	   return NULL;
	}
	
	rv = newPFunctor( ((PAtom *)name)->handle, arity);
	if (rv == NULL)
	    return NULL;
	
	return (PyObject *)rv;
}

static PyObject *
swipl_new_term(PyObject *self, PyObject *args)
{
	PTerm *rv;
	
	if (!PyArg_ParseTuple(args, ":new_term"))
		return NULL;
	
	rv = newPTerm();
	
	if (rv == NULL)
	    return NULL;
	
	return (PyObject *)rv;
}

static PyObject *
swipl_reset_term_refs(PyObject *self, PyObject *args)
{
	PyObject *after;
	
	if (!PyArg_ParseTuple(args, "O:reset_term_refs", &after))
		return NULL;
	
	PL_reset_term_refs( ((PTerm *)after)->handle);
	
	/* TBD: what about dangling PTerm objects? */
	
	Py_INCREF(Py_None);
	return Py_None;
}

static PyObject *
swipl_new_term_array(PyObject *self, PyObject *args)
{
	PTermArray *rv;
	int quantity = 0;
	
	if (!PyArg_ParseTuple(args, "|i:new_term_array", &quantity))
		return NULL;
	
	if (!quantity)
	  quantity = 1;
	
	rv = newPTermArray(quantity);
	if (rv == NULL)
	    return NULL;
	
	return (PyObject *)rv;
}

static PyObject *
swipl_new_predicate(PyObject *self, PyObject *args)
{
	PPredicate *rv;
	char *name, *module;
	int arity;
	
	if (!PyArg_ParseTuple(args, "sis:new_predicate", &name, &arity, &module))
		return NULL;
	
	rv = newPPredicate(name, arity, module);
	if (rv == NULL)
	    return NULL;
	
	return (PyObject *)rv;
}

static PyObject *
swipl_new_module(PyObject *self, PyObject *args)
{
  PyObject *module_name;
  PModule *rv;
  
  if (!PyArg_ParseTuple(args, "O:new_module", &module_name))
	return NULL;
	
  if (!PAtom_Check(module_name)) {
	PyErr_BadArgument();
	return NULL;
  }
  
  rv = newPModule( ((PAtom *)module_name)->handle);
  if (rv == NULL)
	return NULL;
  
  return (PyObject *)rv;
}

static PyObject *
swipl_open_query(PyObject *self, PyObject *args)
{
  PQuery *rv;
  PyObject *predicate, *terms, *module;
  int flags;
  predicate_t p;
  term_t t;
  module_t ctx = NULL;
  
  if (!PyArg_ParseTuple(args, "OOi|O:new_module", &predicate, &terms, 
						&flags, &module))
	return NULL;
  
  if (!PPredicate_Check(predicate)) {
	PyErr_BadArgument();
	return NULL;
  }
  
  if (!PTermArray_Check(terms)) {
	PyErr_BadArgument();
	return NULL;
  }
  
  if (!module)
	ctx = PL_context();
  else {
	if (!PModule_Check(module)) {
	  PyErr_BadArgument();
	  return NULL;
	}
	ctx = ((PModule *)module)->handle;
  }
  
  p = ((PPredicate *)predicate)->handle;
  t = ((PTermArray *)terms)->handle;
  
  rv = openPQuery(ctx, flags, p, t);
  if (rv == NULL)
	return NULL;
  
  return (PyObject *)rv;
}

#if 0
/* f = cons_functor_v(new_atom('animal'), (new_atom('gnu'), new_integer(50))) */
static PyObject *
swipl_cons_functor_v(PyObject *self, PyObject *args)
{
	PTerm *rv;
	PyObject *functor; /* PTerm atom */
	PyObject *arg_tuple; /* tuple, len 1+, of PTerm items */
	functor_t f;
	int arity, i;
	atom_t functor_name;
	PyObject *temp;
	term_t term_vector;
	term_t new_term;
	
	if (!PyArg_ParseTuple(args, "OO:cons_functor_v", &functor, &arg_tuple))
		return NULL;
	
	if (! PTerm_Check(functor)) {
	   PyErr_BadArgument();
	   return NULL;
	}
	
	if (! PyTuple_Check(arg_tuple)) {
	   PyErr_BadArgument();
	   return NULL;
	}
	
	/* functor is the generic PTerm/term_t, which in this case
	   is an atom.  I need to convert the value to type atom_t, and name
	   functor_name. */
	if (!PL_get_atom( ((PTerm *)functor)->handle, &functor_name)) {
	  PyErr_SetString(PyExc_SystemError, "could not convert term to atom");
	  return NULL;
	}
	
	/* arity = number of items in arg_tuple, which is also the arity
	   of the compound term that is being constructed.*/
	arity = PyTuple_Size(arg_tuple);
	if (arity < 1) {
	  PyErr_SetString(PyExc_TypeError, "functor needs at least 1 argument");
	  return NULL;
	}
	
	/* assign arguments to term vector */
	term_vector = PL_new_term_refs(arity);
	for (i=0; i<arity; i++) {
	  temp = PyTuple_GetItem(arg_tuple, i);
	  if (temp == NULL)
		return NULL;
	  if (!PTerm_Check(temp)) {
		PyErr_SetString(PyExc_TypeError, "invalid functor argument");
		return NULL;
	  }
	  PL_put_term(term_vector+i, ((PTerm *)temp)->handle);
	}
	
	/* make new compound and wrap it */
	f = PL_new_functor(functor_name, arity);
	new_term = PL_new_term_ref();
	PL_cons_functor_v(new_term, f, term_vector);
	rv = newPTerm(1);
	if (rv == NULL)
	  return NULL;
	PL_put_term(rv->handle, new_term);
	return (PyObject *)rv;
}
#endif

static PyObject *
swipl_chars_to_term(PyObject *self, PyObject *args) 
{
	term_t new_term;
	PTerm *rv;
	char *source;
	
	if (!PyArg_ParseTuple(args, "s:chars_to_term", &source))
		return NULL;
	
	new_term = PL_new_term_ref();
	if (PL_chars_to_term(source, new_term)) {
	  /* term parsed ok. */
	  rv = newPTerm();
	  if (rv == NULL)
		return NULL;
	  PL_put_term(rv->handle, new_term);
	  return (PyObject *)rv;
	} else {
	  /* syntax error */
	  /* TBD: return new_term */
	  PyErr_SetString(ErrorObject, "prolog syntax error");
	  return NULL;
	}
}

static PyObject *
swipl_halt(PyObject *self, PyObject *args)
{
	int status;
	
	if (!PyArg_ParseTuple(args, "i:halt", &status))
		return NULL;
	
	PL_halt(status);
	
	Py_INCREF(Py_None);
	return Py_None;
}

static PyObject *
swipl_context(PyObject *self, PyObject *args)
{
  PModule *rv;
  
  if (!PyArg_ParseTuple(args, ":context"))
	return NULL;
  
  rv = PyObject_NEW(PModule, &PModule_Type);
  if (rv == NULL)
	return NULL;
  
  rv->handle = PL_context();
  
  return (PyObject *)rv;
}

/* populate a contiguous array of term refs */
static PyObject *
swipl_make_args(PyObject *self, PyObject *args) 
{
	PTermArray *rv;
	PyObject *arg_list, *obj;
	int len, i;
	
	if (!PyArg_ParseTuple(args, "O!:make_args", &PyList_Type, &arg_list))
		return NULL;
	
	len = PyList_Size(arg_list);
	if (len < 1) {
	  return NULL;
	}
	
	rv = PyObject_NEW(PTermArray, &PTermArray_Type);
	if (rv == NULL) {
	  PyErr_SetString(PyExc_TypeError, "arg list needs at least one item");
	  return NULL;
	}
	
	rv->handle = PL_new_term_refs(len);
	rv->quantity = len;
	
	for (i=0;i<len;i++) {
	  obj = PyList_GetItem(arg_list, i);
	  if (PTerm_Check(obj))
		PL_put_term(rv->handle+i, ((PTermArray *)obj)->handle);
	  else {
		PyErr_SetString(PyExc_TypeError, "arg list must contain only prolog terms");
		Py_DECREF(rv);
		return NULL;
	  }
	}
	
	return (PyObject *)rv;
}

/* List of functions defined in the module */
static PyMethodDef swipl_methods[] = {
	{"new_atom",		swipl_new_atom,		METH_VARARGS},
	{"new_functor",		swipl_new_functor,		METH_VARARGS},
	{"new_term",		swipl_new_term,		METH_VARARGS},
	{"reset_term_refs",		swipl_reset_term_refs,		METH_VARARGS},
	{"new_term_array",		swipl_new_term_array,		METH_VARARGS},
	{"new_predicate",		swipl_new_predicate,		METH_VARARGS},
	{"new_module",		swipl_new_module,		METH_VARARGS},
	{"open_query",		swipl_open_query,		METH_VARARGS},
/* 	{"cons_functor_v",		swipl_cons_functor_v,		METH_VARARGS}, */
	{"chars_to_term",		swipl_chars_to_term,		METH_VARARGS},
	{"halt",		swipl_halt,		METH_VARARGS},
	{"context",		swipl_context,		METH_VARARGS},
	{"make_args",		swipl_make_args,		METH_VARARGS},
	{NULL,		NULL}
};


DL_EXPORT(void)
initswipl(void)
{
	PyObject *m, *d;
	int ac = 3;
	char *av[10] = {"./python", "-g", "true", NULL};
	
	/* Initialize the type of the new type object here; doing it here
	 * is required for portability to Windows without requiring C++. */
	PAtom_Type.ob_type = &PyType_Type;
	PFunctor_Type.ob_type = &PyType_Type;
	PTerm_Type.ob_type = &PyType_Type;
	PTermArray_Type.ob_type = &PyType_Type;
	PPredicate_Type.ob_type = &PyType_Type;
	PQuery_Type.ob_type = &PyType_Type;
	PModule_Type.ob_type = &PyType_Type;
	
	if ( !PL_initialise(ac, av) )
	  PL_halt(1);
	
	/* Create the module and add the functions */
	m = Py_InitModule("swipl", swipl_methods);

	/* Add some symbolic constants to the module */
	d = PyModule_GetDict(m);
	ErrorObject = PyErr_NewException("swipl.error", NULL, NULL);
	PyDict_SetItemString(d, "error", ErrorObject);
	
	PyDict_SetItemString(d, "PLVERSION", PyInt_FromLong(PLVERSION));
	
	PyDict_SetItemString(d, "CVT_ATOM", PyInt_FromLong(CVT_ATOM));
	PyDict_SetItemString(d, "CVT_STRING", PyInt_FromLong(CVT_STRING));
	PyDict_SetItemString(d, "CVT_LIST", PyInt_FromLong(CVT_LIST));
	PyDict_SetItemString(d, "CVT_INTEGER", PyInt_FromLong(CVT_INTEGER));
	PyDict_SetItemString(d, "CVT_FLOAT", PyInt_FromLong(CVT_FLOAT));
	PyDict_SetItemString(d, "CVT_NUMBER", PyInt_FromLong(CVT_NUMBER));
	PyDict_SetItemString(d, "CVT_ATOMIC", PyInt_FromLong(CVT_ATOMIC));
	PyDict_SetItemString(d, "CVT_VARIABLE", PyInt_FromLong(CVT_VARIABLE));
	PyDict_SetItemString(d, "CVT_WRITE", PyInt_FromLong(CVT_WRITE));
	PyDict_SetItemString(d, "CVT_ALL", PyInt_FromLong(CVT_ALL));
	
	PyDict_SetItemString(d, "BUF_DISCARDABLE", PyInt_FromLong(BUF_DISCARDABLE));
	PyDict_SetItemString(d, "BUF_RING", PyInt_FromLong(BUF_RING));
	PyDict_SetItemString(d, "BUF_MALLOC", PyInt_FromLong(BUF_MALLOC));

	PyDict_SetItemString(d, "PL_VARIABLE", PyInt_FromLong(PL_VARIABLE));
	PyDict_SetItemString(d, "PL_ATOM", PyInt_FromLong(PL_ATOM));
	PyDict_SetItemString(d, "PL_INTEGER", PyInt_FromLong(PL_INTEGER));
	PyDict_SetItemString(d, "PL_FLOAT", PyInt_FromLong(PL_FLOAT));
	PyDict_SetItemString(d, "PL_TERM", PyInt_FromLong(PL_TERM));
	PyDict_SetItemString(d, "PL_STRING", PyInt_FromLong(PL_STRING));
	PyDict_SetItemString(d, "PL_FUNCTOR", PyInt_FromLong(PL_FUNCTOR));
	PyDict_SetItemString(d, "PL_LIST", PyInt_FromLong(PL_LIST));
	PyDict_SetItemString(d, "PL_POINTER", PyInt_FromLong(PL_POINTER));
	PyDict_SetItemString(d, "PL_CODE_LIST", PyInt_FromLong(PL_CODE_LIST));
	PyDict_SetItemString(d, "PL_CHAR_LIST", PyInt_FromLong(PL_CHAR_LIST));
	PyDict_SetItemString(d, "PL_BOOL", PyInt_FromLong(PL_BOOL));
	PyDict_SetItemString(d, "PL_FUNCTOR_CHARS", PyInt_FromLong(PL_FUNCTOR_CHARS));

	PyDict_SetItemString(d, "PL_FIRST_CALL", PyInt_FromLong(PL_FUNCTOR_CHARS));
	PyDict_SetItemString(d, "PL_CUTTED", PyInt_FromLong(PL_FUNCTOR_CHARS));
	PyDict_SetItemString(d, "PL_REDO", PyInt_FromLong(PL_FUNCTOR_CHARS));

	PyDict_SetItemString(d, "PL_Q_NORMAL", PyInt_FromLong(PL_Q_NORMAL));
	PyDict_SetItemString(d, "PL_Q_NODEBUG", PyInt_FromLong(PL_Q_NODEBUG));
	PyDict_SetItemString(d, "PL_Q_CATCH_EXCEPTION", 
						 PyInt_FromLong(PL_Q_CATCH_EXCEPTION));
	PyDict_SetItemString(d, "PL_Q_PASS_EXCEPTION", 
						 PyInt_FromLong(PL_Q_PASS_EXCEPTION));
}
