/**
 * e4py.cpp --
 *
 *	This file contains global functions.
 *
 *	Authors: Mike Krimerman.
 *		 hemkond@yahoo.com
 *
 * Copyright (c) 2003, Mike Krimerman.
 * 
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 * 
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE, EVEN IF
 * MIKE KRIMERMAN IS MADE AWARE OF THE POSSIBILITY OF SUCH DAMAGE.
 */
#include "e4py.h"
#include "marshal.h"


static char doc_e4py[] = "\
This is the Python interface for the e4graph database library.\n\
Example of use:\n\
import e4py\n\
s = e4py.Storage('demo.dat')\n";



/** PyStorage_DefineEventCode.
 *
 */
static PyObject* PyStorage_DefineEventCode(PyObject* self, PyObject* args)
{
	int ec;
	if (!PyArg_ParseTuple(args, "i", &ec))
		return 0;

	if (!e4_Storage::DefineEventCode(ec)) {
		PyErr_SetString(e4pyExc_APIFailure, "DefineEventCode: Cannot define code");
		return 0;
	}

	return PyInt_FromLong(ec);
}

/** PyStorage_UndefineEventCode.
 *
 */
static PyObject* PyStorage_UndefineEventCode(PyObject* self, PyObject* args)
{
	int ec;
	if (!PyArg_ParseTuple(args, "i", &ec))
		return 0;

	if (!e4_Storage::UndefineEventCode(ec)) {
		PyErr_SetString(e4pyExc_APIFailure, "UndefineEventCode: Cannot undefine code");
		return 0;
	}

	Py_INCREF(Py_None);
	return Py_None;
}

/** PyStorage_IsEventCodeDefined.
 *
 */
static PyObject* PyStorage_IsEventCodeDefined(PyObject* self, PyObject* args)
{
	int ec;
	if (!PyArg_ParseTuple(args, "i", &ec))
		return 0;

	return PyInt_FromLong(long(e4_Storage::IsEventCodeDefined(ec)));
}

/** Method list */

/* doc */
static char doc_Storage[] = "\
Storage(name[, kind[, mode]]) -> Storage\n\
Creates and returns a new storage.\n\
name (string) - file name for storage\n\
kind (string) - driver name, use E4_METAKIT constant for Metakit\n\
mode (int) - Commit, Garbage-collection and allocation policy\n\
  E4_COMMITATCLOSE: If set, the storage is committed before it is closed (default on).\n\
  E4_AUTOCOMMIT: If set, the storage is committed periodically when changes exist in the storage (default off).\n\
  E4_OPENGC: If set, a garbage collection is performed when the storage is opened (default on).\n\
  E4_GCBEFORECOMMIT: If set, a garbage collection is performed before every commit (default off).\n\
  E4_AUTOGC: If set, a garbage collection is performed whenever an entity (node or vertex) becomes unreachable (default on).\n\
  E4_BIGPREALLOC: If set, space is preallocated in the storage in big increment (default off).\n\
  E4_COMPACTATCLOSE: If turned on, space is compacted when the storage is closed (default off).\n\
  E4_DEFAULTSTATE: Sets the defaults.\n";

static char doc_StorageVisitor[] = "\
StorageVisitor() -> StorageVisitor\n\
Returns a new storage visitor\n";

static PyMethodDef e4pyMethods[] = {
	{"Storage", PyStorage_new, METH_VARARGS, doc_Storage},
	{"StorageVisitor", PyStorageVisitor_new, METH_VARARGS, doc_StorageVisitor},
	{"NodeVisitor", PyNodeVisitor_new, METH_VARARGS, "NodeVisitor(...) - creates a node visitor"},
	{"VertexVisitor", PyVertexVisitor_new, METH_VARARGS, "VertexVisitor(...) - creates a vertex visitor"},
	{"DefineEventCode", PyStorage_DefineEventCode, METH_VARARGS, "DefineEventCode -- "},
	{"UndefineEventCode", PyStorage_UndefineEventCode, METH_VARARGS, "UndefineEventCode -- "},
	{"IsEventCodeDefined", PyStorage_IsEventCodeDefined, METH_VARARGS, "IsEventCodeDefined -- "},
	{0, 0, 0, 0}
};

//Function pointers for pickling when using the cPickle module
//static PyObject *dumps = NULL;
//static PyObject *loads = NULL;

/** PickleTo.
 *
 */
PyObject *PickleTo(PyObject *value)
{
	return PyMarshal_WriteObjectToString(value);
//	return PyObject_CallFunction(dumps, "Oi", value, 1);
}

/** PickleFrom.
 *
 */
PyObject *PickleFrom(const char* buffer, int count)
{
	return PyMarshal_ReadObjectFromString((char*)buffer, count);
//	return count == 0 ?
//		PyObject_CallFunction(loads, "s", buffer) :
//		PyObject_CallFunction(loads, "s#", buffer, count);
}

/** e4Value_FromPyObject.
 * @note If data is returned as E4_VTBINARY, it should be freed by PyMem_Free
 */
e4_Value e4_Value_FromPyObject(PyObject *py_value)
{
	e4_Value value;
	value.vertexType = E4_VTUNKNOWN;

	if (py_value == NULL) {
		return value;
	} if (PyInt_CheckExact(py_value)) {
		value.u.i = PyInt_AsLong(py_value);
		value.vertexType = E4_VTINT;
	} else if (PyFloat_CheckExact(py_value)) {
		value.u.d = PyFloat_AsDouble(py_value);
		value.vertexType = E4_VTDOUBLE;
	} else if (PyString_CheckExact(py_value)) {
		value.u.s = PyString_AsString(py_value);
		value.vertexType = E4_VTSTRING;
	} else if (PyNode_Check(py_value)) {
		value.n = PyNode_AsNode(py_value);
		value.vertexType = E4_VTNODE;
	} else {	//binary
		PyObject *data = PickleTo(py_value);
		if (data && PyString_Check(data)) {
			value.u.b.nbytes = PyString_Size(data);
			value.u.b.bytes = PyMem_Malloc(value.u.b.nbytes);
			memcpy(value.u.b.bytes, PyString_AsString(data), value.u.b.nbytes);
			value.vertexType = E4_VTBINARY;
		}
		if (data)
			Py_DECREF(data);
	}

	return value;
}

/** PyObject_Frome4_Value.
 *
 */
PyObject *PyObject_Frome4_Value(e4_Value value)
{
	PyObject *py_value = NULL;
	switch (value.vertexType) {
    case E4_VTNODE:
		py_value = PyNode_FromNode(value.n);		
		break;
    case E4_VTINT:
		py_value = PyInt_FromLong(value.u.i);		
		break;
    case E4_VTDOUBLE:
		py_value = PyFloat_FromDouble(value.u.d);		
		break;
    case E4_VTSTRING:
		py_value = PyString_FromString(value.u.s);
		break;
    case E4_VTBINARY:
		py_value = PickleFrom((const char*)value.u.b.bytes, value.u.b.nbytes);		
		break;
	default:
		PyErr_SetString(e4pyExc_APIFailure, "Unknown value type");
		break;
	}
	return py_value;

}

//extern "C" __declspec(dllexport) void
DL_EXPORT(void)
inite4py(void)
{
	PyObject* m = Py_InitModule3("e4py", e4pyMethods, doc_e4py);

///@todo: these attrbiutes aren't read-only!
	PyModule_AddStringConstant(m, "version", (char*)e4_Storage::version());

//Affective as of version 1.08a
	PyModule_AddIntConstant(m, "major_version", e4_Storage::major_version());
	PyModule_AddIntConstant(m, "minor_version", e4_Storage::minor_version());
	PyModule_AddIntConstant(m, "release_status", e4_Storage::release_status());
	PyModule_AddIntConstant(m, "release_iteration", e4_Storage::release_iteration());

//register an exception for reporting False results from e4graph methods
	if (e4pyExc_APIFailure = PyErr_NewException("e4py.APIFailure", PyExc_RuntimeError, NULL))
		PyObject_SetAttrString(m, "error", e4pyExc_APIFailure);

//cPickle module used to store binaries
/*no need for this, use python marshler instead
	PyObject *pickle_module = PyImport_ImportModule("cPickle");
	PyModule_AddObject(m, "pickle", pickle_module);
	PyObject *pickle_dict = PyModule_GetDict(pickle_module);
	dumps = PyDict_GetItemString(pickle_dict, "dumps");
	loads = PyDict_GetItemString(pickle_dict, "loads");
*/

//register class-types
	PyObject_SetAttrString(m, "StorageType", (PyObject*)&PyStorageType);
	PyObject_SetAttrString(m, "NodeType", (PyObject*)&PyNodeType);
	PyObject_SetAttrString(m, "VertexType", (PyObject*)&PyVertexType);
	PyObject_SetAttrString(m, "StorageVisitorType", (PyObject*)&PyStorageVisitorType);
	PyObject_SetAttrString(m, "NodeVisitorType", (PyObject*)&PyNodeVisitorType);
	PyObject_SetAttrString(m, "VertexVisitorType", (PyObject*)&PyVertexVisitorType);

//register all enums and #defines
	PyModule_AddStringConstant(m, "E4_METAKIT", E4_METAKIT);

//search result
	PyModule_AddIntConstant(m, "E4_VERTEXNOTFOUND", E4_VERTEXNOTFOUND);
	PyModule_AddIntConstant(m, "E4_VERTEXNOTCREATED", E4_VERTEXNOTCREATED);
	PyModule_AddIntConstant(m, "E4_NODENOTFOUND", E4_NODENOTFOUND);
	PyModule_AddIntConstant(m, "E4_RANKNOTUSED", E4_RANKNOTUSED);
	PyModule_AddIntConstant(m, "E4_INVALIDUNIQUEID", E4_INVALIDUNIQUEID);

//storage states
	PyModule_AddIntConstant(m, "E4_COMMITATCLOSE", E4_COMMITATCLOSE);
	PyModule_AddIntConstant(m, "E4_AUTOCOMMIT", E4_AUTOCOMMIT);
	PyModule_AddIntConstant(m, "E4_OPENGC", E4_OPENGC);
	PyModule_AddIntConstant(m, "E4_GCBEFORECOMMIT", E4_GCBEFORECOMMIT);
	PyModule_AddIntConstant(m, "E4_AUTOGC", E4_AUTOGC);
	PyModule_AddIntConstant(m, "E4_BIGPREALLOC", E4_BIGPREALLOC);
	PyModule_AddIntConstant(m, "E4_COMPACTATCLOSE", E4_COMPACTATCLOSE);
	PyModule_AddIntConstant(m, "E4_DEFAULTSTATE", E4_DEFAULTSTATE);

//e4_RefKind
	PyModule_AddIntConstant(m, "E4_RKINVALID", E4_RKINVALID);
	PyModule_AddIntConstant(m, "E4_RKSTORAGE", E4_RKSTORAGE);
	PyModule_AddIntConstant(m, "E4_RKNODE", E4_RKNODE);
	PyModule_AddIntConstant(m, "E4_RKVERTEX", E4_RKVERTEX);

//e4_VertexType
	PyModule_AddIntConstant(m, "E4_VTUNKNOWN", E4_VTUNKNOWN);
	PyModule_AddIntConstant(m, "E4_VTNODE", E4_VTNODE);
	PyModule_AddIntConstant(m, "E4_VTINT", E4_VTINT);
	PyModule_AddIntConstant(m, "E4_VTDOUBLE", E4_VTDOUBLE);
	PyModule_AddIntConstant(m, "E4_VTSTRING", E4_VTSTRING);
	PyModule_AddIntConstant(m, "E4_VTBINARY", E4_VTBINARY);
//E4_VTLASTVERTEXTYPE

//e4_InsertOrder
	PyModule_AddIntConstant(m, "E4_IONONE", E4_IONONE);
	PyModule_AddIntConstant(m, "E4_IOAT", E4_IOAT);
	PyModule_AddIntConstant(m, "E4_IOFIRST", E4_IOFIRST);
	PyModule_AddIntConstant(m, "E4_IOLAST", E4_IOLAST);
	PyModule_AddIntConstant(m, "E4_IOBEFORE", E4_IOBEFORE);
	PyModule_AddIntConstant(m, "E4_IOAFTER", E4_IOAFTER);
//E4_IOLASTINSERTORDER

//e4_VisitMethod
	PyModule_AddIntConstant(m, "E4_VMUNKNOWN", E4_VMUNKNOWN);
	PyModule_AddIntConstant(m, "E4_VMSTORAGE", E4_VMSTORAGE);
	PyModule_AddIntConstant(m, "E4_VMNODE", E4_VMNODE);
	PyModule_AddIntConstant(m, "E4_VMNODERANDOM", E4_VMNODERANDOM);
//E4_VMLASTMETHOD

//e4_DetachChoice
	PyModule_AddIntConstant(m, "E4_DCDETACHED", E4_DCDETACHED);
	PyModule_AddIntConstant(m, "E4_DCATTACHED", E4_DCATTACHED);
	PyModule_AddIntConstant(m, "E4_DCBOTH", E4_DCBOTH);

//visit
	PyModule_AddIntConstant(m, "E4_VFNONE", E4_VFNONE);
	PyModule_AddIntConstant(m, "E4_VFNAME", E4_VFNAME);
	PyModule_AddIntConstant(m, "E4_VFTYPE", E4_VFTYPE);

//e4_Space
	PyModule_AddIntConstant(m, "E4_SPNODE", E4_SPNODE);
	PyModule_AddIntConstant(m, "E4_SPVERTEX", E4_SPVERTEX);
	PyModule_AddIntConstant(m, "E4_SPNAME", E4_SPNAME);
	PyModule_AddIntConstant(m, "E4_SPSTRING", E4_SPSTRING);
	PyModule_AddIntConstant(m, "E4_SPINT", E4_SPINT);
	PyModule_AddIntConstant(m, "E4_SPDOUBLE", E4_SPDOUBLE);
	PyModule_AddIntConstant(m, "E4_SPBINARY", E4_SPBINARY);
//E4_SPLAST

//e4_SpaceStat
	PyModule_AddIntConstant(m, "E4_SSUSED", E4_SSUSED);
	PyModule_AddIntConstant(m, "E4_SSAVAIL", E4_SSAVAIL);
	PyModule_AddIntConstant(m, "E4_SSFREED", E4_SSFREED);
	PyModule_AddIntConstant(m, "E4_SSALLOC", E4_SSALLOC);
	PyModule_AddIntConstant(m, "E4_SSLAST", E4_SSLAST);

//event codes
	PyModule_AddIntConstant(m, "E4_ECADDNODE", E4_ECADDNODE);
	PyModule_AddIntConstant(m, "E4_ECDETNODE", E4_ECDETNODE);
	PyModule_AddIntConstant(m, "E4_ECATTNODE", E4_ECATTNODE);
	PyModule_AddIntConstant(m, "E4_ECMODNODE", E4_ECMODNODE);
	PyModule_AddIntConstant(m, "E4_ECADDVERTEX", E4_ECADDVERTEX);
	PyModule_AddIntConstant(m, "E4_ECDETVERTEX", E4_ECDETVERTEX);
	PyModule_AddIntConstant(m, "E4_ECATTVERTEX", E4_ECATTVERTEX);
	PyModule_AddIntConstant(m, "E4_ECMODVERTEX", E4_ECMODVERTEX);
	PyModule_AddIntConstant(m, "E4_ECCHANGESTG", E4_ECCHANGESTG);

//user event codes
	PyModule_AddIntConstant(m, "E4_FIRSTUSERDEFINEDEVENTCODE", E4_FIRSTUSERDEFINEDEVENTCODE);
	PyModule_AddIntConstant(m, "E4_LASTUSERDEFINEDEVENTCODE", E4_LASTUSERDEFINEDEVENTCODE);

//e4_ModNodeEventReason
	PyModule_AddIntConstant(m, "E4_ERMNADDVERTEX", E4_ERMNADDVERTEX);
	PyModule_AddIntConstant(m, "E4_ERMNDETVERTEX", E4_ERMNDETVERTEX);
	PyModule_AddIntConstant(m, "E4_ERMNRENVERTEX", E4_ERMNRENVERTEX);
	PyModule_AddIntConstant(m, "E4_ERMNMOVVERTEX", E4_ERMNMOVVERTEX);

//e4_ModVertexEventReason
	PyModule_AddIntConstant(m, "E4_ERMVMODVALUE", E4_ERMVMODVALUE);
	PyModule_AddIntConstant(m, "E4_ERMVRENAME", E4_ERMVRENAME);
	PyModule_AddIntConstant(m, "E4_ERMVREPARENT", E4_ERMVREPARENT);
	PyModule_AddIntConstant(m, "E4_ERMVDETVERTEX", E4_ERMVDETVERTEX);
}
