// 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 Ruby
		\author Anders Dahnielson (anders@dahnielson.com)
		\author Romain Behar (romainbehar@yahoo.com)
*/

#include "object_model.h"

#include <k3dsdk/algebra.h>
#include <k3dsdk/command_node.h>
#include <k3dsdk/file_filter.h>
#include <k3dsdk/iapplication.h>
#include <k3dsdk/icommand_tree.h>
#include <k3dsdk/idocument.h>
#include <k3dsdk/igeometry_read_format.h>
#include <k3dsdk/igeometry_write_format.h>
#include <k3dsdk/iobject.h>
#include <k3dsdk/iproperty_collection.h>
#include <k3dsdk/iproperty.h>
#include <k3dsdk/iuser_interface.h>
#include <k3dsdk/iwritable_property.h>
#include <k3dsdk/objects.h>
#include <k3dsdk/scripting.h>
#include <k3dsdk/serialization.h>
#include <k3dsdk/viewport.h>

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

#include <fstream>
#include <iostream>

namespace
{

VALUE ConvertTuple3(const k3d::vector3& Source)
{
	// Converts 3d vector to array
	VALUE arr = rb_ary_new();
	rb_ary_push(arr, DOUBLE_TO_RBVAL(Source[0]));
	rb_ary_push(arr, DOUBLE_TO_RBVAL(Source[1]));
	rb_ary_push(arr, DOUBLE_TO_RBVAL(Source[2]));

	return arr;
}

VALUE ConvertTuple4(const k3d::angle_axis& Source)
{
	// Convert 1+3d vector to array
	VALUE arr = rb_ary_new();
	rb_ary_push(arr, DOUBLE_TO_RBVAL(Source.axis[0]));
	rb_ary_push(arr, DOUBLE_TO_RBVAL(Source.axis[1]));
	rb_ary_push(arr, DOUBLE_TO_RBVAL(Source.axis[2]));
	rb_ary_push(arr, DOUBLE_TO_RBVAL(Source.angle));

	return arr;
}

k3d::vector3 ConvertVector3(VALUE Value)
{
	// Converts array to 3d vector
	k3d::vector3 result;

	if(TYPE(Value) == T_ARRAY)
	{
		result[2] = RBVAL_TO_DOUBLE(rb_ary_pop(Value));
		result[1] = RBVAL_TO_DOUBLE(rb_ary_pop(Value));
		result[0] = RBVAL_TO_DOUBLE(rb_ary_pop(Value));
	}

	return result;
}

k3d::angle_axis ConvertAngleAxis(VALUE Value)
{
	// Convert array to 1+3d vector
	k3d::angle_axis result;

	if(TYPE(Value) == T_ARRAY)
	{
		result.angle = RBVAL_TO_DOUBLE(rb_ary_pop(Value));
		result.axis[0] = RBVAL_TO_DOUBLE(rb_ary_pop(Value));
		result.axis[1] = RBVAL_TO_DOUBLE(rb_ary_pop(Value));
		result.axis[2] = RBVAL_TO_DOUBLE(rb_ary_pop(Value));
		result.angle = k3d::radians(result.angle);
	}

	return result;
}

/// converts a boost::any object to a Ruby value
VALUE 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 RBVAL_TRUE;
			else
				return RBVAL_FALSE;
		}

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

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

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

			VALUE arr = rb_ary_new();
			rb_ary_push(arr, DOUBLE_TO_RBVAL(value[0]));
			rb_ary_push(arr, DOUBLE_TO_RBVAL(value[1]));

			return arr;
		}

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

			VALUE arr = rb_ary_new();
			rb_ary_push(arr, DOUBLE_TO_RBVAL(value[0]));
			rb_ary_push(arr, DOUBLE_TO_RBVAL(value[1]));
			rb_ary_push(arr, DOUBLE_TO_RBVAL(value[2]));

			return arr;
		}

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

			VALUE arr = rb_ary_new();
			rb_ary_push(arr, DOUBLE_TO_RBVAL(value[0]));
			rb_ary_push(arr, DOUBLE_TO_RBVAL(value[1]));
			rb_ary_push(arr, DOUBLE_TO_RBVAL(value[2]));
			rb_ary_push(arr, DOUBLE_TO_RBVAL(value[3]));

			return arr;
		}

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

			VALUE arr = rb_ary_new();
			rb_ary_push(arr, DOUBLE_TO_RBVAL(value.angle));
			rb_ary_push(arr, DOUBLE_TO_RBVAL(value.axis[0]));
			rb_ary_push(arr, DOUBLE_TO_RBVAL(value.axis[1]));
			rb_ary_push(arr, DOUBLE_TO_RBVAL(value.axis[2]));

			return arr;
		}

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

			VALUE arr = rb_ary_new();
			rb_ary_push(arr, DOUBLE_TO_RBVAL(value[0]));
			rb_ary_push(arr, DOUBLE_TO_RBVAL(value[1]));
			rb_ary_push(arr, DOUBLE_TO_RBVAL(value[2]));
			rb_ary_push(arr, INT_TO_RBVAL(value.order));

			return arr;
		}

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

	if(Value.type() == typeid(int))
		return INT_TO_RBVAL(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 RBVAL_FALSE;
}

/// converts a Ruby value to a boost::any object
boost::any convert(VALUE Value, const std::type_info& TargetType)
{
	if(TargetType == typeid(bool))
		{
			return boost::any(RBVAL_TO_INT(Value));
		}

	if(TargetType == typeid(double))
		{
			return boost::any(RBVAL_TO_DOUBLE(Value));
		}

	if(TargetType == typeid(std::string))
		{
			return boost::any(Ruby_To_String(Value));
		}

	if(TargetType == typeid(k3d::vector2))
		{
			if(TYPE(Value) != T_ARRAY)
				return boost::any();

			k3d::vector2 result;

			result[1] = RBVAL_TO_DOUBLE(rb_ary_pop(Value));
			result[0] = RBVAL_TO_DOUBLE(rb_ary_pop(Value));

			return boost::any(result);
		}

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

			k3d::vector3 result;

			result[2] = RBVAL_TO_DOUBLE(rb_ary_pop(Value));
			result[1] = RBVAL_TO_DOUBLE(rb_ary_pop(Value));
			result[0] = RBVAL_TO_DOUBLE(rb_ary_pop(Value));

			return boost::any(result);
		}

	if(TargetType == typeid(k3d::vector4))
		{
			if(TYPE(Value) != T_ARRAY)
				return boost::any();

			k3d::vector4 result;

			result[3] = RBVAL_TO_DOUBLE(rb_ary_pop(Value));
			result[2] = RBVAL_TO_DOUBLE(rb_ary_pop(Value));
			result[1] = RBVAL_TO_DOUBLE(rb_ary_pop(Value));
			result[0] = RBVAL_TO_DOUBLE(rb_ary_pop(Value));

			return boost::any(result);
		}

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

			unsigned int length = RARRAY(Value)->len;
			if(3 == length)
				{
					k3d::euler_angles euler(0, 0, 0, k3d::euler_angles::ZXYstatic);

					euler.n[2] = RBVAL_TO_DOUBLE(rb_ary_pop(Value));
					euler.n[1] = RBVAL_TO_DOUBLE(rb_ary_pop(Value));
					euler.n[0] = RBVAL_TO_DOUBLE(rb_ary_pop(Value));

					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.axis[2] = RBVAL_TO_DOUBLE(rb_ary_pop(Value));
					result.axis[1] = RBVAL_TO_DOUBLE(rb_ary_pop(Value));
					result.axis[0] = RBVAL_TO_DOUBLE(rb_ary_pop(Value));
					result.angle = RBVAL_TO_DOUBLE(rb_ary_pop(Value));

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

					return boost::any(result);
				}
		}

	//if(TargetType == typeid(unsigned long))
	//	{
	//		return boost::any(RBVAL_TO_ULONG(Value));
	//	}

	if(TargetType == typeid(int))
		{
			return boost::any(RBVAL_TO_INT(Value));
		}

	//if(TargetType == typeid(long))
	//	{
	//		return boost::any(RBVAL_TO_LONG(Value));
	//	}

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

} // namespace

/////////////////////////////////////////////////////////////////////////////
// CRubyObject

VALUE CRubyObject::Skeleton::RBClass = Qnil;

void CRubyObject::CreateClass()
{
	Skeleton::RBClass = rb_define_class("ObjectClass", rb_cObject);

	rb_define_method(Skeleton::RBClass, "initialize", (VALUE(*)(...))Owner::initialize, 0);

	rb_define_method(Skeleton::RBClass, "GetDocument", (VALUE(*)(...))Owner::GetDocument, 0);
	rb_define_method(Skeleton::RBClass, "GetName", (VALUE(*)(...))Owner::GetName, 0);
	rb_define_method(Skeleton::RBClass, "SetName", (VALUE(*)(...))Owner::SetName, 1);
	rb_define_method(Skeleton::RBClass, "EditObject", (VALUE(*)(...))Owner::EditObject, 0);
}

VALUE CRubyObject::initialize(VALUE self)
{
	return self;
}

VALUE CRubyObject::GetDocument(VALUE self)
{
	return CRubyDocument::Create(&Interface(self)->document());
}

VALUE CRubyObject::GetName(VALUE self)
{
	return String_To_Ruby(Interface(self)->name());
}

VALUE CRubyObject::SetName(VALUE self, VALUE str_name)
{
	Interface(self)->set_name(Ruby_To_String(str_name));
	return RBVAL_TRUE;
}

VALUE CRubyObject::EditObject(VALUE self)
{
	if(k3d::application().user_interface())
		{
			k3d::application().user_interface()->show(*Interface(self));
			return RBVAL_TRUE;
		}

	return RBVAL_FALSE;
}

/////////////////////////////////////////////////////////////////////////////
// CRubyDocument

VALUE CRubyDocument::Skeleton::RBClass = Qnil;

void CRubyDocument::CreateClass()
{
	Skeleton::RBClass = rb_define_class("DocumentClass", rb_cObject);

	rb_define_method(Skeleton::RBClass, "initialize", (VALUE(*)(...))Owner::initialize, 0);

	rb_define_method(Skeleton::RBClass, "GetApplication", (VALUE(*)(...))Owner::GetApplication, 0);
	rb_define_method(Skeleton::RBClass, "GetPath", (VALUE(*)(...))Owner::GetPath, 0);
	rb_define_method(Skeleton::RBClass, "Import", (VALUE(*)(...))Owner::Import, 2);
	rb_define_method(Skeleton::RBClass, "Export", (VALUE(*)(...))Owner::Export, 2);
	rb_define_method(Skeleton::RBClass, "Save", (VALUE(*)(...))Owner::Save, 1);
	rb_define_method(Skeleton::RBClass, "StartChangeSet", (VALUE(*)(...))Owner::StartChangeSet, 0);
	rb_define_method(Skeleton::RBClass, "FinishChangeSet", (VALUE(*)(...))Owner::FinishChangeSet, 1);
	rb_define_method(Skeleton::RBClass, "RedrawAll", (VALUE(*)(...))Owner::RedrawAll, 0);

	rb_define_method(Skeleton::RBClass, "CreateObject", (VALUE(*)(...))Owner::CreateObject, 1);
	rb_define_method(Skeleton::RBClass, "Objects", (VALUE(*)(...))Owner::Objects, 0);
	rb_define_method(Skeleton::RBClass, "GetObject", (VALUE(*)(...))Owner::GetObject, 1);
	rb_define_method(Skeleton::RBClass, "DeleteObject", (VALUE(*)(...))Owner::DeleteObject, 1);
}

VALUE CRubyDocument::initialize(VALUE self)
{
	return self;
}

VALUE CRubyDocument::GetApplication(VALUE self)
{
	//FIXME: Return Application instance
	return RBVAL_NONE;
}

VALUE CRubyDocument::GetPath(VALUE self)
{
	return String_To_Ruby(Interface(self)->path().native_file_string());
}

VALUE CRubyDocument::Import(VALUE self, VALUE str_file, VALUE str_format)
{
	const std::string filepath = Ruby_To_String(str_file);
	return_val_if_fail(filepath.size(), RBVAL_FALSE);

	const std::string formatname = Ruby_To_String(str_format);

	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(), RBVAL_FALSE);

	return k3d::import_file(*Interface(self), *filter, filepath) ? RBVAL_TRUE : RBVAL_FALSE;
}

VALUE CRubyDocument::Export(VALUE self, VALUE str_file, VALUE str_format)
{
	const std::string filepath = Ruby_To_String(str_file);
	return_val_if_fail(filepath.size(), RBVAL_FALSE);

	const std::string formatname = Ruby_To_String(str_format);

	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(), RBVAL_FALSE);

	return k3d::export_file(*Interface(self), *filter, filepath) ? RBVAL_TRUE : RBVAL_FALSE;
}

VALUE CRubyDocument::Save(VALUE self, VALUE str_file)
{
	Interface(self)->save(Ruby_To_String(str_file));

	return RBVAL_FALSE;
}

VALUE CRubyDocument::StartChangeSet(VALUE self)
{
	k3dStartStateChangeSet(*Interface(self));
	return RBVAL_TRUE;
}

VALUE CRubyDocument::FinishChangeSet(VALUE self, VALUE str_string)
{
	k3dFinishStateChangeSet(*Interface(self), Ruby_To_String(str_string));
	return RBVAL_TRUE;
}

VALUE CRubyDocument::RedrawAll(VALUE self)
{
	k3d::viewport::redraw_all(*Interface(self), k3d::iviewport::ASYNCHRONOUS);
	return RBVAL_TRUE;
}

VALUE CRubyDocument::CreateObject(VALUE self, VALUE str_name)
{
	// Look for object types by name ...
	k3d::factories_t factories(k3d::plugins(Ruby_To_String(str_name)));

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

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

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

VALUE CRubyDocument::Objects(VALUE self)
{
	// Return the set of objects in this collection ...
	const k3d::objects_t& objects(Interface(self)->objects().collection());

	// Convert the set to a Ruby array ...
	VALUE arr = rb_ary_new();
	for(k3d::objects_t::const_iterator object = objects.begin(); object != objects.end(); ++object)
		rb_ary_push(arr, CRubyObject::Create(*object));

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

VALUE CRubyDocument::GetObject(VALUE self, VALUE str_name)
{
	// Look for objects by name ...
	const k3d::objects_t objects(k3d::find_objects(Interface(self)->objects(), Ruby_To_String(str_name)));

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

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

VALUE CRubyDocument::DeleteObject(VALUE self, VALUE str_name)
{
	k3d::iobject* victim = 0;

	if(TYPE(str_name) == T_STRING)
	{
		// Look for our victim by name ...
		const k3d::objects_t objects(k3d::find_objects(Interface(self)->objects(), Ruby_To_String(str_name)));

		if(1 == objects.size());
		victim = *objects.begin();
	}
	else if(TYPE(str_name) == T_DATA)
	{
		// We got a direct reference to the object ...
		victim = CRubyObject::Interface(str_name);
	}

	k3d::delete_objects(*Interface(self), k3d::make_collection<k3d::objects_t>(victim));

	return RBVAL_TRUE;
}

/////////////////////////////////////////////////////////////////////////////
// CRubyCommandNode

VALUE CRubyCommandNode::Skeleton::RBClass = Qnil;

void CRubyCommandNode::CreateClass()
{
	Skeleton::RBClass = rb_define_class("CommandNodeClass", rb_cObject);

	rb_define_method(Skeleton::RBClass, "initialize", (VALUE(*)(...))Owner::initialize, 0);

	rb_define_method(Skeleton::RBClass, "Command", (VALUE(*)(...))Owner::Command, 2);
	rb_define_method(Skeleton::RBClass, "GetNode", (VALUE(*)(...))Owner::GetNode, 1);
	rb_define_method(Skeleton::RBClass, "GetProperty", (VALUE(*)(...))Owner::GetProperty, 1);
	rb_define_method(Skeleton::RBClass, "SetProperty", (VALUE(*)(...))Owner::SetProperty, 2);
	rb_define_method(Skeleton::RBClass, "Children", (VALUE(*)(...))Owner::GetChildren, 0);
	rb_define_method(Skeleton::RBClass, "Properties", (VALUE(*)(...))Owner::GetProperties, 0);
}

VALUE CRubyCommandNode::initialize(VALUE self)
{
	return self;
}

VALUE CRubyCommandNode::Command(VALUE self, VALUE str_command, VALUE str_arguments)
{

	const std::string command = Ruby_To_String(str_command);
	return_val_if_fail(command.size(), RBVAL_FALSE);

	const std::string arguments = Ruby_To_String(str_arguments);

	if (Interface(self)->execute_command(command, arguments))
		return RBVAL_TRUE;

	return RBVAL_FALSE;

}

VALUE CRubyCommandNode::GetNode(VALUE self, VALUE str_name)
{
	const std::string node_name = Ruby_To_String(str_name);
	return_val_if_fail(node_name.size(), RBVAL_FALSE);

	// Test to see if it's a command-node ...
	k3d::icommand_node* const command_node = dynamic_cast<k3d::icommand_node*>(Interface(self));
	if(!command_node)
		return RBVAL_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(node_name == (*child)->command_node_name())
			return CRubyCommandNode::Create(*child);

	return RBVAL_FALSE;
}

VALUE CRubyCommandNode::GetProperty(VALUE self, VALUE str_name)
{
	const std::string property_name = Ruby_To_String(str_name);
	return_val_if_fail(property_name.size(), RBVAL_FALSE);

	// Test to see if it's a property collection ...
	k3d::iproperty_collection* const property_collection = dynamic_cast<k3d::iproperty_collection*>(Interface(self));
	if(!property_collection)
		return RBVAL_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 RBVAL_FALSE;
}

VALUE CRubyCommandNode::SetProperty(VALUE self, VALUE str_name, VALUE argument)
{
	const std::string property_name = Ruby_To_String(str_name);
	return_val_if_fail(property_name.size(), RBVAL_FALSE);

	// Test to see if it's a property collection ...
	k3d::iproperty_collection* const property_collection = dynamic_cast<k3d::iproperty_collection*>(Interface(self));
	if(!property_collection)
		return RBVAL_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(argument, (*property)->type()));
			}

	return RBVAL_TRUE;
}

VALUE CRubyCommandNode::GetChildren(VALUE self)
{
	// Test to see if it's a command-node ...
	k3d::icommand_node* const command_node = dynamic_cast<k3d::icommand_node*>(Interface(self));
	if(!command_node)
		return RBVAL_FALSE;

	// List children names ...
	VALUE arr = rb_ary_new();
	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)
		rb_ary_push(arr, String_To_Ruby((*child)->command_node_name()));

	return arr;
}

VALUE CRubyCommandNode::GetProperties(VALUE self)
{
	// Test to see if it's a property collection ...
	k3d::iproperty_collection* const property_collection = dynamic_cast<k3d::iproperty_collection*>(Interface(self));
	if(!property_collection)
		return RBVAL_FALSE;

	// List node properties ...
	VALUE arr = rb_ary_new();
	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)
		rb_ary_push(arr, String_To_Ruby((*property)->name()));

	return arr;
}

/////////////////////////////////////////////////////////////////////////////
// CRubyUserInterface

VALUE CRubyUserInterface::Skeleton::RBClass = Qnil;

void CRubyUserInterface::CreateClass()
{
	Skeleton::RBClass = rb_define_class("UIClass", rb_cObject);

	rb_define_method(Skeleton::RBClass, "initialize", (VALUE(*)(...))Owner::initialize, 0);

	rb_define_method(Skeleton::RBClass, "BrowserNavigate", (VALUE(*)(...))Owner::BrowserNavigate, 1);
	rb_define_method(Skeleton::RBClass, "Message", (VALUE(*)(...))Owner::Message, 2);
	rb_define_method(Skeleton::RBClass, "QueryMessage", (VALUE(*)(...))Owner::QueryMessage, 3);
	rb_define_method(Skeleton::RBClass, "ErrorMessage", (VALUE(*)(...))Owner::ErrorMessage, 2);
	rb_define_method(Skeleton::RBClass, "GetFilePath", (VALUE(*)(...))Owner::ErrorMessage, 4);
}

VALUE CRubyUserInterface::initialize(VALUE self)
{
	return self;
}

VALUE CRubyUserInterface::BrowserNavigate(VALUE self, VALUE str_url)
{
	Interface(self)->browser_navigate(Ruby_To_String(str_url));

	return RBVAL_TRUE;
}

VALUE CRubyUserInterface::Message(VALUE self, VALUE str_message, VALUE str_title)
{
	Interface(self)->message(Ruby_To_String(str_message), Ruby_To_String(str_title));
	return RBVAL_TRUE;
}

VALUE CRubyUserInterface::QueryMessage(VALUE self, VALUE str_message, VALUE str_title, VALUE arr_buttons)
{
	std::vector<std::string> buttons;
	for(int i = RARRAY(arr_buttons)->len; i > 0; i--)
	{
		buttons.push_back(Ruby_To_String(rb_ary_shift(arr_buttons)));
	}

	return INT_TO_RBVAL(Interface(self)->query_message(Ruby_To_String(str_message).c_str(), Ruby_To_String(str_title).c_str(), 0, buttons));
}

VALUE CRubyUserInterface::ErrorMessage(VALUE self, VALUE str_message, VALUE str_title)
{
	Interface(self)->error_message(Ruby_To_String(str_message), Ruby_To_String(str_title));
	return RBVAL_TRUE;
}

VALUE CRubyUserInterface::GetFilePath(VALUE self, VALUE string_type, VALUE string_prompt, VALUE bool_overwrite, VALUE string_oldpath)
{
	boost::filesystem::path FilePath;
	Interface(self)->get_file_path(Ruby_To_String(string_type), Ruby_To_String(string_prompt), bool_overwrite == RBVAL_TRUE, Ruby_To_String(string_oldpath), FilePath);

	return String_To_Ruby(FilePath.native_file_string());
}

/////////////////////////////////////////////////////////////////////////////
// CRubyScriptEngines

VALUE CRubyScriptEngines::RBClass = Qnil;

void CRubyScriptEngines::CreateClass()
{
	RBClass = rb_define_class("ScriptEnginesClass", rb_cObject);

	rb_define_method(RBClass, "initialize", (VALUE(*)(...))initialize, 0);

	rb_define_method(RBClass, "playFile", (VALUE(*)(...))PlayFile, 1);
}

VALUE CRubyScriptEngines::initialize(VALUE self)
{
	return self;
}

VALUE CRubyScriptEngines::PlayFile(VALUE self, VALUE string_filename)
{
	const std::string filepath = Ruby_To_String(string_filename);

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

	if(recognized && executed)
		return RBVAL_TRUE;

	return RBVAL_FALSE;
}

/////////////////////////////////////////////////////////////////////////////
// CRubyApplication

VALUE CRubyApplication::RBModule = Qnil;

void CRubyApplication::CreateModule()
{
	RBModule = rb_define_module("Application");

	rb_define_module_function(RBModule, "initialize", (VALUE(*)(...))initialize, 0);

	rb_define_module_function(RBModule, "UI", (VALUE(*)(...))GetUI, 0);
	rb_define_module_function(RBModule, "ScriptEngine", (VALUE(*)(...))GetScripts, 0);
	rb_define_module_function(RBModule, "Documents", (VALUE(*)(...))GetDocuments, 0);
	rb_define_module_function(RBModule, "ShaderCachePath", (VALUE(*)(...))GetShaderCachePath, 0);
	rb_define_module_function(RBModule, "SharePath", (VALUE(*)(...))GetSharePath, 0);
	rb_define_module_function(RBModule, "Close", (VALUE(*)(...))Close, 0);
	rb_define_module_function(RBModule, "NewDocument", (VALUE(*)(...))NewDocument, 0);
	rb_define_module_function(RBModule, "OpenDocument", (VALUE(*)(...))OpenDocument, 1);
	rb_define_module_function(RBModule, "CloseDocument", (VALUE(*)(...))CloseDocument, 1);
	rb_define_module_function(RBModule, "CommandNode", (VALUE(*)(...))CommandNode, 1);
}

VALUE CRubyApplication::initialize(VALUE self)
{
	return self;
}

VALUE CRubyApplication::GetUI(VALUE self)
{
	if(k3d::application().user_interface())
		return CRubyUserInterface::Create(k3d::application().user_interface());

	return RBVAL_NONE;
}

VALUE CRubyApplication::GetScripts(VALUE self)
{
	return RBVAL_NONE;
}

VALUE CRubyApplication::GetDocuments(VALUE self)
{
	// Convert the set of open documents to a Ruby array ...
	VALUE arr = rb_ary_new();

	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)
		rb_ary_push(arr, CRubyDocument::Create(*document));

	return arr;
}

VALUE CRubyApplication::GetShaderCachePath(VALUE self)
{
	return String_To_Ruby(k3d::application().shader_cache_path().native_file_string());
}

VALUE CRubyApplication::GetSharePath(VALUE self)
{
	return String_To_Ruby(k3d::application().share_path().native_file_string());
}

VALUE CRubyApplication::Close(VALUE self)
{
	return RBVAL_FALSE;
}

VALUE CRubyApplication::NewDocument(VALUE self)
{
	k3d::idocument* const document = k3d::application().create_document();
	if(document)
		return CRubyDocument::Create(document);

	return RBVAL_NONE;
}

VALUE CRubyApplication::OpenDocument(VALUE self, VALUE str_file)
{
	k3d::idocument* const document = k3d::application().open_document(Ruby_To_String(str_file));
	if(document)
		return CRubyDocument::Create(document);

	return RBVAL_NONE;
}

VALUE CRubyApplication::CloseDocument(VALUE self, VALUE doc_document)
{
	k3d::application().close_document(*CRubyDocument::Interface(doc_document));

	return RBVAL_NONE;
}

VALUE CRubyApplication::CommandNode(VALUE self, VALUE str_nodepath)
{
	std::string nodepath = Ruby_To_String(str_nodepath);
	if(0 == nodepath.size())
	{
		std::cerr << "Empty command node path" << std::endl;
		return RBVAL_FALSE;
	}

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

	return CRubyCommandNode::Create(node);
}


