// K-3D
// Copyright (c) 1995-2004, Timothy M. Shead
//
// Contact: tshead@k-3d.com
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This program 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
// General Public License for more details.
//
// You should have received a copy of the GNU General Public
// License along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

/** \file
		\brief Implements a scripting object model in Python
		\author Anders Dahnielson (anders@dahnielson.com)
		\author Romain Behar (romainbehar@yahoo.com)
*/

#include "object_model.h"

#include <k3dsdk/algebra.h>
#include <k3dsdk/classes.h>
#include <k3dsdk/color.h>
#include <k3dsdk/command_node.h>
#include <k3dsdk/file_filter.h>
#include <k3dsdk/iapplication.h>
#include <k3dsdk/iapplication_plugin_factory.h>
#include <k3dsdk/ibezier_channel.h>
#include <k3dsdk/icommand_tree.h>
#include <k3dsdk/idocument.h>
#include <k3dsdk/idocument_plugin_factory.h>
#include <k3dsdk/igeometry_read_format.h>
#include <k3dsdk/igeometry_write_format.h>
#include <k3dsdk/imesh_sink.h>
#include <k3dsdk/imesh_source.h>
#include <k3dsdk/iobject.h>
#include <k3dsdk/iproperty.h>
#include <k3dsdk/iproperty_collection.h>
#include <k3dsdk/iuser_interface.h>
#include <k3dsdk/iwritable_property.h>
#include <k3dsdk/objects.h>
#include <k3dsdk/plugins.h>
#include <k3dsdk/property.h>
#include <k3dsdk/scripting.h>
#include <k3dsdk/serialization.h>
#include <k3dsdk/state_change_set.h>
#include <k3dsdk/utility.h>
#include <k3dsdk/viewport.h>

#include <boost/filesystem/fstream.hpp>
#include <boost/filesystem/path.hpp>

#include <fstream>
#include <iostream>

namespace
{

PyObject* ConvertTuple3(const k3d::vector3& Source)
{
	// Converts 3d vector to tuple
	return Py_BuildValue("(ddd)", Source[0], Source[1], Source[2]);
}

PyObject* ConvertTuple4(const k3d::angle_axis& Source)
{
	// Convert 1+3d vector to tuple
	return Py_BuildValue("(dddd)", k3d::degrees(Source.angle), Source.axis[0], Source.axis[1], Source.axis[2]);
}

k3d::vector3 ConvertVector3(PyObject* Value)
{
	// Converts tuple to 3d vector
	k3d::vector3 result;

	if(PyTuple_Check(Value))
	{
		result[0] = PyFloat_AsDouble(PyTuple_GetItem(Value, 0));
		result[1] = PyFloat_AsDouble(PyTuple_GetItem(Value, 1));
		result[2] = PyFloat_AsDouble(PyTuple_GetItem(Value, 2));
	}

	return result;
}

k3d::angle_axis ConvertAngleAxis(PyObject* Value)
{
	// Convert tuple to 1+3d vector
	k3d::angle_axis result;

	if(PyTuple_Check(Value))
	{
		result.angle = PyFloat_AsDouble(PyTuple_GetItem(Value, 0));
		result.axis[0] = PyFloat_AsDouble(PyTuple_GetItem(Value, 1));
		result.axis[1] = PyFloat_AsDouble(PyTuple_GetItem(Value, 2));
		result.axis[2] = PyFloat_AsDouble(PyTuple_GetItem(Value, 3));
		result.angle = k3d::radians(result.angle);
	}

	return result;
}

/// converts a boost::any object to a Python value
PyObject* convert(const boost::any Value)
{
	// We put the likely types up front for efficiency ...
	if(Value.type() == typeid(bool))
		{
			if(boost::any_cast<bool>(Value))
				return PYVAL_TRUE;
			else
				return PYVAL_NONE;
		}

	if(Value.type() == typeid(double))
		return DoubleToPyVal(boost::any_cast<double>(Value));

	if(Value.type() == typeid(std::string))
		return StringToPyVal(boost::any_cast<std::string>(Value));

	if(Value.type() == typeid(k3d::vector2))
		{
			const k3d::vector2 value = boost::any_cast<k3d::vector2>(Value);

			return Py_BuildValue("(dd)", value[0], value[1]);
		}

	if(Value.type() == typeid(k3d::vector3))
		{
			const k3d::vector3 value = boost::any_cast<k3d::vector3>(Value);

			return Py_BuildValue("(ddd)", value[0], value[1], value[2]);
		}

	if(Value.type() == typeid(k3d::vector4))
		{
			const k3d::vector4 value = boost::any_cast<k3d::vector4>(Value);

			return Py_BuildValue("(dddd)", value[0], value[1], value[2], value[3]);
		}

	if(Value.type() == typeid(k3d::angle_axis))
		{
			const k3d::angle_axis value = boost::any_cast<k3d::angle_axis>(Value);

			return Py_BuildValue("(dddd)", value.angle, value.axis[0], value.axis[1], value.axis[2]);
		}

	if(Value.type() == typeid(k3d::euler_angles))
		{
			const k3d::euler_angles value = boost::any_cast<k3d::euler_angles>(Value);

			return Py_BuildValue("(dddi)", value[0], value[1], value[2], value.order);
		}

	// Less-likely stuff here ...
	if(Value.type() == typeid(unsigned long))
		return LongToPyVal(boost::any_cast<unsigned long>(Value));

	if(Value.type() == typeid(int))
		return IntToPyVal(boost::any_cast<int>(Value));

	if(Value.type() == typeid(long))
		return LongToPyVal(boost::any_cast<long>(Value));

	std::cerr << __PRETTY_FUNCTION__ << " : unrecognized type" << std::endl;
	return PYVAL_NONE;
}

/// converts a Python value to a boost::any object
boost::any convert(PyObject* Value, const std::type_info& TargetType)
{
	if(TargetType == typeid(bool))
		{
			return boost::any(PyObject_IsTrue(Value));
		}

	if(TargetType == typeid(double))
		{
			if(!PyFloat_Check(Value))
				return boost::any();

			return boost::any(PyFloat_AsDouble(Value));
		}

	if(TargetType == typeid(std::string))
		{
			if(!PyString_Check(Value))
				return boost::any();

			return boost::any(std::string(PyString_AsString(Value)));
		}

	if(TargetType == typeid(k3d::vector3))
		{
			if(!PyTuple_Check(Value))
				return boost::any();

			k3d::vector3 result;

			result[0] = PyFloat_AsDouble(PyTuple_GetItem(Value, 0));
			result[1] = PyFloat_AsDouble(PyTuple_GetItem(Value, 1));
			result[2] = PyFloat_AsDouble(PyTuple_GetItem(Value, 2));

			return boost::any(result);
		}

	if(TargetType == typeid(k3d::angle_axis))
		{
			if(!PyTuple_Check(Value))
				return boost::any();

			unsigned int length = PyTuple_Size(Value);
			if(3 == length)
				{
					k3d::euler_angles euler(0, 0, 0, k3d::euler_angles::ZXYstatic);

					euler.n[1] = PyFloat_AsDouble(PyTuple_GetItem(Value, 0));
					euler.n[2] = PyFloat_AsDouble(PyTuple_GetItem(Value, 1));
					euler.n[0] = PyFloat_AsDouble(PyTuple_GetItem(Value, 2));

					euler.n[0] = k3d::radians(euler.n[0]);
					euler.n[1] = k3d::radians(euler.n[1]);
					euler.n[2] = k3d::radians(euler.n[2]);

					return boost::any(k3d::angle_axis(euler));
				}
			else if(4 == length)
				{
					k3d::angle_axis result;

					result.angle = PyFloat_AsDouble(PyTuple_GetItem(Value, 0));
					result.axis[0] = PyFloat_AsDouble(PyTuple_GetItem(Value, 1));
					result.axis[1] = PyFloat_AsDouble(PyTuple_GetItem(Value, 2));
					result.axis[2] = PyFloat_AsDouble(PyTuple_GetItem(Value, 3));

					result.angle = k3d::radians(result.angle);

					return boost::any(result);
				}
		}

	if(TargetType == typeid(unsigned long))
		{
			if(!PyLong_Check(Value))
				return boost::any();

			return boost::any(PyLong_AsLong(Value));
		}

	if(TargetType == typeid(int))
		{
			if(!PyInt_Check(Value))
				return boost::any();

			return boost::any(PyInt_AsLong(Value));
		}

	if(TargetType == typeid(long))
		{
			if(!PyLong_Check(Value))
				return boost::any();

			return boost::any(PyLong_AsLong(Value));
		}

	std::cerr << __PRETTY_FUNCTION__ << " : unrecognized type" << std::endl;
	return boost::any();
}

} // namespace

/////////////////////////////////////////////////////////////////////////////
// CPythonObject

PyMethodDef CPythonObject::Skeleton::Methods[] =
{
	{"get_document", Owner::GetDocument, METH_VARARGS, "Return document that own object"},
	{"get_factory", Owner::GetFactory, METH_VARARGS, "Return object factory."},
	{"get_name", Owner::get_name, METH_VARARGS, "Get object name"},
	{"set_name", Owner::set_name, METH_VARARGS, "Set object name"},
	{"get_mesh", Owner::get_mesh, METH_VARARGS, "Return k3d::mesh object from a FrozenMesh"},
	{"EditObject", Owner::EditObject, METH_VARARGS, "Called to request that the object open its user interface."},
	{NULL, NULL, 0, NULL}
};

PyObject* CPythonObject::GetDocument(PyObject* self, PyObject* args)
{
	PyObject *object1;
	if (!PyArg_ParseTuple(args, "O", &object1))
		return PYVAL_FALSE;

	return CPythonDocument::Create(&Interface(object1)->document());
}

PyObject* CPythonObject::GetFactory(PyObject* self, PyObject* args)
{
	PyObject *object1;
	if (!PyArg_ParseTuple(args, "O", &object1))
		return PYVAL_FALSE;

	return CPythonPluginFactory::Create(&Interface(object1)->factory());
}

PyObject* CPythonObject::get_name(PyObject* self, PyObject* args)
{
	PyObject* object;
	return_val_if_fail(PyArg_ParseTuple(args, "O", &object), PYVAL_FALSE);

	return StringToPyVal(Interface(object)->name());
}

PyObject* CPythonObject::set_name(PyObject* self, PyObject* args)
{
	PyObject* object;
	char* name;
	return_val_if_fail(PyArg_ParseTuple(args, "Os", &object, &name), PYVAL_FALSE);

	Interface(object)->set_name(name);
	return PYVAL_TRUE;
}

PyObject* CPythonObject::get_mesh(PyObject* self, PyObject* args)
{
	PyObject* py_object;
	return_val_if_fail(PyArg_ParseTuple(args, "O", &py_object), PYVAL_FALSE);

	k3d::iobject* object = Interface(py_object);
	return_val_if_fail(object, PYVAL_FALSE);
	k3d::imesh_source* const mesh_source = dynamic_cast<k3d::imesh_source*>(object);
	return_val_if_fail(mesh_source, PYVAL_FALSE);

	k3d::mesh* const mesh = boost::any_cast<k3d::mesh*>(k3d::get_property_value(object->document().dag(), mesh_source->mesh_source_output()));
	return_val_if_fail(mesh, PYVAL_FALSE);

	return python_mesh::Create(mesh);
}

PyObject* CPythonObject::EditObject(PyObject* self, PyObject* args)
{
	PyObject *object1;
	if (!PyArg_ParseTuple(args, "O", &object1))
		return PYVAL_FALSE;

	if(k3d::application().user_interface())
		{
			k3d::application().user_interface()->show(*Interface(object1));
			return PYVAL_TRUE;
		}

	return PYVAL_FALSE;
}

/////////////////////////////////////////////////////////////////////////////
// CPythonBezierChannel

PyMethodDef CPythonBezierChannel::Skeleton::Methods[] =
{
	{"GetCurve", Owner::GetCurve, METH_VARARGS, "Returns the channel's curve."},
	{NULL, NULL, 0, NULL}
};

PyObject* CPythonBezierChannel::GetCurve(PyObject* self, PyObject* args)
{
	PyObject *object1;
	if(!PyArg_ParseTuple(args, "O", &object1))
		return PYVAL_FALSE;

/*
	k3d::ibezier_channel* const bezier_channel = dynamic_cast<k3d::ibezier_channel*>(Interface(object1));
	return_val_if_fail(bezier_channel, PYVAL_FALSE);

	k3d::ibezier_channel::control_points_t control_points;
	k3d::ibezier_channel::values_t values;
	bezier_channel->get_curve(control_points, values);
*/
	return PYVAL_FALSE;
}

/////////////////////////////////////////////////////////////////////////////
// CPythonDocument

PyMethodDef CPythonDocument::Skeleton::Methods[] =
{
	{"GetApplication", Owner::GetApplication, METH_VARARGS, "Return the application that owns document."},
	{"GetPath", Owner::GetPath, METH_VARARGS, "Return the filepath to the document."},
	{"Import", Owner::Import, METH_VARARGS, "Imports a geometry file into the document, using the given file format."},
	{"Export", Owner::Export, METH_VARARGS, "Exports the document to a geometry file, using the given file format."},
	{"Save", Owner::Save, METH_VARARGS, "Save document to a given file."},
	{"StartChangeSet", Owner::StartStateChangeSet, METH_VARARGS, "Records an original state that should be restored when undo."},
	{"FinishChangeSet", Owner::FinishStateChangeSet, METH_VARARGS, ""},
	{"RedrawAll", Owner::RedrawAll, METH_VARARGS, "Redraws all cameras."},

	{"CreateObject", Owner::CreateObject, METH_VARARGS, "Create a new K-3D object."},
	{"Objects", Owner::Objects, METH_VARARGS, "Return the set of K-3D objects within this document as a list."},
	{"GetObject", Owner::GetObject, METH_VARARGS, "Return named K-3D object."},
	{"DeleteObject", Owner::DeleteObject, METH_VARARGS, "Delete named K-3D object."},
	{"get_mesh_instances", Owner::get_mesh_instances, METH_VARARGS, "Returns mesh list"},
	{"get_frozen_meshes", Owner::get_frozen_meshes, METH_VARARGS, "Returns mesh list"},
	{"get_objects_by_name", Owner::get_objects_by_name, METH_VARARGS, "Returns mesh list"},
	{NULL, NULL, 0, NULL}
};

PyObject* CPythonDocument::GetApplication(PyObject* self, PyObject* args)
{
	//FIXME: Return Application instance
	return PYVAL_TRUE;
}

PyObject* CPythonDocument::GetPath(PyObject* self, PyObject* args)
{
	PyObject *object1;
	if (!PyArg_ParseTuple(args, "O", &object1))
		return PYVAL_FALSE;

	return StringToPyVal(Interface(object1)->path().native_file_string());
}

PyObject* CPythonDocument::Import(PyObject* self, PyObject* args)
{
	PyObject *object1;
	char *string1, *string2;
	if (!PyArg_ParseTuple(args, "Oss", &object1, &string1, &string2))
		return PYVAL_FALSE;

	const boost::filesystem::path filepath(string1, boost::filesystem::native);
	return_val_if_fail(!filepath.empty(), PYVAL_FALSE);

	const std::string formatname(string2);

	k3d::auto_ptr<k3d::igeometry_read_format> filter(
		formatname.size() ?
		k3d::file_filter<k3d::igeometry_read_format>(formatname) :
		k3d::auto_file_filter<k3d::igeometry_read_format>(filepath));
	return_val_if_fail(filter.get(), PYVAL_FALSE);

	return k3d::import_file(*Interface(object1), *filter, filepath) ? PYVAL_TRUE : PYVAL_FALSE;
}

PyObject* CPythonDocument::Export(PyObject* self, PyObject* args)
{
	PyObject *object1;
	char *string1, *string2;
	if (!PyArg_ParseTuple(args, "Oss", &object1, &string1, &string2))
		return PYVAL_FALSE;

	const boost::filesystem::path filepath(string1, boost::filesystem::native);
	return_val_if_fail(!filepath.empty(), PYVAL_FALSE);

	const std::string formatname(string2);

	k3d::auto_ptr<k3d::igeometry_write_format> filter(
		formatname.size() ?
		k3d::file_filter<k3d::igeometry_write_format>(formatname) :
		k3d::auto_file_filter<k3d::igeometry_write_format>(filepath));
	return_val_if_fail(filter.get(), PYVAL_FALSE);

	return k3d::export_file(*Interface(object1), *filter, filepath) ? PYVAL_TRUE : PYVAL_FALSE;
}

PyObject* CPythonDocument::Save(PyObject* self, PyObject* args)
{
	PyObject *object1;
	char *string1;
	if (!PyArg_ParseTuple(args, "Os", &object1, &string1))
		return PYVAL_FALSE;

	if (Interface(object1)->save(boost::filesystem::path(string1, boost::filesystem::native)))
		return PYVAL_TRUE;

	return PYVAL_FALSE;
}

PyObject* CPythonDocument::StartStateChangeSet(PyObject* self, PyObject* args)
{
	PyObject *object1;
	if (!PyArg_ParseTuple(args, "O", &object1))
		return PYVAL_FALSE;

	k3d::start_state_change_set(*Interface(object1));
	return PYVAL_TRUE;
}

PyObject* CPythonDocument::FinishStateChangeSet(PyObject* self, PyObject* args)
{
	PyObject *object1;
	char *string1;
	if (!PyArg_ParseTuple(args, "Os", &object1, &string1))
		return PYVAL_FALSE;

	k3d::finish_state_change_set(*Interface(object1), string1);
	return PYVAL_TRUE;
}

PyObject* CPythonDocument::RedrawAll(PyObject* self, PyObject* args)
{
	PyObject *object1;
	if (!PyArg_ParseTuple(args, "O", &object1))
		return PYVAL_FALSE;

	k3d::viewport::redraw_all(*Interface(object1), k3d::iviewport::ASYNCHRONOUS);
	return PYVAL_TRUE;
}

PyObject* CPythonDocument::CreateObject(PyObject* self, PyObject* args)
{
	PyObject *object1;
	char *string1;
	if (!PyArg_ParseTuple(args, "Os", &object1, &string1))
		return PYVAL_FALSE;

	// Look for object types by name ...
	k3d::iplugin_factory_collection::factories_t factories(k3d::plugins(string1));

	// If we got at exactly one object type, create an instance of it ...
	return_val_if_fail(1 == factories.size(), PYVAL_FALSE);

	// Create an object ...
	k3d::iobject* const object = k3d::create_document_plugin(**factories.begin(), *Interface(object1));
	return_val_if_fail(object, PYVAL_FALSE);

	// Return that baby...
	return CPythonObject::Create(object);
}

PyObject* CPythonDocument::Objects(PyObject* self, PyObject* args)
{
	PyObject *object1, *list1;
	if (!PyArg_ParseTuple(args, "O", &object1))
		return PYVAL_FALSE;

	// Return the set of objects in this collection ...
	const k3d::objects_t objects(Interface(object1)->objects().collection());

	// Convert the set to a Python list ...
	list1 = PyList_New(0);
	for(k3d::objects_t::const_iterator object = objects.begin(); object != objects.end(); ++object)
		PyList_Append(list1, CPythonObject::Create(*object));

	// Return that baby ...
	return list1;
}

PyObject* CPythonDocument::GetObject(PyObject* self, PyObject* args)
{
	PyObject *object1;
	char *string1;
	if (!PyArg_ParseTuple(args, "Os", &object1, &string1))
		return PYVAL_FALSE;

	// Look for objects by name ...
	const k3d::objects_t objects(k3d::find_objects(Interface(object1)->objects(), string1));

	// If we found something return it ...
	return_val_if_fail(1 == objects.size(), PYVAL_FALSE);

	return CPythonObject::Create(*objects.begin());
}

PyObject* CPythonDocument::DeleteObject(PyObject* self, PyObject* args)
{
	k3d::iobject* victim = 0;

	PyObject *object1, *object2;
	if (!PyArg_ParseTuple(args, "OO", &object1, &object2))
		return PYVAL_FALSE;

	if(PyString_Check(object2))
	{
		// Look for our victim by name ...
		const k3d::objects_t objects(k3d::find_objects(Interface(object1)->objects(), PyString_AsString(object2)));

		if(1 == objects.size());
		victim = *objects.begin();
	}
	else if(PyCObject_Check(object2))
	{
		// We got a direct reference to the object ...
		victim = CPythonObject::Interface(object2);
	}

	k3d::delete_objects(*Interface(object1), k3d::make_collection<k3d::objects_t>(victim));
	return PYVAL_TRUE;
}

PyObject* get_document_objects(k3d::idocument* Document, const k3d::uuid ClassID)
{
	// Get object collection ...
	const k3d::objects_t objects(k3d::find_objects(Document->objects(), ClassID));

	// Return it as a Python list ...
	PyObject* list = PyList_New(0);
	for(k3d::objects_t::const_iterator object = objects.begin(); object != objects.end(); ++object)
		PyList_Append(list, CPythonObject::Create(*object));

	return list;
}

PyObject* CPythonDocument::get_frozen_meshes(PyObject* self, PyObject* args)
{
	PyObject* py_document;
	return_val_if_fail(PyArg_ParseTuple(args, "O", &py_document), PYVAL_FALSE);

	k3d::idocument* document = dynamic_cast<k3d::idocument*>(Interface(py_document));
	return get_document_objects(document, k3d::classes::FrozenMesh());
}

PyObject* CPythonDocument::get_mesh_instances(PyObject* self, PyObject* args)
{
	PyObject* py_document;
	return_val_if_fail(PyArg_ParseTuple(args, "O", &py_document), PYVAL_FALSE);

	k3d::idocument* document = dynamic_cast<k3d::idocument*>(Interface(py_document));
	return get_document_objects(document, k3d::classes::MeshInstance());
}

PyObject* CPythonDocument::get_objects_by_name(PyObject* self, PyObject* args)
{
	PyObject* py_document;
	char* name;
	return_val_if_fail(PyArg_ParseTuple(args, "Os", &py_document, &name), PYVAL_FALSE);

	k3d::idocument* document = dynamic_cast<k3d::idocument*>(Interface(py_document));

	// Get object collection ...
	const k3d::objects_t objects(k3d::find_objects(document->objects(), name));

	// Return it as a Python list ...
	PyObject* list = PyList_New(0);
	for(k3d::objects_t::const_iterator object = objects.begin(); object != objects.end(); ++object)
		PyList_Append(list, CPythonObject::Create(*object));

	return list;
}

/////////////////////////////////////////////////////////////////////////////
// CPythonProperty

PyMethodDef CPythonProperty::Skeleton::Methods[] =
{
	{"get_property_name", Owner::GetName, METH_VARARGS, "Returns property name."},
	{"get_property_description", Owner::GetDescription, METH_VARARGS, "Returns property description."},
	{NULL, NULL, 0, NULL}
};

PyObject* CPythonProperty::GetName(PyObject* self, PyObject* args)
{
	PyObject *object1;

	if(!PyArg_ParseTuple(args, "O", &object1))
		return PYVAL_FALSE;

	k3d::iproperty* const property = dynamic_cast<k3d::iproperty*>(Interface(object1));
	return StringToPyVal(property->name());
}

PyObject* CPythonProperty::GetDescription(PyObject* self, PyObject* args)
{
	PyObject *object1;

	if(!PyArg_ParseTuple(args, "O", &object1))
		return PYVAL_FALSE;

	k3d::iproperty* const property = dynamic_cast<k3d::iproperty*>(Interface(object1));
	return StringToPyVal(property->description());
}

/////////////////////////////////////////////////////////////////////////////
// CPythonCommandNode

PyMethodDef CPythonCommandNode::Skeleton::Methods[] =
{
	{"Command", Owner::Command, METH_VARARGS, "Execute a command on the command-node."},
	{"get_child", Owner::GetChild, METH_VARARGS, "Return command-node child."},
	{"get_property", Owner::GetProperty, METH_VARARGS, "Return property value."},
	{"set_property", Owner::SetProperty, METH_VARARGS, "Set property value."},
	{"get_children", Owner::GetChildren, METH_VARARGS, "Return command-node children."},
	{"get_properties", Owner::GetProperties, METH_VARARGS, "Return command-node properties."},
	{NULL, NULL, 0, NULL}
};

PyObject* CPythonCommandNode::Command(PyObject* self, PyObject* args)
{
	PyObject *object1;
	char* string1;
	char* string2;
	if(!PyArg_ParseTuple(args, "Oss", &object1, &string1, &string2))
		return PYVAL_FALSE;

	const std::string command(string1);
	return_val_if_fail(command.size(), PYVAL_FALSE);

	const std::string arguments(string2);

	if(Interface(object1)->execute_command(command, arguments))
		return PYVAL_TRUE;

	return PYVAL_FALSE;
}

PyObject* CPythonCommandNode::GetChild(PyObject* self, PyObject* args)
{
	PyObject *object1, *object2;

	if(!PyArg_ParseTuple(args, "OO", &object1, &object2))
		return PYVAL_FALSE;

	if(!PyString_Check(object2))
		return PYVAL_FALSE;

	std::string child_name = PyString_AsString(object2);

	// Test to see if it's a command-node ...
	k3d::icommand_node* const command_node = dynamic_cast<k3d::icommand_node*>(Interface(object1));
	if(!command_node)
		return PYVAL_FALSE;

	// Check against children ...
	k3d::icommand_tree::children_t children = k3d::application().command_tree().children(*command_node);
	for(k3d::icommand_tree::children_t::iterator child = children.begin(); child != children.end(); ++child)
		if(child_name == (*child)->command_node_name())
			return CPythonCommandNode::Create(*child);

	return PYVAL_NONE;
}

/// k3d::iproperty::value wrapper
PyObject* CPythonCommandNode::GetProperty(PyObject* self, PyObject* args)
{
	PyObject *object1, *object2;

	if(!PyArg_ParseTuple(args, "OO", &object1, &object2))
		return PYVAL_FALSE;

	if(!PyString_Check(object2))
		return PYVAL_FALSE;

	std::string property_name = PyString_AsString(object2);

	// Test to see if it's a property collection ...
	k3d::iproperty_collection* const property_collection = dynamic_cast<k3d::iproperty_collection*>(Interface(object1));
	if(!property_collection)
		return PYVAL_FALSE;

	// Check against properties ...
	const k3d::iproperty_collection::properties_t properties(property_collection->properties());
	for(k3d::iproperty_collection::properties_t::const_iterator property = properties.begin(); property != properties.end(); ++property)
		if((*property)->name() == property_name)
			return convert((*property)->value());

	return PYVAL_NONE;
}

/// k3d::iproperty::set_value wrapper
PyObject* CPythonCommandNode::SetProperty(PyObject* self, PyObject* args)
{
	PyObject *object1, *object2, *object3;
	if (!PyArg_ParseTuple(args, "OOO", &object1, &object2, &object3))
		return PYVAL_FALSE;

	if(!PyString_Check(object2))
		return PYVAL_FALSE;

	std::string property_name = PyString_AsString(object2);

	// Test to see if it's a property collection ...
	k3d::iproperty_collection* const property_collection = dynamic_cast<k3d::iproperty_collection*>(Interface(object1));
	if(!property_collection)
		return PYVAL_FALSE;

	// Check against properties ...
	const k3d::iproperty_collection::properties_t properties(property_collection->properties());
	for(k3d::iproperty_collection::properties_t::const_iterator property = properties.begin(); property != properties.end(); ++property)
		{
			if((*property)->name() == property_name)
				{
					k3d::iwritable_property* const writable_property = dynamic_cast<k3d::iwritable_property*>(*property);
					if(writable_property)
						writable_property->set_value(convert(object3, (*property)->type()));

					break;
				}
		}

	return PYVAL_TRUE;
}

PyObject* CPythonCommandNode::GetChildren(PyObject* self, PyObject* args)
{
	PyObject *object1, *list1;

	if(!PyArg_ParseTuple(args, "O", &object1))
		return PYVAL_FALSE;

	// Test to see if it's a command-node ...
	k3d::icommand_node* const command_node = dynamic_cast<k3d::icommand_node*>(Interface(object1));
	if(!command_node)
		return PYVAL_FALSE;

	// List children names ...
	list1 = PyList_New(0);
	k3d::icommand_tree::children_t children = k3d::application().command_tree().children(*command_node);
	for(k3d::icommand_tree::children_t::iterator child = children.begin(); child != children.end(); ++child)
		PyList_Append(list1, StringToPyVal((*child)->command_node_name()));

	return list1;
}

PyObject* CPythonCommandNode::GetProperties(PyObject* self, PyObject* args)
{
	PyObject *object1, *list1;

	if(!PyArg_ParseTuple(args, "O", &object1))
		return PYVAL_FALSE;

	// Test to see if it's a property collection ...
	k3d::iproperty_collection* const property_collection = dynamic_cast<k3d::iproperty_collection*>(Interface(object1));
	if(!property_collection)
		return PYVAL_FALSE;

	// List node properties ...
	list1 = PyList_New(0);
	const k3d::iproperty_collection::properties_t properties(property_collection->properties());
	for(k3d::iproperty_collection::properties_t::const_iterator property = properties.begin(); property != properties.end(); ++property)
		PyList_Append(list1, CPythonProperty::Create(*property));
		//PyList_Append(list1, StringToPyVal((*property)->name()));

	return list1;
}

/////////////////////////////////////////////////////////////////////////////
// CPythonUserInterface

PyMethodDef CPythonUserInterface::Skeleton::Methods[] =
{
	{"BrowserNavigate", Owner::BrowserNavigate, METH_VARARGS, "Displays a URL in the user's preffered web browser."},
	{"Message", Owner::Message, METH_VARARGS, "Displays an informational message in a modal dialog box."},
	{"ErrorMessage", Owner::ErrorMessage, METH_VARARGS, "Displays an error in a modal dialog box."},
	{"QueryMessage", Owner::QueryMessage, METH_VARARGS, "Prompts the user to choose one of selections in a modal dialog box."},
	{"GetFilePath", Owner::GetFilePath, METH_VARARGS, "Prompt user for filepath."},
	{NULL, NULL, 0, NULL}
};

PyObject* CPythonUserInterface::BrowserNavigate(PyObject* self, PyObject* args)
{
	PyObject *object1;
	char *string1;
	if (!PyArg_ParseTuple(args, "Os", &object1, &string1))
		return PYVAL_FALSE;

	Interface(object1)->browser_navigate(string1);
	return PYVAL_TRUE;
}

PyObject* CPythonUserInterface::Message(PyObject* self, PyObject* args)
{
	PyObject *object1;
	char *string1, *string2;
	if (!PyArg_ParseTuple(args, "Oss", &object1, &string1, &string2))
		return PYVAL_FALSE;

	Interface(object1)->message(string1, string2);
	return PYVAL_TRUE;
}

PyObject* CPythonUserInterface::ErrorMessage(PyObject* self, PyObject* args)
{
	PyObject *object1;
	char *string1, *string2;
	if (!PyArg_ParseTuple(args, "Oss", &object1, &string1, &string2))
		return PYVAL_FALSE;

	Interface(object1)->error_message(string1, string2);
	return PYVAL_TRUE;
}

PyObject* CPythonUserInterface::QueryMessage(PyObject* self, PyObject* args)
{
	PyObject *object1, *list1;
	char *string1, *string2;
	if (!PyArg_ParseTuple(args, "OssO", &object1, &string1, &string2, &list1))
		return PYVAL_FALSE;

	std::vector<std::string> buttons;
	for(int i = 0; i < PyList_Size(list1); i++)
	{
		buttons.push_back(std::string(PyString_AsString(PyList_GetItem(list1, i))));
	}

	return IntToPyVal(Interface(object1)->query_message(string1, string2, 0, buttons));
}

PyObject* CPythonUserInterface::GetFilePath(PyObject* self, PyObject* args)
{
	PyObject *object1;
	char *string1, *string2, *string3;
	int bool1 = 1;
	if(!PyArg_ParseTuple(args, "Ossis", &object1, &string1, &string2, bool1, &string3))
		return PYVAL_FALSE;

	const boost::filesystem::path old_path(string3, boost::filesystem::native);
	boost::filesystem::path FilePath;
	Interface(object1)->get_file_path(string1, string2, bool1 == 1, old_path, FilePath);

	return StringToPyVal(FilePath.native_file_string());
}

/////////////////////////////////////////////////////////////////////////////
// CPythonScriptEngines

PyObject* CPythonScriptEngines::Create()
{
	return PYVAL_TRUE;
}

PyMethodDef CPythonScriptEngines::Methods[] =
{
	{"PlayFile", PlayFile, METH_VARARGS, "Play script."},
	{NULL, NULL, 0, NULL}
};

PyObject* CPythonScriptEngines::PlayFile(PyObject* self, PyObject* args)
{
	char *string1;
	if (!PyArg_ParseTuple(args, "s", &string1))
		return PYVAL_FALSE;

	const boost::filesystem::path filepath(string1, boost::filesystem::native);

	bool recognized = false;
	bool executed = false;
	boost::filesystem::ifstream file(filepath);
	k3d::execute_script(file, filepath.native_file_string(), k3d::iscript_engine::context_t(), recognized, executed);

	if (recognized && executed)
		return PYVAL_TRUE;

	return PYVAL_FALSE;
}

/////////////////////////////////////////////////////////////////////////////
// CPythonPluginFactory

PyMethodDef CPythonPluginFactory::Methods[] =
{
	{"get_attribute", GetAttribute, METH_VARARGS, "Returns names and types."},
	{NULL, NULL, 0, NULL}
};

PyObject* CPythonPluginFactory::GetAttribute(PyObject* self, PyObject* args)
{
	PyObject *object1, *object2;

	if(!PyArg_ParseTuple(args, "OO", &object1, &object2))
		return PYVAL_FALSE;

	if(!PyString_Check(object2))
		return PYVAL_FALSE;

	std::string attribute = PyString_AsString(object2);

	k3d::iplugin_factory* const plugin_factory = dynamic_cast<k3d::iplugin_factory*>(Interface(object1));
	if(attribute == "Name")
		return StringToPyVal(plugin_factory->name());
	if(attribute == "ShortDescription")
		return StringToPyVal(plugin_factory->short_description());
	if(attribute == "DefaultCategory")
		return StringToPyVal(plugin_factory->default_category());

	if(attribute == "ApplicationPlugin")
		{
			k3d::iapplication_plugin_factory* const application_plugin_factory = dynamic_cast<k3d::iapplication_plugin_factory*>(Interface(object1));
			if(application_plugin_factory)
				return PYVAL_TRUE;

			return PYVAL_NONE;
		}
	if(attribute == "DocumentPlugin")
		{
			k3d::idocument_plugin_factory* const document_plugin_factory = dynamic_cast<k3d::idocument_plugin_factory*>(Interface(object1));
			if(document_plugin_factory)
				return PYVAL_TRUE;

			return PYVAL_NONE;
		}

	return PYVAL_FALSE;
}

/////////////////////////////////////////////////////////////////////////////
// CPythonApplication

PyMethodDef CPythonApplication::Methods[] =
{
	{"get_attribute", GetAttribute, METH_VARARGS, "Return objects and paths."},
	{"Close", Close, METH_VARARGS, "Closes K-3D."},
	{"NewDocument", NewDocument, METH_VARARGS, "Creates a new K-3D document."},
	{"OpenDocument", OpenDocument, METH_VARARGS, "Opens a  K-3D document."},
	{"CloseDocument", CloseDocument, METH_VARARGS, "Closes an open K-3D document."},
	{"get_command_node", CommandNode, METH_VARARGS, "Return a command-node."},
	{"get_plugin_factories", Plugins, METH_VARARGS, "Returns plugin factories."},
	{NULL, NULL, 0, NULL}
};

PyObject* CPythonApplication::GetAttribute(PyObject* self, PyObject* args)
{
	PyObject *object1;

	if(!PyArg_ParseTuple(args, "O", &object1))
		return PYVAL_FALSE;

	if(!PyString_Check(object1))
		return PYVAL_FALSE;

	std::string attribute = PyString_AsString(object1);

	if(attribute == "UI")
		// Return User Interface
		if(k3d::application().user_interface())
			return CPythonUserInterface::Create(k3d::application().user_interface());
		else
			return PYVAL_NONE;
	if(attribute == "ScriptEngine")
		return CPythonScriptEngines::Create();
	if(attribute == "Documents")
		{
			PyObject* list1;

			// Convert the set of open documents to a Python list ...
			list1 = PyList_New(0);

			const k3d::iapplication::document_collection_t documents(k3d::application().documents());
			for(k3d::iapplication::document_collection_t::const_iterator document = documents.begin(); document != documents.end(); ++document)
				PyList_Append(list1, CPythonDocument::Create(*document));

			return list1;
		}
	if(attribute == "ShaderCachePath")
		return StringToPyVal(k3d::application().shader_cache_path().native_file_string());
	if(attribute == "SharePath")
		return StringToPyVal(k3d::application().share_path().native_file_string());

	return PYVAL_FALSE;
}

PyObject* CPythonApplication::Close(PyObject* self, PyObject* args)
{
	return PYVAL_NONE;
}

PyObject* CPythonApplication::NewDocument(PyObject* self, PyObject* args)
{
	k3d::idocument* const document = k3d::application().create_document();
	if(document)
		return CPythonDocument::Create(document);

	return PYVAL_NONE;
}

PyObject* CPythonApplication::OpenDocument(PyObject* self, PyObject* args)
{
	const char *string1;
	if (!PyArg_ParseTuple(args, "s", &string1))
		return PYVAL_FALSE;

	k3d::idocument* const document = k3d::application().open_document(boost::filesystem::path(string1, boost::filesystem::native));
	if(document)
		return CPythonDocument::Create(document);

	return PYVAL_NONE;
}

PyObject* CPythonApplication::CloseDocument(PyObject* self, PyObject* args)
{
	PyObject *object1;
	if (!PyArg_ParseTuple(args, "O", &object1))
		return PYVAL_FALSE;

	k3d::application().close_document(*CPythonDocument::Interface(object1));

	return PYVAL_TRUE;
}

PyObject* CPythonApplication::CommandNode(PyObject* self, PyObject* args)
{
	//k3d::icommand_node* const application_node = dynamic_cast<k3d::icommand_node*>(k3dApplication());
	//return CPythonCommandNode::Create(application_node);

	char *string1;
	if(!PyArg_ParseTuple(args, "s", &string1))
		return PYVAL_FALSE;

	std::string nodepath(string1);
	if(0 == nodepath.size())
	{
		std::cerr << "Empty command node path";
		return PYVAL_FALSE;
	}

	k3d::icommand_node* node = k3d::get_command_node(nodepath);
	if(0 == node)
	{
		std::cerr << "Could not find command node [" << nodepath << "]" << std::endl;
		return PYVAL_FALSE;
	}

	return CPythonCommandNode::Create(node);
}

PyObject* CPythonApplication::Plugins(PyObject* self, PyObject* args)
{
	const k3d::iplugin_factory_collection::factories_t& factories(k3d::application().plugins());

	PyObject* list1 = PyList_New(0);

	for(k3d::iplugin_factory_collection::factories_t::const_iterator factory = factories.begin(); factory != factories.end(); ++factory)
		PyList_Append(list1, CPythonPluginFactory::Create(*factory));

	return list1;
}

/////////////////////////////////////////////////////////////////////////////
// python_mesh

PyMethodDef python_mesh::methods[] =
{
	{"create_mesh", create_mesh, METH_VARARGS, "Create a new mesh"},
	{"create_mesh_object", create_mesh_object, METH_VARARGS, "Create an object for a mesh"},
	{"create_mesh_instance", create_mesh_instance, METH_VARARGS, "Create an instance for a mesh object"},
	{"create_point", create_point, METH_VARARGS, "Create a new point, add it to the point list"},
	{"get_points", get_points, METH_VARARGS, "Get mesh points as a list"},
	{"create_polyhedron", create_polyhedron, METH_VARARGS, "Create a new polyhedron, add it to the polyhedra list"},
	{"get_polyhedra", get_polyhedra, METH_VARARGS, "Get mesh polyhedra as a list"},
	{"add_blobby", add_blobby, METH_VARARGS, "Adds a blobby object"},
	{0, 0, 0, 0}
};

PyObject* python_mesh::create_mesh(PyObject* self, PyObject* args)
{
	k3d::mesh* const mesh = new k3d::mesh();
	return_val_if_fail(mesh, PYVAL_NONE);

	return Create(mesh);
}

PyObject* python_mesh::create_mesh_object(PyObject* self, PyObject* args)
{
	PyObject* py_mesh;
	PyObject* py_document;
	return_val_if_fail(PyArg_ParseTuple(args, "OO", &py_mesh, &py_document), PYVAL_FALSE);

	k3d::idocument* document = dynamic_cast<k3d::idocument*>(CPythonDocument::Interface(py_document));
	return_val_if_fail(document, PYVAL_FALSE);

	// Create a FrozenMesh
	k3d::iobject* const frozen_mesh = k3d::create_document_plugin(k3d::classes::FrozenMesh(), *document);
	return_val_if_fail(frozen_mesh, PYVAL_FALSE);

	frozen_mesh->set_name("Mesh");

	k3d::imesh_sink* const frozen_mesh_sink = dynamic_cast<k3d::imesh_sink*>(frozen_mesh);
	return_val_if_fail(frozen_mesh_sink, PYVAL_FALSE);
	assert_warning(k3d::set_property_value(frozen_mesh_sink->mesh_sink_input(), dynamic_cast<k3d::mesh*>(Interface(py_mesh))));

	return CPythonObject::Create(frozen_mesh);
}
PyObject* python_mesh::create_mesh_instance(PyObject* self, PyObject* args)
{
	PyObject* py_frozen_mesh;
	PyObject* py_document;
	if(!PyArg_ParseTuple(args, "OO", &py_frozen_mesh, &py_document))
		return PYVAL_FALSE;

	k3d::idocument* document = dynamic_cast<k3d::idocument*>(CPythonDocument::Interface(py_document));
	return_val_if_fail(document, PYVAL_FALSE);

	// Create mesh object instance ...
	k3d::iobject* instance = k3d::create_document_plugin(k3d::classes::MeshInstance(), *document);
	return_val_if_fail(instance, PYVAL_FALSE);

	instance->set_name("Mesh instance");

	// Set dependencies ...
	k3d::imesh_sink* const instance_sink = dynamic_cast<k3d::imesh_sink*>(instance);
	return_val_if_fail(instance_sink, false);
	k3d::imesh_source* const frozen_mesh_source = dynamic_cast<k3d::imesh_source*>(CPythonObject::Interface(py_frozen_mesh));
	return_val_if_fail(frozen_mesh_source, PYVAL_FALSE);

	k3d::idag::dependencies_t dependencies;
	dependencies[&instance_sink->mesh_sink_input()] = &frozen_mesh_source->mesh_source_output();
	document->dag().set_dependencies(dependencies);

	return CPythonObject::Create(instance);
}

PyObject* python_mesh::create_point(PyObject* self, PyObject* args)
{
	PyObject* py_mesh;
	PyObject* py_vector;
	return_val_if_fail(PyArg_ParseTuple(args, "OO", &py_mesh, &py_vector), PYVAL_FALSE);

	k3d::mesh* const mesh = dynamic_cast<k3d::mesh*>(Interface(py_mesh));
	return_val_if_fail(mesh, PYVAL_FALSE);

	k3d::vector3 position = k3d::vector3(0, 0, 0);
	if(py_vector != Py_None)
		position = ConvertVector3(py_vector);

	k3d::point* const point = new k3d::point(position);
	return_val_if_fail(point, PYVAL_FALSE);

	mesh->points.push_back(point);
	return python_point::Create(point);
}

PyObject* python_mesh::get_points(PyObject* self, PyObject* args)
{
	PyObject* py_mesh;
	return_val_if_fail(PyArg_ParseTuple(args, "O", &py_mesh), PYVAL_FALSE);

	k3d::mesh* const mesh = dynamic_cast<k3d::mesh*>(Interface(py_mesh));
	return_val_if_fail(mesh, PYVAL_FALSE);

	PyObject* list = PyList_New(0);
	for(k3d::mesh::points_t::const_iterator point = mesh->points.begin(); point != mesh->points.end(); ++point)
		PyList_Append(list, python_point::Create(*point));

	return list;
}

PyObject* python_mesh::create_polyhedron(PyObject* self, PyObject* args)
{
	PyObject* py_mesh;
	return_val_if_fail(PyArg_ParseTuple(args, "O", &py_mesh), PYVAL_FALSE);

	k3d::mesh* const mesh = dynamic_cast<k3d::mesh*>(Interface(py_mesh));
	return_val_if_fail(mesh, PYVAL_FALSE);

	k3d::polyhedron* const polyhedron = new k3d::polyhedron();
	return_val_if_fail(polyhedron, PYVAL_FALSE);

	mesh->polyhedra.push_back(polyhedron);
	return python_polyhedron::Create(polyhedron);
}

PyObject* python_mesh::get_polyhedra(PyObject* self, PyObject* args)
{
	PyObject* py_mesh;
	return_val_if_fail(PyArg_ParseTuple(args, "O", &py_mesh), PYVAL_FALSE);

	k3d::mesh* const mesh = dynamic_cast<k3d::mesh*>(Interface(py_mesh));
	return_val_if_fail(mesh, PYVAL_FALSE);

	PyObject* list = PyList_New(0);
	for(k3d::mesh::polyhedra_t::const_iterator polyhedron = mesh->polyhedra.begin(); polyhedron != mesh->polyhedra.end(); ++polyhedron)
		PyList_Append(list, python_polyhedron::Create(*polyhedron));

	return list;
}

/// Collects blobby points
class get_blobby_points :
	public k3d::blobby::visitor
{
public:
	get_blobby_points(k3d::blobby& Blobby, k3d::mesh& Mesh) :
		mesh(Mesh)
	{
		Blobby.accept(*this);
	}

	void visit_constant(k3d::blobby::constant&)
	{
	}

	void visit_ellipsoid(k3d::blobby::ellipsoid& Ellipsoid)
	{
		mesh.points.push_back(Ellipsoid.origin);
	}

	void visit_segment(k3d::blobby::segment& Segment)
	{
		mesh.points.push_back(Segment.start);
		mesh.points.push_back(Segment.end);
	}

	void visit_subtract(k3d::blobby::subtract& Subtract)
	{
		Subtract.subtrahend->accept(*this);
		Subtract.minuend->accept(*this);
	}

	void visit_divide(k3d::blobby::divide& Divide)
	{
		Divide.dividend->accept(*this);
		Divide.divisor->accept(*this);
	}

	void visit_add(k3d::blobby::add& Add)
	{
		Add.operands_accept(*this);
	}

	void visit_multiply(k3d::blobby::multiply& Multiply)
	{
		Multiply.operands_accept(*this);
	}

	void visit_min(k3d::blobby::min& Min)
	{
		Min.operands_accept(*this);
	}

	void visit_max(k3d::blobby::max& Max)
	{
		Max.operands_accept(*this);
	}

private:
	k3d::mesh& mesh;
};

PyObject* python_mesh::add_blobby(PyObject* self, PyObject* args)
{
	PyObject* py_mesh;
	PyObject* py_opcode;
	return_val_if_fail(PyArg_ParseTuple(args, "OO", &py_mesh, &py_opcode), PYVAL_FALSE);

	k3d::mesh* const mesh = dynamic_cast<k3d::mesh*>(Interface(py_mesh));
	return_val_if_fail(mesh, PYVAL_FALSE);

	k3d::blobby::opcode* const opcode = dynamic_cast<k3d::blobby::opcode*>(python_blobby_opcode::Interface(py_opcode));
	return_val_if_fail(opcode, PYVAL_FALSE);

	k3d::blobby* new_blobby = new k3d::blobby(opcode);
	return_val_if_fail(new_blobby, PYVAL_FALSE);

	get_blobby_points(*new_blobby, *mesh);

	mesh->blobbies.push_back(new k3d::blobby(opcode));

	return PYVAL_TRUE;
}

/////////////////////////////////////////////////////////////////////////////
// python_point

PyMethodDef python_point::methods[] =
{
	{"get_position", get_position, METH_VARARGS, "Return point position"},
	{"set_position", set_position, METH_VARARGS, "Set point position"},
	{"get_reference", get_reference, METH_VARARGS, "Return point reference (C++ pointer)"},
	{0, 0, 0, 0}
};

PyObject* python_point::get_position(PyObject* self, PyObject* args)
{
	PyObject* py_point;
	return_val_if_fail(PyArg_ParseTuple(args, "O", &py_point), PYVAL_FALSE);

	k3d::point* const point = dynamic_cast<k3d::point*>(Interface(py_point));
	return_val_if_fail(point, PYVAL_FALSE);

	return ConvertTuple3(point->position);
}

PyObject* python_point::set_position(PyObject* self, PyObject* args)
{
	PyObject* py_point;
	PyObject* py_vector;
	return_val_if_fail(PyArg_ParseTuple(args, "OO", &py_point, &py_vector), PYVAL_FALSE);

	k3d::point* const point = dynamic_cast<k3d::point*>(Interface(py_point));
	return_val_if_fail(point, PYVAL_FALSE);

	point->position = ConvertVector3(py_vector);

	return PYVAL_TRUE;
}

PyObject* python_point::get_reference(PyObject* self, PyObject* args)
{
	PyObject* py_point;
	return_val_if_fail(PyArg_ParseTuple(args, "O", &py_point), PYVAL_FALSE);

	k3d::point* const point = dynamic_cast<k3d::point*>(Interface(py_point));
	return_val_if_fail(point, PYVAL_FALSE);

	return Py_BuildValue("i", reinterpret_cast<unsigned long>(point));
}

/////////////////////////////////////////////////////////////////////////////
// python_polyhedron

PyMethodDef python_polyhedron::methods[] =
{
	{"create_face", create_face, METH_VARARGS, "Create a new face"},
	{"add_point_to_face", add_point_to_face, METH_VARARGS, "Add a new edge to an existing face"},
	{"get_faces", get_faces, METH_VARARGS, "Return face list"},
	{0, 0, 0, 0}
};

PyObject* python_polyhedron::create_face(PyObject* self, PyObject* args)
{
	PyObject* py_polyhedron;
	PyObject* py_mesh;
	PyObject* py_point;
	return_val_if_fail(PyArg_ParseTuple(args, "OOO", &py_polyhedron, &py_mesh, &py_point), PYVAL_FALSE);

	k3d::polyhedron* const polyhedron = dynamic_cast<k3d::polyhedron*>(Interface(py_polyhedron));
	return_val_if_fail(polyhedron, PYVAL_FALSE);
	// TODO: check that point and polyhedron belong to the same mesh
	k3d::point* const point = dynamic_cast<k3d::point*>(python_point::Interface(py_point));
	return_val_if_fail(point, PYVAL_FALSE);

	k3d::split_edge* const edge = new k3d::split_edge(point);
	return_val_if_fail(edge, PYVAL_FALSE);
	k3d::face* const face = new k3d::face(edge);
	return_val_if_fail(face, PYVAL_FALSE);

	polyhedron->edges.push_back(edge);
	polyhedron->faces.push_back(face);

	return python_face::Create(face);
}

PyObject* python_polyhedron::add_point_to_face(PyObject* self, PyObject* args)
{
	PyObject* py_polyhedron;
	PyObject* py_mesh;
	PyObject* py_face;
	PyObject* py_point;
	return_val_if_fail(PyArg_ParseTuple(args, "OOOO", &py_polyhedron, &py_mesh, &py_face, &py_point), PYVAL_FALSE);

	k3d::polyhedron* const polyhedron = dynamic_cast<k3d::polyhedron*>(Interface(py_polyhedron));
	return_val_if_fail(polyhedron, PYVAL_FALSE);
	// TODO: check that point and polyhedron belong to the same mesh
	k3d::face* const face = dynamic_cast<k3d::face*>(python_face::Interface(py_face));
	return_val_if_fail(face, PYVAL_FALSE);
	k3d::point* const point = dynamic_cast<k3d::point*>(python_point::Interface(py_point));
	return_val_if_fail(point, PYVAL_FALSE);

	k3d::split_edge* const new_edge = new k3d::split_edge(point);
	return_val_if_fail(new_edge, PYVAL_FALSE);

	polyhedron->edges.push_back(new_edge);
	k3d::split_edge* edge = face->first_edge;
	while(edge->face_clockwise && edge->face_clockwise != face->first_edge)
		{
			edge = edge->face_clockwise;
		}
	edge->face_clockwise = new_edge;
	new_edge->face_clockwise = face->first_edge;

	return python_face::Create(face);
}

PyObject* python_polyhedron::get_faces(PyObject* self, PyObject* args)
{
	PyObject* py_polyhedron;
	return_val_if_fail(PyArg_ParseTuple(args, "O", &py_polyhedron), PYVAL_FALSE);

	k3d::polyhedron* const polyhedron = dynamic_cast<k3d::polyhedron*>(Interface(py_polyhedron));
	return_val_if_fail(polyhedron, PYVAL_FALSE);

	PyObject* list = PyList_New(0);
	for(k3d::polyhedron::faces_t::const_iterator face = polyhedron->faces.begin(); face != polyhedron->faces.end(); ++face)
		PyList_Append(list, python_face::Create(*face));

	return list;
}

/////////////////////////////////////////////////////////////////////////////
// python_face

PyMethodDef python_face::methods[] =
{
	{"get_edge_points", get_edge_points, METH_VARARGS, "Return point list"},
	{0, 0, 0, 0}
};

PyObject* python_face::get_edge_points(PyObject* self, PyObject* args)
{
	PyObject* py_face;
	return_val_if_fail(PyArg_ParseTuple(args, "O", &py_face), PYVAL_FALSE);

	k3d::face* const face = dynamic_cast<k3d::face*>(Interface(py_face));
	return_val_if_fail(face, PYVAL_FALSE);

	PyObject* list = PyList_New(0);
	k3d::split_edge* edge = face->first_edge;
	while(edge)
		{
			PyList_Append(list, python_point::Create(edge->vertex));

			edge = edge->face_clockwise;
			if(edge && edge == face->first_edge)
				edge = 0;
		}

	return list;
}

/////////////////////////////////////////////////////////////////////////////
// python_blobby_opcode

PyMethodDef python_blobby_opcode::methods[] =
{
	{"ellipsoid", create_ellipsoid, METH_VARARGS, "Creates a new blobby ellispoid"},
	{"segment", create_segment, METH_VARARGS, "Creates a new blobby segment"},
	{"set_color", set_color, METH_VARARGS, "Set ellipsoid or segment color"},
	{"add", add_operation, METH_VARARGS, "Add operation"},
	{"multiply", mult_operation, METH_VARARGS, "Mult operation"},
	{"max", max_operation, METH_VARARGS, "Max operation"},
	{"min", min_operation, METH_VARARGS, "Min operation"},
	{"subtract", sub_operation, METH_VARARGS, "Sub operation"},
	{"divide", div_operation, METH_VARARGS, "Div operation"},
	{0, 0, 0, 0}
};

PyObject* python_blobby_opcode::create_ellipsoid(PyObject* self, PyObject* args)
{
	PyObject* py_position;
	//PyObject* py_matrix;
	return_val_if_fail(PyArg_ParseTuple(args, "O", &py_position), PYVAL_FALSE);

	k3d::vector3 position = ConvertVector3(py_position);
	k3d::matrix4 transformation = k3d::identity3D();
	k3d::blobby::opcode* const ellipsoid = new k3d::blobby::ellipsoid(new k3d::point(position), transformation);
	return_val_if_fail(ellipsoid, PYVAL_NONE);

	return Create(ellipsoid);
}

PyObject* python_blobby_opcode::create_segment(PyObject* self, PyObject* args)
{
	PyObject* py_start;
	PyObject* py_end;
	PyObject* py_radius;
	//PyObject* py_matrix;
	return_val_if_fail(PyArg_ParseTuple(args, "OOO", &py_start, &py_end, &py_radius), PYVAL_FALSE);

	k3d::vector3 start = ConvertVector3(py_start);
	k3d::vector3 end = ConvertVector3(py_end);
	double radius = PyFloat_AsDouble(py_radius);
	k3d::matrix4 transformation = k3d::identity3D();
	k3d::blobby::opcode* const segment = new k3d::blobby::segment(new k3d::point(start), new k3d::point(end), radius, transformation);
	return_val_if_fail(segment, PYVAL_NONE);

	return Create(segment);
}

PyObject* python_blobby_opcode::set_color(PyObject* self, PyObject* args)
{
	PyObject* py_opcode;
	PyObject* py_color;
	return_val_if_fail(PyArg_ParseTuple(args, "OO", &py_opcode, &py_color), PYVAL_FALSE);

	k3d::vector3 color = ConvertVector3(py_color);
	k3d::blobby::ellipsoid* ellipsoid = dynamic_cast<k3d::blobby::ellipsoid*>(Interface(py_opcode));
	if(ellipsoid)
		{
			ellipsoid->vertex_data["Cs"] = k3d::color(color[0], color[1], color[2]);
			return PYVAL_TRUE;
		}

	k3d::blobby::segment* segment = dynamic_cast<k3d::blobby::segment*>(Interface(py_opcode));
	if(segment)
		{
			segment->vertex_data["Cs"] = k3d::color(color[0], color[1], color[2]);
			return PYVAL_TRUE;
		}

	return PYVAL_FALSE;
}

PyObject* python_blobby_opcode::add_operation(PyObject* self, PyObject* args)
{
	PyObject* py_opcode1;
	PyObject* py_opcode2;
	return_val_if_fail(PyArg_ParseTuple(args, "OO", &py_opcode1, &py_opcode2), PYVAL_FALSE);

	k3d::blobby::add* blob1 = dynamic_cast<k3d::blobby::add*>(Interface(py_opcode1));
	if(blob1)
		{
			// First operand is an Add operator
			k3d::blobby::add* blob2 = dynamic_cast<k3d::blobby::add*>(Interface(py_opcode2));
			if(blob2)
				{
					// Both blobbies are Add operators
					for(k3d::blobby::variable_operands::operands_t::const_iterator operand = blob2->operands.begin(); operand != blob2->operands.end(); operand++)
						blob1->add_operand(*operand);

					// TODO DELETE blob2
				}
			else
				{
					k3d::blobby::opcode* second = dynamic_cast<k3d::blobby::opcode*>(Interface(py_opcode2));
					blob1->add_operand(second);
				}

			return py_opcode1;
		}
	else
		{
			k3d::blobby::add* blob2 = dynamic_cast<k3d::blobby::add*>(Interface(py_opcode2));
			if(blob2)
				{
					// Second operand is an Add operator
					k3d::blobby::opcode* first = dynamic_cast<k3d::blobby::opcode*>(Interface(py_opcode1));
					blob2->add_operand(first);
					return py_opcode2;
				}
			else
				{
					// None is an Add operator
					k3d::blobby::add* addition = new k3d::blobby::add();
					k3d::blobby::opcode* first = dynamic_cast<k3d::blobby::opcode*>(Interface(py_opcode1));
					k3d::blobby::opcode* second = dynamic_cast<k3d::blobby::opcode*>(Interface(py_opcode2));
					addition->add_operand(first);
					addition->add_operand(second);
					return Create(addition);
				}
		}
}

PyObject* python_blobby_opcode::mult_operation(PyObject* self, PyObject* args)
{
	PyObject* py_opcode1;
	PyObject* py_opcode2;
	return_val_if_fail(PyArg_ParseTuple(args, "OO", &py_opcode1, &py_opcode2), PYVAL_FALSE);

	k3d::blobby::multiply* blob1 = dynamic_cast<k3d::blobby::multiply*>(Interface(py_opcode1));
	if(blob1)
		{
			// First operand is an Add operator
			k3d::blobby::multiply* blob2 = dynamic_cast<k3d::blobby::multiply*>(Interface(py_opcode2));
			if(blob2)
				{
					// Both blobbies are Add operators
					for(k3d::blobby::variable_operands::operands_t::const_iterator operand = blob2->operands.begin(); operand != blob2->operands.end(); operand++)
						blob1->add_operand(*operand);

					// TODO DELETE blob2
				}
			else
				{
					k3d::blobby::opcode* second = dynamic_cast<k3d::blobby::opcode*>(Interface(py_opcode2));
					blob1->add_operand(second);
				}

			return py_opcode1;
		}
	else
		{
			k3d::blobby::multiply* blob2 = dynamic_cast<k3d::blobby::multiply*>(Interface(py_opcode2));
			if(blob2)
				{
					// Second operand is an Add operator
					k3d::blobby::opcode* first = dynamic_cast<k3d::blobby::opcode*>(Interface(py_opcode1));
					blob2->add_operand(first);
					return py_opcode2;
				}
			else
				{
					// None is an Add operator
					k3d::blobby::multiply* addition = new k3d::blobby::multiply();
					k3d::blobby::opcode* first = dynamic_cast<k3d::blobby::opcode*>(Interface(py_opcode1));
					k3d::blobby::opcode* second = dynamic_cast<k3d::blobby::opcode*>(Interface(py_opcode2));
					addition->add_operand(first);
					addition->add_operand(second);
					return Create(addition);
				}
		}
}

PyObject* python_blobby_opcode::max_operation(PyObject* self, PyObject* args)
{
	PyObject* py_opcode1;
	PyObject* py_opcode2;
	return_val_if_fail(PyArg_ParseTuple(args, "OO", &py_opcode1, &py_opcode2), PYVAL_FALSE);

	k3d::blobby::max* blob1 = dynamic_cast<k3d::blobby::max*>(Interface(py_opcode1));
	if(blob1)
		{
			// First operand is an Add operator
			k3d::blobby::max* blob2 = dynamic_cast<k3d::blobby::max*>(Interface(py_opcode2));
			if(blob2)
				{
					// Both blobbies are Add operators
					for(k3d::blobby::variable_operands::operands_t::const_iterator operand = blob2->operands.begin(); operand != blob2->operands.end(); operand++)
						blob1->add_operand(*operand);

					// TODO DELETE blob2
				}
			else
				{
					k3d::blobby::opcode* second = dynamic_cast<k3d::blobby::opcode*>(Interface(py_opcode2));
					blob1->add_operand(second);
				}

			return py_opcode1;
		}
	else
		{
			k3d::blobby::max* blob2 = dynamic_cast<k3d::blobby::max*>(Interface(py_opcode2));
			if(blob2)
				{
					// Second operand is an Add operator
					k3d::blobby::opcode* first = dynamic_cast<k3d::blobby::opcode*>(Interface(py_opcode1));
					blob2->add_operand(first);
					return py_opcode2;
				}
			else
				{
					// None is an Add operator
					k3d::blobby::max* addition = new k3d::blobby::max();
					k3d::blobby::opcode* first = dynamic_cast<k3d::blobby::opcode*>(Interface(py_opcode1));
					k3d::blobby::opcode* second = dynamic_cast<k3d::blobby::opcode*>(Interface(py_opcode2));
					addition->add_operand(first);
					addition->add_operand(second);
					return Create(addition);
				}
		}
}

PyObject* python_blobby_opcode::min_operation(PyObject* self, PyObject* args)
{
	PyObject* py_opcode1;
	PyObject* py_opcode2;
	return_val_if_fail(PyArg_ParseTuple(args, "OO", &py_opcode1, &py_opcode2), PYVAL_FALSE);

	k3d::blobby::min* blob1 = dynamic_cast<k3d::blobby::min*>(Interface(py_opcode1));
	if(blob1)
		{
			// First operand is an Add operator
			k3d::blobby::min* blob2 = dynamic_cast<k3d::blobby::min*>(Interface(py_opcode2));
			if(blob2)
				{
					// Both blobbies are Add operators
					for(k3d::blobby::variable_operands::operands_t::const_iterator operand = blob2->operands.begin(); operand != blob2->operands.end(); operand++)
						blob1->add_operand(*operand);

					// TODO DELETE blob2
				}
			else
				{
					k3d::blobby::opcode* second = dynamic_cast<k3d::blobby::opcode*>(Interface(py_opcode2));
					blob1->add_operand(second);
				}

			return py_opcode1;
		}
	else
		{
			k3d::blobby::min* blob2 = dynamic_cast<k3d::blobby::min*>(Interface(py_opcode2));
			if(blob2)
				{
					// Second operand is an Add operator
					k3d::blobby::opcode* first = dynamic_cast<k3d::blobby::opcode*>(Interface(py_opcode1));
					blob2->add_operand(first);
					return py_opcode2;
				}
			else
				{
					// None is an Add operator
					k3d::blobby::min* addition = new k3d::blobby::min();
					k3d::blobby::opcode* first = dynamic_cast<k3d::blobby::opcode*>(Interface(py_opcode1));
					k3d::blobby::opcode* second = dynamic_cast<k3d::blobby::opcode*>(Interface(py_opcode2));
					addition->add_operand(first);
					addition->add_operand(second);
					return Create(addition);
				}
		}
}

PyObject* python_blobby_opcode::sub_operation(PyObject* self, PyObject* args)
{
	PyObject* py_opcode1;
	PyObject* py_opcode2;
	return_val_if_fail(PyArg_ParseTuple(args, "OO", &py_opcode1, &py_opcode2), PYVAL_FALSE);

	k3d::blobby::subtract* sub = new k3d::blobby::subtract(dynamic_cast<k3d::blobby::opcode*>(Interface(py_opcode1)), dynamic_cast<k3d::blobby::opcode*>(Interface(py_opcode2)));
	return Create(sub);
}

PyObject* python_blobby_opcode::div_operation(PyObject* self, PyObject* args)
{
	PyObject* py_opcode1;
	PyObject* py_opcode2;
	return_val_if_fail(PyArg_ParseTuple(args, "OO", &py_opcode1, &py_opcode2), PYVAL_FALSE);

	k3d::blobby::divide* div = new k3d::blobby::divide(dynamic_cast<k3d::blobby::opcode*>(Interface(py_opcode1)), dynamic_cast<k3d::blobby::opcode*>(Interface(py_opcode2)));
	return Create(div);
}


