/**
 * PyNode.cpp --
 *
 *	This file contains the node 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 */

/** PyNode_New.
 * A helper function for object creation from C.
 */
PyObject* PyNode_New()
{
	PyNode* result = PyObject_NEW(PyNode, &PyNodeType);
	if (result)
		new(&result->node) e4_Node();		//Placement new
	return (PyObject*)result;
}

/** PyNode_FromNode.
 * A helper function for object creation from C.
 */
PyObject* PyNode_FromNode(e4_Node node)
{
	PyNode* result = PyObject_NEW(PyNode, &PyNodeType);
	if (result)
		new(&result->node) e4_Node(node);		//Placement new
	return (PyObject*)result;
}

/** PyNode_AsNode.
 *
 */
e4_Node& PyNode_AsNode(PyObject* self)
{
	return ((PyNode*)self)->node;
}

/** Methods */

/** PyNode_Detach.
 *
 */
static PyObject* PyNode_Detach(PyNode* self)
{
	if (!self->node.Detach()) {
		PyErr_SetString(e4pyExc_APIFailure, "Detach: Operation failed");
		return 0;
	}

	Py_INCREF(Py_None);
	return Py_None;
}

/** PyNode_VertexCount.
 *
 */
static PyObject* PyNode_VertexCount(PyNode* self, PyObject* args)
{
	PyObject *py_object = NULL;
	if (!PyArg_ParseTuple(args, "|O", &py_object))
		return 0;

	long result = 0;
	if (py_object == NULL)					//total
		result = self->node.VertexCount();
	else if (PyString_Check(py_object))		//with name
		result = self->node.VertexCountWithName(PyString_AsString(py_object));
	else if (PyInt_Check(py_object))		//with type
		result = self->node.VertexCountWithType(e4_VertexType(PyInt_AsLong(py_object)));
	else {
		PyErr_SetString(PyExc_TypeError, "VertexCount: Unknown count criteria.");
		return 0;
	}

	return PyInt_FromLong(result);
}

/** PyNode_VertexCountWithValue.
 *
 */
static PyObject* PyNode_VertexCountWithValue(PyNode* self, PyObject* args)
{
	PyObject *py_value;
	if (!PyArg_ParseTuple(args, "O", &py_value))
		return 0;

	e4_Value value = e4_Value_FromPyObject(py_value);
	if (value.vertexType == E4_VTUNKNOWN) {
		PyErr_SetString(e4pyExc_APIFailure, "VertexCountWithValue: unknown value");
		return 0;
	}

	int result = self->node.VertexCountWithValue(value);
	if (value.vertexType == E4_VTBINARY)
		PyMem_Free(value.u.b.bytes);

	return PyInt_FromLong(result);
}

/** PyNode_SetVertex.
 * handles rank, name, (name, nth)
 */
static PyObject* PyNode_SetVertex(PyNode* self, PyObject* args)
{
	PyObject *py_key;
	PyObject *py_value;
	if (!PyArg_ParseTuple(args, "OO", &py_key, &py_value))
		return 0;

	e4_Value value = e4_Value_FromPyObject(py_value);
	if (value.vertexType == E4_VTUNKNOWN) {
		PyErr_SetString(PyExc_TypeError, "SetVertex: Unknown value type.");
		return 0;
	}

	bool success = false;
	if (PyInt_Check(py_key)) {				//rank
		success = self->node.SetVertexByRank(PyInt_AsLong(py_key), value);
	} else if (PyString_Check(py_key)) {	//name
		success = self->node.SetVertex(PyString_AsString(py_key), value);
	} else {								//name & nth
		const char *name;
		int nth;
		if (!PyArg_ParseTuple(py_key, "si", &name, &nth))
			return 0;
		
		success = self->node.SetNthVertex(name, nth, value);
	}

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

	if (!success) {
		PyErr_SetString(e4pyExc_APIFailure, "Vertex not found");
		return 0;
	}

	Py_INCREF(Py_None);
	return Py_None;
}

/** PyNode_SetNode.
 * handles rank, name, (name, nth)
 */
static PyObject* PyNode_SetNode(PyNode* self, PyObject* args)
{
	PyObject *py_key;
	if (!PyArg_ParseTuple(args, "O", &py_key))
		return 0;

	e4_Node node;
	bool success = false;
	if (PyInt_Check(py_key)) {				//rank
		success = self->node.SetNodeByRank(PyInt_AsLong(py_key), node);
	} else if (PyString_Check(py_key)) {	//name
		success = self->node.SetNode(PyString_AsString(py_key), node);
	} else {								//name & nth
		const char *name;
		int nth;
		if (!PyArg_ParseTuple(py_key, "si", &name, &nth))
			return 0;
		
		success = self->node.SetNthNode(name, nth, node);
	} 

	if (!success) {
		PyErr_SetString(e4pyExc_APIFailure, "SetNode: vertex not found");
		return 0;
	}

	return PyNode_FromNode(node);
}

/** PyNode_AddVertex.
 * @todo add default parameters (E4_IOLAST, rank=0)
 */
static PyObject* PyNode_AddVertex(PyNode* self, PyObject* args)
{
	const char *name;
	int order;
	int rank;
	PyObject *py_value;
	if (!PyArg_ParseTuple(args, "siiO", &name, &order, &rank, &py_value))
		return 0;

	e4_Value value = e4_Value_FromPyObject(py_value);
	if (value.vertexType == E4_VTUNKNOWN) {
		PyErr_SetString(e4pyExc_APIFailure, "AddVertex: unknown value");
		return 0;
	}

	bool success = self->node.AddVertex(name, e4_InsertOrder(order), rank, value);

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

	if (!success) {
		PyErr_SetString(e4pyExc_APIFailure, "AddVertex: Operation failed");
		return 0;
	}

	return PyInt_FromLong(rank);
}

/** PyNode_AddVertexRef.
 * @todo add default parameters (E4_IOLAST, rank=0)
 */
static PyObject* PyNode_AddVertexRef(PyNode* self, PyObject* args)
{
	const char *name;
	int order;
	int rank;
	PyObject *py_value;
	if (!PyArg_ParseTuple(args, "siiO", &name, &order, &rank, &py_value))
		return 0;

	e4_Vertex vertex;
	e4_Value value = e4_Value_FromPyObject(py_value);
	if (value.vertexType == E4_VTUNKNOWN) {
		PyErr_SetString(e4pyExc_APIFailure, "AddVertexRef: Unknown value type");
		return 0;
	}

	bool success = self->node.AddVertexRef(name, e4_InsertOrder(order), rank, value, vertex); 
	
	if (value.vertexType == E4_VTBINARY)
		PyMem_Free(value.u.b.bytes);

	if (!success) {
		PyErr_SetString(e4pyExc_APIFailure, "AddVertexRef: Operation failed");
		return 0;
	}
	
	return Py_BuildValue("(Oi)", PyVertex_FromVertex(vertex), rank);
}

/** PyNode_AddNode.
 *
 */
static PyObject* PyNode_AddNode(PyNode* self, PyObject* args)
{
	const char *name;
	int order = E4_IOLAST;
	int rank = 0;
	if (!PyArg_ParseTuple(args, "s|ii", &name, &order, &rank))
		return 0;

	e4_Node node;
	if (!self->node.AddNode(name, e4_InsertOrder(order), rank, node)) {
		PyErr_SetString(e4pyExc_APIFailure, "AddNode: Operation failed");
		return 0;
	}
	return Py_BuildValue("(Oi)", PyNode_FromNode(node), rank);
}

/** PyNode_MoveVertex.
 *
 */
static PyObject* PyNode_MoveVertex(PyNode* self, PyObject* args)
{
	PyVertex *py_vertex;
	int order;
	int rank;
	if (!PyArg_ParseTuple(args, "O!ii", &PyVertexType, &py_vertex, &order, &rank))
		return 0;

	if (!self->node.MoveVertex(py_vertex->vertex, e4_InsertOrder(order), rank)) {
		PyErr_SetString(e4pyExc_APIFailure, "MoveVertex: Operation failed");
		return 0;
	}
	Py_INCREF(Py_None);
	return Py_None;
}

/** PyNode_GetVertex.
 * handles rank, name, (name, nth)
 */
static PyObject* PyNode_GetVertex(PyNode* self, PyObject* args)
{
	PyObject *py_key;
	if (!PyArg_ParseTuple(args, "O", &py_key))
		return 0;

	e4_Value value;
	bool success = false;
	if (PyInt_Check(py_key)) {				//rank
		success = self->node.GetVertexByRank(PyInt_AsLong(py_key), value);
	} else if (PyString_Check(py_key)) {	//name
		success = self->node.GetVertex(PyString_AsString(py_key), value);
	} else {								//name & nth
		const char *name;
		int nth;
		if (!PyArg_ParseTuple(py_key, "si", &name, &nth))
			return 0;
		
		success = self->node.GetNthVertex(name, nth, value);
	} 

	if (!success) {
		PyErr_SetString(e4pyExc_APIFailure, "GetVertex: Vertex not found");
		return 0;
	}

	return PyObject_Frome4_Value(value);
}

/** PyNode_DetachVertex.
 * handles rank, name, (name, nth) AND node
 */
static PyObject* PyNode_DetachVertex(PyNode* self, PyObject* args)
{
	PyObject *py_key;
	if (!PyArg_ParseTuple(args, "O", &py_key))
		return 0;

	bool success = false;
	if (PyInt_Check(py_key)) {				//rank
		success = self->node.DetachVertexByRank(PyInt_AsLong(py_key));
	} else if (PyString_Check(py_key)) {	//name
		success = self->node.DetachVertex(PyString_AsString(py_key));
	} else if (PyNode_Check(py_key)) {		//node
		success = self->node.DetachFirstVertexWithNode(PyNode_AsNode(py_key));
	} else {								//name & nth
		const char *name;
		int nth;
		if (!PyArg_ParseTuple(py_key, "si", &name, &nth))
			return 0;
		
		success = self->node.DetachVertex(name, nth);
	} 

	if (!success) {
		PyErr_SetString(e4pyExc_APIFailure, "DetachVertex: vertex not found");
		return 0;
	}

	Py_INCREF(Py_None);
	return Py_None;
}

/** PyNode_GetVertexRef.
 * handles rank, name, (name, nth)
 */
static PyObject* PyNode_GetVertexRef(PyNode* self, PyObject* args)
{
	PyObject *py_key;
	if (!PyArg_ParseTuple(args, "O", &py_key))
		return 0;

	e4_Vertex vertex;
	bool success = false;
	if (PyInt_Check(py_key)) {				//rank
		success = self->node.GetVertexRefByRank(PyInt_AsLong(py_key), vertex);
	} else if (PyString_Check(py_key)) {	//name
		success = self->node.GetVertexRef(PyString_AsString(py_key), vertex);
	} else {								//name & nth
		const char *name;
		int nth;
		if (!PyArg_ParseTuple(py_key, "si", &name, &nth))
			return 0;
		
		success = self->node.GetVertexRef(name, nth, vertex);
	} 

	if (!success) {
		PyErr_SetString(e4pyExc_APIFailure, "GetVertexRef: vertex not found");
		return 0;
	}
	
	return PyVertex_FromVertex(vertex);
}

/** PyNode_VertexType.
 * deals with (name, nth), name, rank
 */
static PyObject* PyNode_VertexType(PyNode* self, PyObject* args)
{
	PyObject *key;
	if (!PyArg_ParseTuple(args, "O", &key))
		return 0;

	if (PyInt_Check(key)) {				//rank
		return PyInt_FromLong(self->node.VertexTypeByRank(PyInt_AsLong(key)));
	} else if (PyString_Check(key)) {	//name
		return PyInt_FromLong(self->node.VertexType(PyString_AsString(key)));
	} else {							//name & nth
		const char *name;
		int nth;
		if (!PyArg_ParseTuple(key, "si", &name, &nth))
			return 0;
		
		return PyInt_FromLong(self->node.VertexType(name, nth));
	}
	
	return 0;
}

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

	return PyString_FromString(self->node.VertexName(rank));
}

/** PyNode_RenameVertex.
 *
 */
static PyObject* PyNode_RenameVertex(PyNode* self, PyObject* args)
{
	int rank;
	const char *name;
	if (!PyArg_ParseTuple(args, "is", &rank, &name))
		return 0;

	if (!self->node.RenameVertex(rank, name)) {
		PyErr_SetString(e4pyExc_APIFailure, "RenameVertex: Operation failed");
		return 0;
	}

	Py_INCREF(Py_None);
	return Py_None;
}

/** PyNode_VertexRank.
 * deals with optional nth argument
 */
static PyObject* PyNode_VertexRank(PyNode* self, PyObject* args)
{
	const char *name;
	int nth = 1;
	if (!PyArg_ParseTuple(args, "s|i", &name, &nth))
		return 0;

	return PyInt_FromLong(long(self->node.VertexRank(name, nth)));
}

/** PyNode_Exists.
 * deals with optional nth argument
 */
static PyObject* PyNode_Exists(PyNode* self, PyObject* args)
{
	const char *name;
	int nth = 1;
	if (!PyArg_ParseTuple(args, "s|i", &name, &nth))
		return 0;

	return PyInt_FromLong(long(self->node.Exists(name, nth)));
}

/** PyNode_GetParent.
 * deals with optional nth argument
 */
static PyObject* PyNode_GetParent(PyNode* self, PyObject* args)
{
	int nth = 1;
	if (!PyArg_ParseTuple(args, "|i", &nth))
		return 0;

	e4_Node node;
	if (!self->node.GetParent(nth, node)) {
		PyErr_SetString(e4pyExc_APIFailure, "GetParent: Operation failed");
		return 0;
	}

	return PyNode_FromNode(node);
}

/** PyNode_ParentCount.
 *
 */
static PyObject* PyNode_ParentCount(PyNode* self)
{
	return PyInt_FromLong(long(self->node.ParentCount()));
}

/** PyNode_OccurrenceCount.
 * deals with optional node argument
 */
static PyObject* PyNode_OccurrenceCount(PyNode* self, PyObject* args)
{
	PyNode *py_node = NULL;
	if (!PyArg_ParseTuple(args, "|O!", &PyNodeType, &py_node))
		return 0;

	return py_node ?
		PyInt_FromLong(long(self->node.OccurrenceCount(py_node->node))) :
		PyInt_FromLong(long(self->node.OccurrenceCount()));
}

/** PyNode_ParentRank.
 *
 */
static PyObject* PyNode_ParentRank(PyNode* self, PyObject* args)
{
	PyNode *py_node = NULL;
	if (!PyArg_ParseTuple(args, "O!", &PyNodeType, &py_node))
		return 0;

	return PyInt_FromLong(long(self->node.ParentRank(py_node->node)));
}

/** PyNode_GetVertexUserData.
 * deals with optional nth argument or rank
 * key is one of: 
 * . rank
 * . name
 * . (name, nth)
 */
static PyObject* PyNode_GetVertexUserData(PyNode* self, PyObject* args)
{
	PyObject *key;
	if (!PyArg_ParseTuple(args, "O", &key))
		return 0;

	int user_data;
	bool success = false;
	if (PyInt_Check(key)) {				//rank
		success = self->node.GetVertexUserDataByRank(PyInt_AsLong(key), user_data);
	} else if (PyString_Check(key)) {	//name
		success = self->node.GetVertexUserData(PyString_AsString(key), user_data);
	} else {							//name & nth
		const char *name;
		int nth;
		if (!PyArg_ParseTuple(key, "si", &name, &nth))
			return 0;
		
		success = self->node.GetVertexUserData(name, nth, user_data);
	}
	
	if (!success) {
		PyErr_SetString(e4pyExc_APIFailure, "GetVertexUserData: getting vertex user-data");
		return 0;
	}

	return PyInt_FromLong(user_data);
}

/** PyNode_SetVertexUserData.
 *
 */
static PyObject* PyNode_SetVertexUserData(PyNode* self, PyObject* args)
{
	PyObject *key;
	int user_data;
	if (!PyArg_ParseTuple(args, "Oi", &key, &user_data))
		return 0;

	bool success = false;
	if (PyInt_Check(key)) {				//rank
		success = self->node.SetVertexUserDataByRank(PyInt_AsLong(key), user_data);
	} else if (PyString_Check(key)) {	//name
		success = self->node.SetVertexUserData(PyString_AsString(key), user_data);
	} else {							//name & nth
		const char *name;
		int nth;
		if (!PyArg_ParseTuple(key, "si", &name, &nth))
			return 0;
		
		success = self->node.SetVertexUserData(name, nth, user_data);
	}
	
	if (!success) {
		PyErr_SetString(e4pyExc_APIFailure, "GetVertexUserData: setting vertex user-data");
		return 0;
	}

	Py_INCREF(Py_None);
	return Py_None;
}

/** PyNode_GetVertexRefFromParent.
 *
 */
static PyObject* PyNode_GetVertexRefFromParent(PyNode* self, PyObject* args)
{
	PyObject *pyo;
	int nth;
	if (!PyArg_ParseTuple(args, "Oi", &pyo, &nth))
		return 0;

	if (!PyInt_Check(pyo) && !PyNode_Check(pyo)) {
		PyErr_SetString(PyExc_TypeError, "First argument must be an integer or a node.");
		return 0;
	}

	e4_Vertex vertex;
	bool success = PyInt_Check(pyo) ?
		self->node.GetVertexRefFromParent(PyInt_AsLong(pyo), nth, vertex) :
		self->node.GetVertexRefFromParent(PyNode_AsNode(pyo), nth, vertex);

	if (!success) {
		PyErr_SetString(e4pyExc_APIFailure, "GetVertexRefFromParent: Operation failed");
		return 0;
	}

	return PyVertex_FromVertex(vertex);
}

/** PyNode_GetRankInParent.
 *
 */
static PyObject* PyNode_GetRankInParent(PyNode* self, PyObject* args)
{
	PyObject *pyo = NULL;
	int nth = -1;
	if (!PyArg_ParseTuple(args, "|Oi", &pyo, &nth))
		return 0;

	if (pyo != NULL && !PyInt_Check(pyo) && !PyNode_Check(pyo)) {
		PyErr_SetString(PyExc_TypeError, "First argument must be an integer or a node.");
		return 0;
	}

	int result;
	if (pyo) {
		if (PyNode_Check(pyo)) {
			if (nth == -1)
				result = self->node.GetRankInParent(PyNode_AsNode(pyo), 1);
			else
				result = self->node.GetRankInParent(PyNode_AsNode(pyo), nth);
		} else {	//pyo is an int
			if (nth == -1)
				result = self->node.GetRankInParent(PyInt_AsLong(pyo));
			else
				result = self->node.GetRankInParent(PyInt_AsLong(pyo), nth);
		}
	} else
		result = self->node.GetRankInParent();

	return PyInt_FromLong(result);
}

/** PyNode_GetNameInParent.
 *
 */
static PyObject* PyNode_GetNameInParent(PyNode* self, PyObject* args)
{
	PyObject *pyo = NULL;
	int nth = -1;
	if (!PyArg_ParseTuple(args, "|Oi", &pyo, &nth))
		return 0;

	if (pyo != NULL && !PyInt_Check(pyo) && !PyNode_Check(pyo)) {
		PyErr_SetString(PyExc_TypeError, "First argument must be an integer or a node.");
		return 0;
	}

	const char *result;
	if (pyo) {
		if (PyNode_Check(pyo)) {
			if (nth == -1)
				result = self->node.GetNameInParent(PyNode_AsNode(pyo), 1);
			else
				result = self->node.GetNameInParent(PyNode_AsNode(pyo), nth);
		} else {	//pyo is an int
			if (nth == -1)
				result = self->node.GetNameInParent(PyInt_AsLong(pyo));
			else
				result = self->node.GetNameInParent(PyInt_AsLong(pyo), nth);
		}
	} else
		result = self->node.GetNameInParent();

	return PyString_FromString(result);
}


/** Method doc */

static char doc_Detach[] = "\
Detach()\n\
Detaches this node by detaching all vertices that contain this node as their value.";

static char doc_VertexCount[] = "\
VertexCount([name, type]) -> int\n\
Returns the number of vertices in this node that have the optional name or type.";

static char doc_VertexCountWithValue[] = "\
VertexCountWithValue(value) -> int \n\
Returns the number of vertices in this node that have the value.";

static char doc_SetNode[] = "\
SetNode(name[, nth]) -> node\n\
Sets the nth vertex, in rank order, name name to a new node.\n\
Returns the new node.";

static char doc_SetVertex[] = "\
SetVertex(name[, nth], value)\n\
SetVertex(rank, value)\n\
Sets the vertex with name/(name,nth)/rank to value.";

static char doc_AddVertex[] = "\
AddVertex(name, insert-order, rank, value) -> rank\n\
Adds a vertex with name according to the supplied insert-order and rank.\n\
Returns the vertex rank.";

static char doc_AddVertexRef[] = "\
AddVertexRef(name, insert-order, rank, value) -> (vertex, rank)\n\
Returns a tuple with vertex and actuall rank.";

static char doc_AddNode[] = "\
AddNode(name, insert-order, rank) -> (node, rank)\n\
Adds a new vertex with returned node as it's value.";

static char doc_MoveVertex[] = "\
MoveVertex(vertex, insert-order, rank)\n\
Move the given vertex from this or another node and insert it in this node at the position indicated by insert-order and rank.";

static char doc_GetVertex[] = "\
GetVertex(name[, nth]) -> value\n\
GetVertex(rank) -> value\n\
Returns a vertex value.";

static char doc_DetachVertex[] = "\
DetachVertex(name[, nth]))\n\
DetachVertex(rank)\n\
Detaches the vertex in this node.";

static char doc_GetVertexRef[] = "\
GetVertexRef(name[, nth]) -> vertex\n\
GetVertexRef(rank) -> vertex\n\
Returns the vertex instance.";

static char doc_VertexType[] = "\
VertexType(name[, nth]) -> type(int)\n\
VertexType(rank) -> type\n\
Returns the vertex type.";

static char doc_VertexName[] = "\
VertexName(rank) -> name\n\
Returns the vertex name.";

static char doc_RenameVertex[] = "\
RenameVertex(rank, new-name)\n\
Renames the vertex with rank to the new-name.";

static char doc_VertexRank[] = "\
VertexRank(name[, nth]) -> rank\n\
Returns the vertex rank.";

static char doc_Exists[] = "\
Exists(name[, nth]) -> boolean\n\
Returns true if the vertex exists.";

static char doc_GetParent[] = "\
GetParent([nth]) -> parent-node\n\
Returns the nth parent of this node.";

static char doc_ParentCount[] = "\
ParentCount() -> count\n\
Returns the number of parents of this node.";

static char doc_OccurrenceCount[] = "\
OccurrenceCount([parent-node]) -> count\n\
Returns the number of vertices (under node parent if specified) that contain this node as their value.";

static char doc_ParentRank[] = "\
ParentRank(parent-node) -> rank\n\
Returns the rank of the parent-node in the parents of this node.";

static char doc_GetRankInParent[] = "\
GetRankInParent([nth[, ith]]) -> rank\n\
Returns the rank of the ith vertex in the nth parent node whose value is this node.\n\
GetRankInParent(parent-node, ith) -> rank\n\
Returns the rank of the ith vertex in the parent-node of this node, whose value is this node.";

static char doc_GetNameInParent[] = "\
GetNameInParent([nth[, ith]]) -> rank\n\
Returns the name of the ith vertex in the nth parent node whose value is this node.\n\
GetNameInParent(parent-node, ith) -> rank\n\
Returns the name of the ith vertex in the parent-node of this node, whose value is this node.";

static char doc_GetVertexUserData[] = "\
GetVertexUserData(name[, nth]) -> data\n\
GetVertexUserData(rank) -> data\n\
Returns vertex user-data.";

static char doc_SetVertexUserData[] = "\
SetVertexUserData(name[, nth], data)\n\
SetVertexUserData(rank, data)\n\
Sets vertex user-data.";

///@todo purpose not clear.
static char doc_GetVertexRefFromParent[] = "\
GetVertexRefFromParent(node, nth) -> vertex\n\
Returns nth vertex?";

/** Method list */
static PyMethodDef PyNodeMethods[] = {
	{"Detach", (PyCFunction)PyNode_Detach, METH_NOARGS, doc_Detach},
	{"VertexCount", (PyCFunction)PyNode_VertexCount, METH_VARARGS, doc_VertexCount},
	{"VertexCountWithValue", (PyCFunction)PyNode_VertexCountWithValue, METH_VARARGS, doc_VertexCountWithValue},
	{"SetNode", (PyCFunction)PyNode_SetNode, METH_VARARGS, doc_SetNode},
	{"SetVertex", (PyCFunction)PyNode_SetVertex, METH_VARARGS, doc_SetVertex},
	{"AddVertex", (PyCFunction)PyNode_AddVertex, METH_VARARGS, doc_AddVertex},
	{"AddVertexRef", (PyCFunction)PyNode_AddVertexRef, METH_VARARGS, doc_AddVertexRef},
	{"AddNode", (PyCFunction)PyNode_AddNode, METH_VARARGS, doc_AddNode},
	{"MoveVertex", (PyCFunction)PyNode_MoveVertex, METH_VARARGS, doc_MoveVertex},
	{"GetVertex", (PyCFunction)PyNode_GetVertex, METH_VARARGS, doc_GetVertex},
	{"DetachVertex", (PyCFunction)PyNode_DetachVertex, METH_VARARGS, doc_DetachVertex},
	{"GetVertexRef", (PyCFunction)PyNode_GetVertexRef, METH_VARARGS, doc_GetVertexRef},
	{"VertexType", (PyCFunction)PyNode_VertexType, METH_VARARGS, doc_VertexType},
	{"VertexName", (PyCFunction)PyNode_VertexName, METH_VARARGS, doc_VertexName},
	{"RenameVertex", (PyCFunction)PyNode_RenameVertex, METH_VARARGS, doc_RenameVertex},
	{"VertexRank", (PyCFunction)PyNode_VertexRank, METH_VARARGS, doc_VertexRank},
	{"Exists", (PyCFunction)PyNode_Exists, METH_VARARGS, doc_Exists},
	{"GetParent", (PyCFunction)PyNode_GetParent, METH_VARARGS, doc_GetParent},
	{"ParentCount", (PyCFunction)PyNode_ParentCount, METH_NOARGS, doc_ParentCount},
	{"OccurrenceCount", (PyCFunction)PyNode_OccurrenceCount, METH_VARARGS, doc_OccurrenceCount},
	{"ParentRank", (PyCFunction)PyNode_ParentRank, METH_VARARGS, doc_ParentRank},
	{"GetRankInParent", (PyCFunction)PyNode_GetRankInParent, METH_VARARGS, doc_GetRankInParent},
	{"GetNameInParent", (PyCFunction)PyNode_GetNameInParent, METH_VARARGS, doc_GetNameInParent},
	{"GetVertexUserData", (PyCFunction)PyNode_GetVertexUserData, METH_VARARGS, doc_GetVertexUserData},
	{"SetVertexUserData", (PyCFunction)PyNode_SetVertexUserData, METH_VARARGS, doc_SetVertexUserData},
	{"GetVertexRefFromParent", (PyCFunction)PyNode_GetVertexRefFromParent, METH_VARARGS, doc_GetVertexRefFromParent},
	{0, 0, 0, 0}
};


/** Attributes */

/** PyNode_get_isRoot.
 *
 */
static PyObject* PyNode_get_isRoot(PyNode* self, void *)
{
	return PyInt_FromLong(self->node.IsRoot());
}

/** PyNode_get_isDetached.
 *
 */
static PyObject* PyNode_get_isDetached(PyNode* self, void *)
{
	return PyInt_FromLong(self->node.IsDetached());
}

/** PyNode_get_storage.
 *
 */
static PyObject* PyNode_get_storage(PyNode* self, void *)
{
	e4_Storage storage;
	if (!self->node.GetStorage(storage)) {
		PyErr_SetString(PyExc_TypeError, "storage: Cannot get storage");
		return 0;
	}
	return PyStorage_FromStorage(storage);
}

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

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

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

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

/** PyNode_get_kind.
 *
 */
static PyObject* PyNode_get_kind(PyNode* self, void *)
{
	return PyInt_FromLong(self->node.Kind());
}

/** PyNode_get_valid.
 *
 */
static PyObject* PyNode_get_valid(PyNode* self, void *)
{
	return PyInt_FromLong(self->node.IsValid());
}

/** PyNode_get_tempUID.
 *
 */
static PyObject* PyNode_get_tempUID(PyNode* self, void *)
{
	return PyInt_FromLong(self->node.GetTemporaryUID());
}

/** PyNode_get_transientUserData.
 *
 */
static PyObject* PyNode_get_transientUserData(PyNode* self, void *)
{
	PyObject* result = (PyObject*)self->node.GetTransientUserData();
	if (result == NULL)
		PyErr_SetString(e4pyExc_APIFailure, "transientUserData: Failed getting transient data");
	else
		Py_INCREF(result);

	return result;
}

/** PyNode_set_transientUserData.
 *
 */
static int PyNode_set_transientUserData(PyNode* self, PyObject* value, void *)
{
	Py_INCREF(value);
	self->node.SetTransientUserData(value);
	return 0;
}

/** Attribute doc */

static char doc_isRoot[] = "\
Returns true if this node is the current root node of its containing storage.";

static char doc_isDetached[] = "\
Returns true if this node is detached.\n\
A node is detached if it is not the value of any vertices contained within a node.";

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

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

static char doc_uid[] = "\
Returns the node unique-id";

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

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

static char doc_valid[] = "\
Returns true if this refers to a valid node.\n\
The result is true if the storage containing this node has not been closed and the program variable on which the method is invoked has not been assigned invalidNode.";

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

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

/** Attribute list */
static PyGetSetDef PyNodeGetsets[] = {
    {"isRoot", (getter)PyNode_get_isRoot, (setter)NULL, doc_isRoot},
    {"isDetached", (getter)PyNode_get_isDetached, (setter)NULL, doc_isDetached},
    {"storage", (getter)PyNode_get_storage, (setter)NULL, doc_storage},
    {"root", (getter)PyNode_get_root, (setter)NULL, doc_root},
    {"uid", (getter)PyNode_get_uid, (setter)NULL, doc_uid},
    {"userData", (getter)PyNode_get_userData, (setter)PyNode_set_userData, doc_userData},
    {"kind", (getter)PyNode_get_kind, (setter)NULL, doc_kind},
    {"valid", (getter)PyNode_get_valid, (setter)NULL, doc_valid},
    {"tempUID", (getter)PyNode_get_tempUID, (setter)NULL, doc_tempUID},
    {"transientUserData", (getter)PyNode_get_transientUserData, (setter)PyNode_set_transientUserData, doc_transientUserData},
    {NULL}
};


/** Mapping */

/** PyNode_length.
 * -1 upon error
 */
static int PyNode_length(PyNode *self)
{
	return self->node.VertexCount();
}

/** PyNode_getitem.
 * Same as GetVertex
 */
static PyObject * PyNode_getitem(PyNode *self, PyObject *key)
{
	e4_Value value;
	bool success = false;

	if (PyInt_Check(key)) {				//by rank
		success = self->node.GetVertexByRank(PyInt_AsLong(key), value);
	} else if (PyString_Check(key)) {	//by name
		success = self->node.GetVertex(PyString_AsString(key), value);
	} else {							//by name and index
		const char *name;
		int nth = 1;
		if (PyArg_ParseTuple(key, "s|i", &name, &nth))
			success = self->node.GetNthVertex(name, nth, value);
	}
	if (!success) {
		PyErr_SetString(e4pyExc_APIFailure, "getitem: vertex not found");
		return NULL;
	}

	return PyObject_Frome4_Value(value);
}

/** PyNode_setitem.
 *
 */
static int PyNode_setitem(PyNode *self, PyObject *key, PyObject *py_value)
{
	e4_Value value = e4_Value_FromPyObject(py_value);

	if (py_value != NULL && value.vertexType == E4_VTUNKNOWN) {
		PyErr_SetString(e4pyExc_APIFailure, "setitem: unknown value");
		return -1;
	}

//extract key & SetVertex
	bool success = false;
	if (PyInt_Check(key)) {				//by rank
		success = (py_value == NULL) ?
			self->node.DetachVertexByRank(PyInt_AsLong(key)) :
			self->node.SetVertexByRank(PyInt_AsLong(key), value);
	} else if (PyString_Check(key)) {	//by name
		success = (py_value == NULL) ?
			self->node.DetachVertex(PyString_AsString(key)) :
			self->node.SetVertex(PyString_AsString(key), value);
		if (!success && py_value != NULL)	{ //set failed, try adding a vertex
			int rank = 0;
			success = self->node.AddVertex(PyString_AsString(key), E4_IOLAST, rank, value);
		}
	} else {							//by name and index
		const char *name;
		int nth = 1;
		if (PyArg_ParseTuple(key, "s|i", &name, &nth))
			success = (py_value == NULL) ?
				self->node.DetachVertex(name, nth) :
				self->node.SetNthVertex(name, nth, value);
	}

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

	if (!success) {
		if (py_value == NULL) 
			PyErr_SetString(e4pyExc_APIFailure, "setitem: Detach vertex failed");
		else
			PyErr_SetString(e4pyExc_APIFailure, "setitem: Set vertex failed");
		return -1;
	}

	return 0;
}

static PyMappingMethods PyNodeMapping = {
    (inquiry)PyNode_length,      /*mp_length*/
    (binaryfunc)PyNode_getitem,  /*mp_subscript*/
    (objobjargproc)PyNode_setitem, /*mp_ass_subscript*/
};


/** Node access */

/** PyNode_new.
 * Is this required?
 */
static PyObject* PyNode_new(PyObject* o, PyObject* args)
{
	PyNode* result = NULL;
	switch (PyTuple_Size(args)) {
	case 0:
		result = (PyNode*)PyNode_New();
		break;
	case 1: 
		PyNode *py_node;
		if (!PyArg_ParseTuple(args, "O!", &PyNodeType, &py_node))
			return 0;
		result = (PyNode*)PyNode_FromNode(py_node->node);
		break;
	default:
		PyErr_SetString(PyExc_TypeError, ErrInvalidArgs);
		break;
	}
	return (PyObject*)result;
}

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

	self->node.~e4_Node();		//"placement" dtor
	PyObject_DEL(self);
}

/** PyNode_compare.
 *
 */
static int PyNode_compare(PyNode *self, PyObject *rhs)
{
	if (!PyNode_Check(rhs))
		return -1;
	return self->node == PyNode_AsNode(rhs) ? 0 : 1;
}

/** PyNode_iter.
 * Enumerate vertices under node
 */
static PyObject *PyNode_iter(PyNode *self)
{
	e4_VertexVisitor visitor(self->node);
	return PyVertexVisitor_FromVertexVisitor(visitor);
}


/** Type doc */
static char doc_PyNode[] = "\
Node class provides the abstraction of a persistent node in a graph.";

/** Node type */
PyTypeObject PyNodeType = {
	PyObject_HEAD_INIT(&PyType_Type)
	0,
	"Node",
	sizeof(PyNode),		/* tp_basicsize */
	0,					/* tp_itemsize */
	(destructor)PyNode_dealloc,	/* tp_dealloc */
	0,//(printfunc)PyNode_print, /* tp_print */
	0,					/* tp_getattr */
	0,					/* tp_setattr */
	(cmpfunc)PyNode_compare,	/* tp_compare */
	(reprfunc)0,		/* tp_repr */
	0,					/* tp_as_number */
	0,					/* tp_as_sequence */
	&PyNodeMapping,		/* 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_PyNode,			/* tp_doc */
    0,					/* tp_traverse */
    0,					/* tp_clear */
    0,					/* tp_richcompare */
    0,					/* tp_weaklistoffset */
    (getiterfunc)PyNode_iter,	/* tp_iter */
    0,					/* tp_iternext */
    PyNodeMethods,		/* tp_methods */
    0,					/* tp_members */
    PyNodeGetsets,		/* tp_getset */
};
