/**
 * PyVertex.cpp --
 *
 *	This file contains the vertex defintion.
 *
 *	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 <new>

/** Helpers */

/** PyVertex_New.
 * A helper function for object creation from C.
 */
PyObject* PyVertex_New()
{
	PyVertex* result = PyObject_NEW(PyVertex, &PyVertexType);
	if (result)
		new(&result->vertex) e4_Vertex();		//Placement new
	return (PyObject*)result;
}

/** PyVertex_FromVertex.
 * A helper function for object creation from C.
 */
PyObject* PyVertex_FromVertex(e4_Vertex vertex)
{
	PyVertex* result = PyObject_NEW(PyVertex, &PyVertexType);
	if (result)
		new(&result->vertex) e4_Vertex(vertex);		//Placement new
	return (PyObject*)result;
}

/** PyVertex_AsVertex.
 *
 */
e4_Vertex& PyVertex_AsVertex(PyObject* self)
{
	return ((PyVertex*)self)->vertex;
}

/** Methods */

/** PyVertex_Detach.
 *
 */
static PyObject* PyVertex_Detach(PyVertex* self)
{
	if (!self->vertex.Detach()) {
		PyErr_SetString(e4pyExc_APIFailure, "Detach: Cannot detach");
		return 0;
	}
	Py_INCREF(Py_None);
	return Py_None;
}

/** PyVertex_SetNode.
 *
 */
static PyObject* PyVertex_SetNode(PyVertex* self, PyObject* args)
{
	e4_Node node;
	if (!self->vertex.SetNode(node)) {
		PyErr_SetString(e4pyExc_APIFailure, "SetNode: Invalid vertex");
		return 0;
	}
	return PyNode_FromNode(node);
}

/** PyVertex_CountWithName.
 *
 */
static PyObject* PyVertex_CountWithName(PyVertex* self)
{
	return PyInt_FromLong(long(self->vertex.CountWithName()));
}

/** PyVertex_TotalCountWithName.
 *
 */
static PyObject* PyVertex_TotalCountWithName(PyVertex* self)
{
	return PyInt_FromLong(long(self->vertex.TotalCountWithName()));
}

/** PyVertex_CountWithType.
 *
 */
static PyObject* PyVertex_CountWithType(PyVertex* self)
{
	return PyInt_FromLong(long(self->vertex.CountWithType()));
}

/** PyVertex_TotalCountWithType.
 *
 */
static PyObject* PyVertex_TotalCountWithType(PyVertex* self)
{
	return PyInt_FromLong(long(self->vertex.TotalCountWithType()));
}

/** PyVertex_GetNode.
 *
 */
static PyObject* PyVertex_GetNode(PyVertex* self)
{
	e4_Node node;
	if (!self->vertex.GetNode(node)) {
		PyErr_SetString(e4pyExc_APIFailure, "GetNode: failed");
		return 0;
	}
	return PyNode_FromNode(node);
}

/** PyVertex_MoveVertex.
 * bool MoveVertex(const e4_Vertex &ff, e4_InsertOrder order, int offset)
 */
static PyObject* PyVertex_MoveVertex(PyVertex* self, PyObject* args)
{
	PyVertex *py_vertex;
	int order, offset;
	if (!PyArg_ParseTuple(args, "O!ii", &PyVertexType, &py_vertex, &order, &offset))
		return 0;

	if (!self->vertex.MoveVertex(py_vertex->vertex, e4_InsertOrder(order), offset)) {
		PyErr_SetString(e4pyExc_APIFailure, "MoveVertex: Cannot move vertex");
		return 0;
	}
	Py_INCREF(Py_None);
	return Py_None;
}

/** PyVertex_Next.
 *
 */
static PyObject* PyVertex_Next(PyVertex* self, PyObject* args)
{
	int num;
	if (!PyArg_ParseTuple(args, "i", &num))
		return 0;
	e4_Vertex vertex;
	if (!self->vertex.Next(num, vertex)) {
		PyErr_SetString(e4pyExc_APIFailure, "Next: No next vertex");
		return 0;
	}
	return PyVertex_FromVertex(vertex);
}

/** PyVertex_Prev.
 *
 */
static PyObject* PyVertex_Prev(PyVertex* self, PyObject* args)
{
	int num;
	if (!PyArg_ParseTuple(args, "i", &num))
		return 0;
	e4_Vertex vertex;
	if (!self->vertex.Prev(num, vertex)) {
		PyErr_SetString(e4pyExc_APIFailure, "Prev: No previous vertex");
		return 0;
	}
	return PyVertex_FromVertex(vertex);
}

/** Method doc */
static char doc_Detach[] = "\
Detach()\n\
Detaches the vertex from the node in which it appears.";

static char doc_SetNode[] = "\
SetNode() -> node\n\
Sets the value of this vertex to a new node and returns it.";

static char doc_CountWithName[] = "\
CountWithName() -> int\n\
Returns the number of vertices up to and including this one in the node containing this vertex that have the same name as this vertex.";

static char doc_TotalCountWithName[] = "\
TotalCountWithName() -> name\n\
Returns the total number of vertices in the node containing this one that have the same name as this vertex.";

static char doc_CountWithType[] = "\
CountWithType() -> int\n\
Returns the number of vertices up to and including this one in the node containing this vertex that have the same type as this vertex.";

static char doc_TotalCountWithType[] = "\
TotalCountWithType() -> int\n\
Returns the total number of vertices in the node containing this one that have the same type as this vertex.";

static char doc_GetNode[] = "\
GetNode() -> node\n\
Returns the node that represents the originating node of this vertex.";

static char doc_MoveVertex[] = "\
MoveVertex(vertex, order, offset)\n\
Inserts the vertex into the node containing this vertex at a rank relative to the rank of this vertex as indicated by order and offset.";

static char doc_Next[] = "\
Next(nth) -> vertex\n\
Returns the nth vertex after this one in the node containing this vertex.";

static char doc_Prev[] = "\
Prev(nth) -> vertex\n\
Returns the nth vertex before this one in the node containing this vertex.";

/** Method list */
static PyMethodDef PyVertexMethods[] = {
	{"Detach", (PyCFunction)PyVertex_Detach, METH_NOARGS, doc_Detach},
	{"SetNode", (PyCFunction)PyVertex_SetNode, METH_NOARGS, doc_SetNode},
	{"CountWithName", (PyCFunction)PyVertex_CountWithName, METH_NOARGS, doc_CountWithName},
	{"TotalCountWithName", (PyCFunction)PyVertex_TotalCountWithName, METH_NOARGS, doc_TotalCountWithName},
	{"CountWithType", (PyCFunction)PyVertex_CountWithType, METH_NOARGS, doc_CountWithType},
	{"TotalCountWithType", (PyCFunction)PyVertex_TotalCountWithType, METH_NOARGS, doc_TotalCountWithType},
	{"GetNode", (PyCFunction)PyVertex_GetNode, METH_NOARGS, doc_GetNode},
	{"MoveVertex", (PyCFunction)PyVertex_MoveVertex, METH_VARARGS, doc_MoveVertex},
	{"Next", (PyCFunction)PyVertex_Next, METH_VARARGS, doc_Next},
	{"Prev", (PyCFunction)PyVertex_Prev, METH_VARARGS, doc_Prev},
	{0, 0, 0, 0}
};


/** Attributes */
/** PyVertex_get_isDetached.
 *
 */
static PyObject* PyVertex_get_isDetached(PyVertex* self, void *)
{
	return PyInt_FromLong(self->vertex.IsDetached());
}

/** PyVertex_get_value.
 *
 */
static PyObject* PyVertex_get_value(PyVertex* self, void *)
{
	e4_Value value;
	if (!self->vertex.Get(value)) {
		PyErr_SetString(e4pyExc_APIFailure, "value: Failed getting vertex value");
		return 0;
	}

	return PyObject_Frome4_Value(value);
}

/** PyVertex_set_value.
 *
 */
static int PyVertex_set_value(PyVertex* self, PyObject* py_value, void *)
{
	e4_Value value = e4_Value_FromPyObject(py_value);
	if (value.vertexType == E4_VTUNKNOWN) {
		PyErr_SetString(PyExc_TypeError, ErrInvalidArgs);
		return -1;
	}

	bool success = self->vertex.Set(value);

	if (value.vertexType == E4_VTBINARY)
		PyMem_Free(value.u.b.bytes);

	if (!success) {
		PyErr_SetString(e4pyExc_APIFailure, "value: Failed setting vertex value");
		return -1;
	}
	
	return 0;
}

/** PyVertex_get_name.
 *
 */
static PyObject* PyVertex_get_name(PyVertex* self, void *)
{
	return PyString_FromString(self->vertex.Name());
}

/** PyVertex_set_name.
 *
 */
static int PyVertex_set_name(PyVertex* self, PyObject* value, void *)
{
	if (!PyString_Check(value)) {
		PyErr_SetString(PyExc_TypeError, ErrInvalidArgs);
		return -1;
	}
	if (!self->vertex.Rename(PyString_AsString(value))) {
		PyErr_SetString(e4pyExc_APIFailure, "name: Failed renaming vertex");
		return -1;
	}
	return 0;
}

/** PyVertex_get_storage.
 *
 */
static PyObject* PyVertex_get_storage(PyVertex* self, void *)
{
	e4_Storage storage;
	if (!self->vertex.GetStorage(storage)) {
		PyErr_SetString(e4pyExc_APIFailure, "storage: Failed getting storage");
		return 0;
	}
	return PyStorage_FromStorage(storage);
}

/** PyVertex_get_root.
 *
 */
static PyObject* PyVertex_get_root(PyVertex* self, void *)
{
	e4_Node node;
	if (!self->vertex.GetRootNode(node)) {
		PyErr_SetString(PyExc_ValueError, "root: Root node not set.");
		return 0;
	}
	return PyNode_FromNode(node);
}

/** PyVertex_get_uid.
 *
 */
static PyObject* PyVertex_get_uid(PyVertex* self, void *)
{
	e4_VertexUniqueID uid;
	if (!self->vertex.GetUniqueID(uid)) {
		PyErr_SetString(e4pyExc_APIFailure, "uid: Failed getting unique-id for vertex");
		return 0;
	}
	return Py_BuildValue("(ii)", uid.GetID(), uid.GetSP());
}

/** PyVertex_get_rank.
 *
 */
static PyObject* PyVertex_get_rank(PyVertex* self, void *)
{
	return PyInt_FromLong(self->vertex.Rank());
}

/** PyVertex_get_userData.
 *
 */
static PyObject* PyVertex_get_userData(PyVertex* self, void *)
{
	int result;
	if (!self->vertex.GetUserData(result)) {
		PyErr_SetString(e4pyExc_APIFailure, "userData: Failed getting vertex user-data");
		return 0;
	}
	return PyInt_FromLong(result);
}

/** PyVertex_set_userData.
 *
 */
static int PyVertex_set_userData(PyVertex* self, PyObject* value, void *)
{
	if (!PyInt_Check(value)) {
		PyErr_SetString(PyExc_TypeError, ErrInvalidArgs);
		return -1;
	}
	if (!self->vertex.SetUserData(PyInt_AsLong(value))) {
		PyErr_SetString(e4pyExc_APIFailure, "userData: Failed setting vertex user-data");
		return -1;
	}
	return 0;
}

/** PyVertex_get_kind.
 *
 */
static PyObject* PyVertex_get_kind(PyVertex* self, void *)
{
	return PyInt_FromLong(self->vertex.Kind());
}

/** PyVertex_get_type.
 *
 */
static PyObject* PyVertex_get_type(PyVertex* self, void *)
{
	return PyInt_FromLong(self->vertex.Type());
}

/** PyVertex_get_valid.
 *
 */
static PyObject* PyVertex_get_valid(PyVertex* self, void *)
{
	return PyInt_FromLong(self->vertex.IsValid());
}

/** PyVertex_get_tempUID.
 *
 */
static PyObject* PyVertex_get_tempUID(PyVertex* self, void *)
{
	return PyInt_FromLong(self->vertex.GetTemporaryUID());
}

/** PyVertex_get_transientUserData.
 *
 */
static PyObject* PyVertex_get_transientUserData(PyVertex* self, void *)
{
	PyObject* result = (PyObject*)self->vertex.GetTransientUserData();
	if (result == NULL)
		PyErr_SetString(e4pyExc_APIFailure, "transientUserData: data not set");
	else
		Py_INCREF(result);

	return result;
}

/** PyVertex_set_transientUserData.
 *
 */
static int PyVertex_set_transientUserData(PyVertex* self, PyObject* value, void *)
{
	Py_INCREF(value);
	self->vertex.SetTransientUserData(value);
	return 0;
}

/** Attribute doc */
static char doc_isDetached[] = "\
Returns true if this vertex is detached.";

static char doc_value[] = "\
Get/Set the value associated with the vertex.";

static char doc_name[] = "\
Get/Set the name of the vertex within its containing node.";

static char doc_storage[] = "\
Returns  the storage containing this vertex.";

static char doc_root[] = "\
Returns the root node for this vertex's storage.";

static char doc_uid[] = "\
Returns vertex unique-id.";

static char doc_rank[] = "\
Returns the rank of this vertex within its containing node.";

static char doc_userData[] = "\
Get/Set the user data value associated with this vertex.";

static char doc_type[] = "\
Returns the type of the value stored in this vertex.";

static char doc_kind[] = "\
Returns E4_RKVERTEX.";

static char doc_valid[] = "\
Returns true if the vertex is valid, false otherwise. A vertex is valid if it is contained within a valid, reachable node.";

static char doc_tempUID[] = "\
Returns temporary unique id for vertex.";

static char doc_transientUserData[] = "\
Get/Set transient user data for vertex.";

/** Attribute list */
static PyGetSetDef PyVertexGetsets[] = {
    {"isDetached", (getter)PyVertex_get_isDetached, (setter)NULL, doc_isDetached},
    {"value", (getter)PyVertex_get_value, (setter)PyVertex_set_value, doc_value},
    {"name", (getter)PyVertex_get_name, (setter)PyVertex_set_name, doc_name},
    {"storage", (getter)PyVertex_get_storage, (setter)NULL, doc_storage},
    {"root", (getter)PyVertex_get_root, (setter)NULL, doc_root},
    {"uid", (getter)PyVertex_get_uid, (setter)NULL, doc_uid},
    {"rank", (getter)PyVertex_get_rank, (setter)NULL, doc_rank},
    {"userData", (getter)PyVertex_get_userData, (setter)PyVertex_set_userData, doc_userData},
    {"type", (getter)PyVertex_get_type, (setter)NULL, doc_type},
    {"kind", (getter)PyVertex_get_kind, (setter)NULL, doc_kind},
    {"valid", (getter)PyVertex_get_valid, (setter)NULL, doc_valid},
    {"tempUID", (getter)PyVertex_get_tempUID, (setter)NULL, doc_tempUID},
    {"transientUserData", (getter)PyVertex_get_transientUserData, (setter)PyVertex_set_transientUserData, doc_transientUserData},
    {NULL}
};


/** Vertex access */
/** PyVertex_new.
 * Is this required?
 */
static PyObject* PyVertex_new(PyObject* o, PyObject* args)
{
	PyObject* result = NULL;
	switch (PyTuple_Size(args)) {
	case 0:
		result = PyVertex_New();
		break;
	case 1:
		PyVertex *py_vertex;
		if (!PyArg_ParseTuple(args, "O!", &PyVertexType, &py_vertex))
			return 0;
		result = PyVertex_FromVertex(py_vertex->vertex);
		break;
	default:
		PyErr_SetString(PyExc_TypeError, ErrInvalidArgs);
		break;
	}
	return result;
}

/** PyVertex_dealloc.
 *
 */
static void PyVertex_dealloc(PyVertex *self)
{
//remove any previous transient data
	PyObject* td = (PyObject*)self->vertex.GetTransientUserData();
	if (td != NULL)
		Py_DECREF(td);

	self->vertex.~e4_Vertex();		//"placement" dtor
	PyObject_DEL(self);
}

/** PyVertex_compare.
 *
 */
static int PyVertex_compare(PyVertex *self, PyObject *rhs)
{
	if (!PyVertex_Check(rhs))
		return -1;
	return self->vertex == PyVertex_AsVertex(rhs) ? 0 : 1;
}

/** Type doc */
static char doc_PyVertex[] = "\
Vertex class provides the abstraction of a vertex originating from a node.";

/** Vertex type */
PyTypeObject PyVertexType = {
	PyObject_HEAD_INIT(&PyType_Type)
	0,
	"Vertex",
	sizeof(PyVertex),		/* tp_basicsize */
	0,					/* tp_itemsize */
	(destructor)PyVertex_dealloc,	/* tp_dealloc */
	0,//(printfunc)PyVertex_print, /* tp_print */
	0,					/* tp_getattr */
	0,					/* tp_setattr */
	(cmpfunc)PyVertex_compare,	/* tp_compare */
	(reprfunc)0,		/* tp_repr */
	0,					/* tp_as_number */
	0,					/* tp_as_sequence */
	0,					/* tp_as_mapping */
    0,					/* tp_hash */
    0,					/* tp_call */
    0,					/* tp_str */
    PyObject_GenericGetAttr,	/* tp_getattro */
    PyObject_GenericSetAttr,	/* tp_setattro */
    0,					/* tp_as_buffer */
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
    doc_PyVertex,		/* tp_doc */
    0,					/* tp_traverse */
    0,					/* tp_clear */
    0,					/* tp_richcompare */
    0,					/* tp_weaklistoffset */
    0,					/* tp_iter */
    0,					/* tp_iternext */
    PyVertexMethods,		/* tp_methods */
    0,					/* tp_members */
    PyVertexGetsets,		/* tp_getset */
};
