// 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 the RIBReader K-3D object, which reads RenderMan .rib files
		\author Romain Behar (romainbehar@yahoo.com)
*/

#include <k3dsdk/algebra.h>
#include <k3dsdk/classes.h>
#include <k3dsdk/color.h>
#include <k3dsdk/file_helpers.h>
#include <k3dsdk/idag.h>
#include <k3dsdk/ideletable.h>
#include <k3dsdk/idocument.h>
#include <k3dsdk/idocument_plugin_factory.h>
#include <k3dsdk/ifile_format.h>
#include <k3dsdk/igeometry_read_format.h>
#include <k3dsdk/imaterial.h>
#include <k3dsdk/imaterial_collection.h>
#include <k3dsdk/iobject.h>
#include <k3dsdk/iobject_collection.h>
#include <k3dsdk/irenderman.h>
#include <k3dsdk/itransform_sink.h>
#include <k3dsdk/itransform_source.h>
#include <k3dsdk/material.h>
#include <k3dsdk/mesh.h>
#include <k3dsdk/module.h>
#include <k3dsdk/plugins.h>
#include <k3dsdk/property.h>
#include <k3dsdk/result.h>
#include <k3dsdk/selection.h>
#include <k3dsdk/string_modifiers.h>
#include <k3dsdk/utility.h>
#include <k3dsdk/vectors.h>

#include "helpers.h"

#include <Hapy/Parser.h>
#include <Hapy/Rules.h>

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

#include <iostream>
#include <map>
#include <stack>

namespace libk3dgeometry
{

using namespace Hapy;

Rule rtint;
Rule rtfloat;
Rule rtstring;
Rule rtint_array;
Rule rtfloat_array;
Rule rtstring_array;

Rule rtpair;
Rule rtparameters;

Rule version;
Rule file;
Rule request_property;
Rule request_object;
Rule request;

Rule camera;
Rule frame;
Rule world;
Rule transform;
Rule attribute;
Rule solid;
Rule object;
Rule motion;
Rule shader;
Rule geometry;
Rule transformation;
Rule errors;
Rule textures;

Rule comment;
Rule comment_body;

static bool hapy_rib_parser = false;

void create_parser()
{
	if(hapy_rib_parser)
		return;

	hapy_rib_parser = true;

	rtint = !char_r('-') >> +digit_r;
	rtint.leaf(true);
	rtint.verbatim(true);
	rtint.committed(true);

	// Make sure rtfloat does not match empty string or '.' but does match 1e2
	rtfloat = !char_r('-') >> (+digit_r >> '.' >> *digit_r | !char_r('.') >> +digit_r) >> !('e' >> rtint);
	rtfloat.leaf(true);
	rtfloat.verbatim(true);
	rtfloat.committed(true);

	rtstring = quoted_r(anychar_r, '"');
	rtstring.leaf(true);
	rtstring.verbatim(true);
	rtstring.committed(true);

	rtint_array = quoted_r(rtint, '[', ']');
	rtfloat_array = quoted_r(rtfloat, '[', ']');
	rtstring_array = quoted_r(rtstring, '[', ']');

	rtpair = rtstring >> rtint
		| rtstring >> rtint_array
		| rtstring >> rtfloat
		| rtstring >> rtfloat_array
		| rtstring >> rtstring
		| rtstring >> rtstring_array
		;

	rtparameters = *rtpair;

	version = "version" >> rtfloat;

	file = *comment >> !version >> *request;


	request_property =
		"Attribute" >> rtstring >> rtparameters
		| "Bound" >> ((rtfloat >> rtfloat >> rtfloat >> rtfloat >> rtfloat >> rtfloat) | rtfloat_array)
		| "Color" >> ((rtfloat >> rtfloat >> rtfloat) | rtfloat_array)
		| "ColorSamples" >> rtfloat_array >> rtfloat_array
		| "Declare" >> rtstring >> rtstring
		| "DetailRange" >> ((rtfloat >> rtfloat >> rtfloat >> rtfloat) | rtfloat_array)
		| "Detail" >> ((rtfloat >> rtfloat >> rtfloat >> rtfloat >> rtfloat >> rtfloat) | rtfloat_array)
		| "Display" >> rtstring >> rtstring >> rtstring >> rtparameters
		| "GeometricApproximation" >> rtstring >> (rtfloat | rtfloat_array)
		| "Illuminate" >> rtint >> rtint
		| "Matte" >> rtint
		| "ObjectInstance" >> rtint
		| "Opacity" >> ((rtfloat >> rtfloat >> rtfloat) | rtfloat_array)
		| "Option" >> rtstring >> rtparameters
		| "Orientation" >> rtstring
		| "RelativeDetail" >> rtfloat
		| "ReverseOrientation"
		| "Sides" >> rtint
		| "TextureCoordinates" >> ((rtfloat >> rtfloat >> rtfloat >> rtfloat >> rtfloat >> rtfloat) | rtfloat_array)
		;

	request_object = comment
		| attribute
		| camera
		| errors
		| frame
		| geometry
		| motion
		| object
		| shader
		| solid
		| transform
		| transformation
		| world
		;

	request = request_property | request_object;
	// HAPY : cannot commit request because Points is a prefix of PointsPolygons
	// a "better" grammar would terminate every command/request with ';'
	// or equivalent so that commands can be isolated
	//request.committed(true);

	camera = "Clipping" >> rtfloat >> rtfloat
		| "CropWindow" >> rtfloat >> rtfloat >> rtfloat >> rtfloat
		| "DepthOfField" >> rtfloat >> rtfloat >> rtfloat
		| "Exposure" >> rtfloat >> rtfloat
		| "Format" >> rtint >> rtint >> rtfloat
		| "FrameAspectRatio" >> rtfloat
		| "Hider" >> rtstring >> rtparameters
		| "PixelFilter" >> rtstring >> rtfloat >> rtfloat
		| "PixelSamples" >> rtfloat >> rtfloat
		| "PixelVariance" >> rtfloat
		| "Projection" >> rtstring >> rtparameters
		| "Quantize" >> rtstring >> rtint >> rtint >> rtint >> rtfloat
		| "ScreenWindow" >> rtfloat >> rtfloat >> rtfloat >> rtfloat
		| "ShadingInterpolation" >> rtstring
		| "ShadingRate" >> rtfloat
		| "Shutter" >> rtfloat >> rtfloat
		;

	frame = "FrameBegin" >> rtint
		| "FrameEnd"
		;

	world = "WorldBegin" >> *request >> "WorldEnd"
		;

	transform = "TransformBegin" >> *request >> "TransformEnd"
		;

	attribute = "AttributeBegin" >> *request >> "AttributeEnd"
		;

	solid = "SolidBegin" >> rtstring
		| "SolidEnd"
		;

	object = "ObjectBegin" >> rtint
		| "ObjectEnd"
		;

	motion = "MotionBegin" >> rtfloat_array
		| "MotionEnd"
		;

	shader = "AreaLightSource" >> rtstring >> rtint >> rtparameters
		| "Atmosphere" >> rtstring >> rtparameters
		| "Deformation" >> rtstring >> rtparameters
		| "Displacement" >> rtstring >> rtparameters
		| "Exterior" >> rtstring >> rtparameters
		| "Imager" >> rtstring >> rtparameters
		| "Interior" >> rtstring >> rtparameters
		| "LightSource" >> rtstring >> rtint >> rtparameters
		| "Surface" >> rtstring >> rtparameters
		;

	geometry = "Polygon" >> rtparameters
		| "GeneralPolygon" >> rtint_array >> rtparameters
		| "Curves" >> rtstring >> rtint_array >> rtstring >> rtparameters
		| "Blobby" >> rtint >> rtint_array >> rtfloat_array >> rtstring_array >> rtparameters
		| "Procedural" >> rtstring >> rtstring >> rtfloat_array >> rtparameters
		| "Points" >> rtparameters
		| "PointsPolygons" >> rtint_array >> rtint_array >> rtparameters
		| "PointsGeneralPolygons" >> rtint_array >> rtint_array >> rtint_array >> rtparameters
		// TODO RiBasis
		| "Patch" >> rtstring >> rtparameters
		| "PatchMesh" >> rtstring >> rtint >> rtstring >> rtint >> rtstring >> rtparameters
		| "NuPatch" >> rtint >> rtint >> rtfloat_array >> rtfloat >> rtfloat >> rtint >> rtint >> rtfloat_array >> rtfloat >> rtfloat >> rtparameters
		| "TrimCurve" >> rtint_array >> rtint_array >> rtfloat_array >> rtfloat_array >> rtfloat_array >> rtint_array >> rtfloat_array >> rtfloat_array >> rtfloat_array
		| "Sphere" >> rtfloat >> rtfloat >> rtfloat >> rtfloat >> rtparameters
		| "Cone" >> rtfloat >> rtfloat >> rtfloat >> rtparameters
		| "Cylinder" >>  rtfloat >> rtfloat >> rtfloat >> rtfloat >> rtparameters
		| "Hyperboloid" >> ((rtfloat >> rtfloat >> rtfloat >> rtfloat >> rtfloat >> rtfloat >> rtfloat) | rtfloat_array) >> rtparameters
		| "Paraboloid" >> rtfloat >> rtfloat >> rtfloat >> rtfloat >> rtparameters
		| "Disk" >> rtfloat >> rtfloat >> rtfloat >> rtparameters
		| "Torus" >> rtfloat >> rtfloat >> rtfloat >> rtfloat >> rtfloat >> rtparameters
		| "Geometry" >> rtstring >> rtparameters
		| "SubdivisionMesh" >> ( rtstring >> rtint_array >> rtint_array | rtstring >> rtint_array >> rtint_array >> rtstring_array >> rtint_array >> rtint_array >> rtfloat_array) >> rtparameters
		;

	transformation = "Identity"
		| "Transform" >> rtfloat_array
		| "ConcatTransform" >> rtfloat_array
		| "Perspective" >> rtfloat
		| "Translate" >> rtfloat >> rtfloat >> rtfloat
		| "Rotate" >> rtfloat >> rtfloat >> rtfloat >> rtfloat
		| "Scale" >> rtfloat >> rtfloat >> rtfloat
		| "Skew" >> ((rtfloat >> rtfloat >> rtfloat >> rtfloat >> rtfloat >> rtfloat >> rtfloat) | rtfloat_array)
		| "CoordinateSystem" >> rtstring
		| "CoordSysTransform" >> rtstring
		| "TransformPoints"
		;

	errors = "ErrorHandler" >> rtstring
		| "ErrorIgnore"
		| "ErrorPrint"
		| "ErrorAbort"
		;

	textures = "MakeTexture" >> *rtstring >> rtfloat >> rtfloat >> rtparameters
		| "MakeBump" >> *rtstring >> rtfloat >> rtfloat >> rtparameters
		| "MakeLatLongEnvironment" >> *rtstring >> rtfloat >> rtfloat >> rtparameters
		| "MakeCubeFaceEnvironment" >> *rtstring >> rtfloat >> rtstring >> rtfloat >> rtfloat >> rtparameters
		| "MakeShadow" >> rtstring >> rtstring >> rtparameters
		;

	// Comment without backtracking
	//comment = quoted_r(anychar_r, '#', eol_r);
	comment = '#' >> comment_body >> eol_r;
	comment_body = *(anychar_r - eol_r);
	comment_body.committed(true);
	comment.verbatim(true);
}

// Helper functions
const std::string no_quotes(const std::string& s)
{
	if(s[0] == '"' && s[s.size() - 1] == '"')
		return std::string(s.begin() + 1, s.end() - 1);

	return s;
}

k3d::vector3 mult_scales(const k3d::vector3& v1, const k3d::vector3& v2)
{
	return k3d::vector3(v1[0] * v2[0], v1[1] * v2[1], v1[2] * v2[2]);
}

typedef std::vector<k3d::vector3> coordinates_t;

bool create_polygon(coordinates_t& points, k3d::mesh* mesh)
{
	return_val_if_fail(mesh, false);

	k3d::polyhedron::edges_t edges;
	coordinates_t::const_iterator point = points.begin();
	for(; point != points.end(); point++)
		{
			k3d::point* new_point = new k3d::point(*point);
			return_val_if_fail(new_point, false);

			mesh->points.push_back(new_point);
			edges.push_back(new k3d::split_edge(new_point));
		}

	k3d::loop_edges(edges.begin(), edges.end());

	k3d::polyhedron* polyhedron = new k3d::polyhedron();
	return_val_if_fail(polyhedron, false);

	polyhedron->edges.insert(polyhedron->edges.end(), edges.begin(), edges.end());

	k3d::face* const face = new k3d::face(*edges.begin());
	return_val_if_fail(face, false);

	polyhedron->faces.push_back(face);
	mesh->polyhedra.push_back(polyhedron);

	return true;
}

/////////////////////////////////////////////////////////////////////////////
// rib_reader_implementation

class rib_reader_implementation :
	public k3d::ifile_format,
	public k3d::igeometry_read_format,
	public k3d::ideletable
{
public:
	rib_reader_implementation()
	{
		m_inside_world = false;

		m_current_mesh = 0;
		m_current_polyhedron = 0;
		m_current_linear_curve_group = 0;

		m_is_solid = false;

		m_transform_number = 0;

		// Init parser ...
		create_parser();
		file.trim(*Hapy::space_r);
		parser.grammar(file);
	}

	unsigned long priority()
	{
		return 0;
	}

	bool query_can_handle(const boost::filesystem::path& FilePath)
	{
		return "rib" == k3d::file_extension(FilePath);
	}

	bool pre_read(k3d::idocument& Document, const boost::filesystem::path& FilePath)
	{
		return true;
	}

	bool read_options(k3d::idocument& Document, const boost::filesystem::path& FilePath)
	{
		return true;
	}

	bool read_file(k3d::idocument& Document, const boost::filesystem::path& FilePath);

	k3d::iplugin_factory& factory()
	{
		return get_factory();
	}

	static k3d::iplugin_factory& get_factory()
	{
		static k3d::plugin_factory<k3d::application_plugin<rib_reader_implementation>, k3d::interface_list<k3d::igeometry_read_format> > factory(
			k3d::uuid(0x9a392c01, 0x50234b23, 0xa61245ff, 0x9345ce15),
			"RIBReader",
			"RenderMan ( .rib )",
			"");

		return factory;
	}

private:
	// Parser
	Hapy::Parser parser;

	std::map<std::string, k3d::imaterial*> m_materials;

	// Properties
	std::map<std::string, std::string> m_declarations;
	k3d::color m_current_color;
	k3d::color m_current_opacity;
	k3d::ri::integer m_current_matte;

	// Frame parameters
	bool m_inside_world;
	k3d::matrix4 frame_transformation;
	k3d::vector3 frame_translation;
	k3d::matrix4 frame_rotation;
	k3d::vector3 frame_scaling;

	// Transformation stacks
	unsigned long m_transform_number;
	std::stack<std::string> begin_end_stack;
	std::vector<k3d::matrix4> m_transformations;
	std::vector<k3d::vector3> m_translations;
	std::vector<k3d::matrix4> m_rotations;
	std::vector<k3d::vector3> m_scalings;

	bool m_is_solid;
	std::string m_solid_type;

	// Mesh variables and functions
	std::stack<k3d::iobject*> m_transform_objects;
	k3d::mesh* m_current_mesh;
	std::string m_current_name;
	k3d::iobject* m_current_mesh_object;
	k3d::iobject* m_current_mesh_instance;
	k3d::nupatch* m_current_nupatch;
	k3d::bilinear_patch* m_current_bilinear_patch;
	k3d::polyhedron* m_current_polyhedron;
	k3d::linear_curve_group* m_current_linear_curve_group;
	k3d::iobject* m_current_material;
	k3d::iobject* m_current_shader;

	bool create_mesh(k3d::idocument& Document)
	{
		// Create document object ...
		k3d::mesh* const mesh = detail::create_mesh(Document, "RIB mesh", m_current_mesh_object, m_current_mesh_instance);
		return_val_if_fail(mesh, false);
		m_current_mesh = mesh;

		return true;
	}

	bool create_nupatch(k3d::idocument& Document)
	{
		return_val_if_fail(create_mesh(Document), false);

		k3d::nupatch* nupatch = new k3d::nupatch();
		return_val_if_fail(nupatch, false);

		m_current_mesh->nupatches.push_back(nupatch);

		m_current_nupatch = nupatch;

		return true;
	}

	bool create_polyhedron(k3d::idocument& Document)
	{
		return_val_if_fail(create_mesh(Document), false);

		k3d::polyhedron* polyhedron = new k3d::polyhedron();
		return_val_if_fail(polyhedron, false);

		m_current_mesh->polyhedra.push_back(polyhedron);

		m_current_polyhedron = polyhedron;

		return true;
	}

	bool create_bilinear_patch(k3d::idocument& Document)
	{
		return_val_if_fail(create_mesh(Document), false);

		k3d::bilinear_patch* bpatch = new k3d::bilinear_patch();
		return_val_if_fail(bpatch, false);

		m_current_mesh->bilinear_patches.push_back(bpatch);

		m_current_bilinear_patch = bpatch;

		return true;
	}

	bool create_linear_curve_group(k3d::idocument& Document)
	{
		return_val_if_fail(create_mesh(Document), false);

		k3d::linear_curve_group* const curve_group = new k3d::linear_curve_group();
		return_val_if_fail(curve_group, false);

		m_current_mesh->linear_curve_groups.push_back(curve_group);

		m_current_linear_curve_group = curve_group;

		return true;
	}

	bool create_transform(k3d::idocument& Document)
	{
		k3d::iobject* const transform = k3d::create_document_plugin(k3d::classes::Transform(), Document);
		return_val_if_fail(transform, false);

		transform->set_name("Transform " + k3d::to_string(++m_transform_number));
		connect_transforms(transform, Document);
		m_transform_objects.push(transform);

		return true;
	}

	bool connect_transforms(k3d::iobject* Object, k3d::idocument& Document)
	{
		if(m_transform_objects.empty())
			return false;

		k3d::iobject* const last_transform = m_transform_objects.top();
		k3d::itransform_source* const transform_source = dynamic_cast<k3d::itransform_source*>(last_transform);
		return_val_if_fail(transform_source, false);

		k3d::itransform_sink* const transform_sink = dynamic_cast<k3d::itransform_sink*>(Object);
		return_val_if_fail(transform_sink, false);

		k3d::idag::dependencies_t dependencies;
		dependencies.insert(std::make_pair(&transform_sink->transform_sink_input(), &transform_source->transform_source_output()));
		Document.dag().set_dependencies(dependencies);

		return true;
	}

	k3d::imaterial* assign_current_material(k3d::idocument& Document)
	{
		if(m_current_material)
			{
				k3d::imaterial* current = dynamic_cast<k3d::imaterial*>(m_current_material);
				if(current)
					return current;
			}

		k3d::imaterial* default_material = dynamic_cast<k3d::imaterial*>(k3d::default_material(Document));
		return default_material;
	}

	void parse_subpree(const Hapy::Pree& Node, k3d::idocument& Document);

	void get_rtint_array(const Hapy::Pree& IntArray, std::vector<unsigned long>& Ints)
	{
		assert_warning(IntArray.rid() == rtint_array.id());

		for(Hapy::Pree::const_iterator node = IntArray.begin(); node != IntArray.end(); node++)
		{
			if(node->image() == "[" || node->image() == "]")
				continue;

			for(Hapy::Pree::const_iterator i = (*node).begin(); i != (*node).end(); i++)
				Ints.push_back(k3d::from_string<unsigned long>(i->image(), 0));
		}
	}

	void get_rtfloat_array(const Hapy::Pree& FloatArray, std::vector<double>& Floats)
	{
		assert_warning(FloatArray.rid() == rtfloat_array.id());

		for(Hapy::Pree::const_iterator node = FloatArray.begin(); node != FloatArray.end(); node++)
		{
			if(node->image() == "[" || node->image() == "]")
				continue;

			for(Hapy::Pree::const_iterator i = (*node).begin(); i != (*node).end(); i++)
				Floats.push_back(k3d::from_string<double>(i->image(), 0));
		}
	}

	void get_rtfloat_array(const Hapy::Pree& FloatArray, coordinates_t& Points)
	{
		assert_warning(FloatArray.rid() == rtfloat_array.id() || FloatArray.rid() == rtint_array.id());

		double x;
		double y;
		unsigned long n = 0;;
		for(Hapy::Pree::const_iterator node = FloatArray.begin(); node != FloatArray.end(); node++)
		{
			if(node->image() == "[" || node->image() == "]")
				continue;

			for(Hapy::Pree::const_iterator i = (*node).begin(); i != (*node).end(); i++)
				{
					double value = k3d::from_string<double>(i->image(), 0);
					if(n == 0)
						x = value;
					else if(n == 1)
						y = value;
					else
						Points.push_back(k3d::vector3(x, y, value));

					n = (n + 1) % 3;
				}
		}

		assert_warning(n == 0);
	}

	void get_rtfloat_matrix(const Hapy::Pree& FloatArray, k3d::matrix4& Matrix)
	{
		std::vector<double> v;
		get_rtfloat_array(FloatArray, v);

		Matrix = k3d::matrix4(
			k3d::vector4(v[0], v[1], v[2], v[3]),
			k3d::vector4(v[4], v[5], v[6], v[7]),
			k3d::vector4(v[8], v[9], v[10], v[11]),
			k3d::vector4(v[12], v[13], v[14], v[15]));

		Matrix = Matrix.Transpose();
	}

	void set_current_transformations(k3d::iobject* Object)
	{
		assert_warning(k3d::set_property_value(*Object, "position", k3d::vector3(*m_translations.rbegin() + extractTranslation(*m_transformations.rbegin()))));
		assert_warning(k3d::set_property_value(*Object, "orientation", k3d::angle_axis(k3d::rotation3D(*m_rotations.rbegin() * *m_transformations.rbegin()))));
		assert_warning(k3d::set_property_value(*Object, "scale", k3d::vector3(mult_scales(*m_scalings.rbegin(), k3d::extractScaling(*m_transformations.rbegin())))));
	}

	void rib_stack_push(const std::string& name, k3d::idocument& Document)
	{
		// Save object type and stack a generic object ...
		begin_end_stack.push(name);

		if(name == "Frame")
			{
				frame_translation = k3d::vector3(0, 0, 0);
				frame_rotation = k3d::identity3D();
				frame_scaling = k3d::vector3(1, 1, 1);
				m_inside_world = false;
			}
		else if(name == "World")
			{
				m_inside_world = true;

				m_transformations.push_back(k3d::identity3D());
				m_translations.push_back(k3d::vector3(0, 0, 0));
				m_rotations.push_back(k3d::identity3D());
				m_scalings.push_back(k3d::vector3(1, 1, 1));
			}

		if(m_inside_world)
			{
				// Save current transformation ...
				if(m_transform_objects.size())
					set_current_transformations(m_transform_objects.top());

				// Push new transformation ...
				create_transform(Document);

				m_transformations.push_back(k3d::identity3D());
				m_translations.push_back(k3d::vector3(0, 0, 0));
				m_rotations.push_back(k3d::identity3D());
				m_scalings.push_back(k3d::vector3(1, 1, 1));
			}
	}

	void rib_stack_pop(const std::string& name)
	{
		assert_warning(begin_end_stack.top() == name);
		begin_end_stack.pop();

		if(m_inside_world)
			{
				m_transformations.pop_back();
				m_translations.pop_back();
				m_rotations.pop_back();
				m_scalings.pop_back();

				m_transform_objects.pop();
			}

		if(name == "World")
			m_inside_world = false;
	}

	void set_object_properties(k3d::iobject* Object, const std::string& Name, k3d::idocument& Document)
	{
		assert_warning(Object);

		// Set name, parent transformer and current transformations
		Object->set_name(m_current_name + "  " + Name);
		connect_transforms(Object, Document);
		set_current_transformations(Object);
	}

	k3d::matrix4 get_blobby_matrix(std::vector<double>::iterator First)
	{
		k3d::matrix4 m;

		m[0] = k3d::vector4(First[0], First[1], First[2], First[3]);
		First += 4;
		m[1] = k3d::vector4(First[0], First[1], First[2], First[3]);
		First += 4;
		m[2] = k3d::vector4(First[0], First[1], First[2], First[3]);
		First += 4;
		m[3] = k3d::vector4(First[0], First[1], First[2], First[3]);

		return m.Transpose();
	}

	// Debug
	void Childs(const Hapy::Pree& Node)
	{
		for(Hapy::Pree::const_iterator node = Node.begin(); node != Node.end(); node++)
			std::cerr << debug << "Node child : " << node->rid() << " -> " << node->image() << std::endl;
	}
};

void rib_reader_implementation::parse_subpree(const Hapy::Pree& Node, k3d::idocument& Document)
{
	if(Node.begin() == Node.end())
		return;

	if(Node[0].rid() == request_property.id())
	{
		Hapy::Pree::const_iterator content_node = (Node[0]).begin();
		if(content_node->image() == "ReverseOrientation")
		{
		}
		else
		{
			content_node = content_node->begin();
			if(content_node->image() == "Attribute")
			{
				++content_node;
				if(content_node->image() == "\"identifier\"")
				{
					++content_node;
					// rtparameters
					content_node = content_node->begin();
					// rtpair
					content_node = content_node->begin();
					// string + string
					content_node = content_node->begin();
					if(content_node->image() == "\"name\"")
					{
						++content_node;
						// string array
						content_node = content_node->begin();
						++content_node;
						m_current_name = content_node->image();
					}
				}
			}
			else
			if(content_node->image() == "Color")
			{
				++content_node;
				if(content_node->begin()->rid() == rtfloat_array.id())
					content_node = content_node->begin()->begin() + 1;

				m_current_color = k3d::from_string<k3d::color>(content_node->image(), k3d::color(0, 0, 0));
			}
			else
			if(content_node->image() == "Declare")
			{
				++content_node;
				assert_warning(content_node->rid() == rtstring.id());
				std::string variable = content_node->image();
				++content_node;
				assert_warning(content_node->rid() == rtstring.id());
				m_declarations[no_quotes(variable)] = no_quotes(content_node->image());
			}
			else
			if(content_node->image() == "Matte")
			{
				++content_node;
				m_current_matte = k3d::from_string<k3d::ri::integer>(content_node->image(), 0);
			}
			else
			if(content_node->image() == "Opacity")
			{
				++content_node;
				if(content_node->begin()->rid() == rtfloat_array.id())
					content_node = content_node->begin()->begin() + 1;

				m_current_opacity = k3d::from_string<k3d::color>(content_node->image(), k3d::color(0, 0, 0));
			}
		}
	}
	else
	if(Node[0].rid() == frame.id())
	{
		Hapy::Pree::const_iterator frame_node = (Node[0]).begin();
		if(frame_node->image() == "FrameEnd")
		{
		}
		else
		{
			frame_node = frame_node->begin();
			assert_warning(frame_node->image() == "FrameBegin");

			rib_stack_push("Frame", Document);

			frame_node++;
			assert_warning(frame_node->rid() == rtint.id());
			k3d::from_string<k3d::ri::integer>(frame_node->image(), 0);
		}
	}
	else
	if(Node[0].rid() == world.id())
	{
		Hapy::Pree::const_iterator world_node = (Node[0]).begin();
		assert_warning(world_node->image() == "WorldBegin");

		rib_stack_push("World", Document);

		world_node++;
		parse_subpree(*world_node, Document);

		rib_stack_pop("World");

		world_node++;
		assert_warning(world_node->image() == "WorldEnd");
	}
	else
	if(Node[0].rid() == transform.id())
	{
		Hapy::Pree::const_iterator transform_node = (Node[0]).begin();
		assert_warning(transform_node->image() == "TransformBegin");

		rib_stack_push("Transform", Document);

		transform_node++;
		parse_subpree(*transform_node, Document);

		rib_stack_pop("Transform");

		transform_node++;
		assert_warning(transform_node->image() == "TransformEnd");
	}
	else
	if(Node[0].rid() == attribute.id())
	{
		Hapy::Pree::const_iterator attribute_node = (Node[0]).begin();
		assert_warning(attribute_node->image() == "AttributeBegin");

		rib_stack_push("Attribute", Document);

		attribute_node++;
		parse_subpree(*attribute_node, Document);

		rib_stack_pop("Attribute");

		attribute_node++;
		assert_warning(attribute_node->image() == "AttributeEnd");
	}
	else
	if(Node[0].rid() == solid.id())
	{
		Hapy::Pree::const_iterator solid_node = (Node[0]).begin();
		if(solid_node->image() == "SolidEnd")
		{
			m_is_solid = false;
		}
		else
		{
			solid_node = solid_node->begin();
			assert_warning(solid_node->image() == "SolidBegin");

			solid_node++;
			assert_warning(solid_node->rid() == rtstring.id());
			m_solid_type = no_quotes(solid_node->image());

			m_is_solid = true;
			//m_current_object_group = create_object_group(Document);
		}
	}
	else
	if(Node[0].rid() == object.id())
	{
		Hapy::Pree::const_iterator object_node = (Node[0]).begin();
		if(object_node->image() == "ObjectEnd")
		{
		}
		else
		{
			object_node = object_node->begin();
			assert_warning(object_node->image() == "ObjectBegin");

			object_node++;
			assert_warning(object_node->rid() == rtint.id());

			k3d::from_string<k3d::ri::integer>(object_node->image(), 0);
		}
	}
	else
	if(Node[0].rid() == motion.id())
	{
		Hapy::Pree::const_iterator motion_node = (Node[0]).begin();
		if(motion_node->image() == "MotionEnd")
		{
		}
		else
		{
			motion_node = motion_node->begin();
			assert_warning(motion_node->image() == "MotionBegin");

			motion_node++;
			assert_warning(motion_node->rid() == rtfloat_array.id());

		}
	}
	else
	if(Node[0].rid() == geometry.id())
	{
		Hapy::Pree::const_iterator pi = (Node[0]).begin();
		for(; pi != (Node[0]).end(); pi++)
		{
			Hapy::Pree f = (*pi);
			Hapy::Pree::const_iterator pir = f.begin();
			if("NuPatch" == (*pir).image())
			{
				k3d::from_string<long>((++pir)->image(), 0); // long u_npoints
				long u_order = k3d::from_string<long>((++pir)->image(), 0);
				const Hapy::Pree& u_knot_pree = *++pir;
				assert_warning(u_knot_pree.rid() == rtfloat_array.id());
				k3d::from_string<double>((++pir)->image(), 0); // double u_min
				k3d::from_string<double>((++pir)->image(), 0); // double u_max

				k3d::from_string<long>((++pir)->image(), 0); // long v_npoints
				long v_order = k3d::from_string<long>((++pir)->image(), 0);
				const Hapy::Pree& v_knot_pree = *++pir;
				assert_warning(v_knot_pree.rid() == rtfloat_array.id());
				k3d::from_string<double>((++pir)->image(), 0); // double v_min
				k3d::from_string<double>((++pir)->image(), 0); // double v_max

				const Hapy::Pree& parameters = *++pir;
				const Hapy::Pree& pair = *parameters.begin();
				const Hapy::Pree& value = *pair.begin();
				const Hapy::Pree& string = *value.begin();
				const Hapy::Pree& float_array = *(value.begin() + 1);

				// Load arrays ...
				std::vector<double> u_knots;
				get_rtfloat_array(u_knot_pree, u_knots);
				std::vector<double> v_knots;
				get_rtfloat_array(v_knot_pree, v_knots);
				std::vector<double> points;
				get_rtfloat_array(float_array, points);

				// Create a new nupatch
				return_if_fail(create_nupatch(Document));

				// Set orders ...
				m_current_nupatch->u_order = u_order;
				m_current_nupatch->v_order = v_order;

				// Set knots ...
				for(std::vector<double>::const_iterator k = u_knots.begin(); k != u_knots.end(); k++)
					m_current_nupatch->u_knots.push_back(*k);
				for(std::vector<double>::const_iterator k = v_knots.begin(); k != v_knots.end(); k++)
					m_current_nupatch->v_knots.push_back(*k);

				// Add control points ...
				unsigned long pts = points.size();
				assert_warning((string.image() == "\"P\"" && (pts % 3 == 0)) || (string.image() == "\"Pw\"" && (pts % 4 == 0)));

				if(string.image() == "\"P\"")
				{
					for(unsigned long p = 0; p < pts / 3; p++)
					{
						k3d::point* const point = new k3d::point(points[p*3], points[p*3 + 1], points[p*3 + 2]);
						return_if_fail(point);

						m_current_mesh->points.push_back(point);
						m_current_nupatch->control_points.push_back(point);
					}
				}
				else if(string.image() == "\"Pw\"")
				{
					for(unsigned long p = 0; p < pts / 4; p++)
					{
						k3d::point* const point = new k3d::point(points[p*4], points[p*4 + 1], points[p*4 + 2]);
						return_if_fail(point);

						m_current_mesh->points.push_back(point);

						double weight = points[p*4 + 3];
						point->position /= weight ? weight : 1;
						m_current_nupatch->control_points.push_back(k3d::nupatch::control_point(point, weight));
					}
				}

				// Set material and transform properties ...
				m_current_nupatch->material = assign_current_material(Document);
				set_object_properties(m_current_mesh_instance, "NuPatch", Document);
			}
			else if("Patch" == (*pir).image())
			{
				std::string type = no_quotes((++pir)->image());
				if("bilinear" == type)
				{
					create_bilinear_patch(Document);

					const Hapy::Pree& parameters = *++pir;
					const Hapy::Pree& pair = *parameters.begin();
					const Hapy::Pree& value = *pair.begin();
					const Hapy::Pree& string = *value.begin();
					const Hapy::Pree& float_array = *(value.begin() + 1);

					if("P" == no_quotes(string.image()))
					{
						std::vector<double> points;
						get_rtfloat_array(float_array, points);
						assert_warning(4 * 3 == points.size());

						for(unsigned long p = 0; p < 4; p++)
						{
							k3d::point* point = new k3d::point(points[p*3], points[p*3+1], points[p*3+2]);
							m_current_mesh->points.push_back(point);
							m_current_bilinear_patch->control_points[p] = point;
						}
					}
					else
						std::cerr << debug << "RIB reader : unsupported point type for bilinear patch" << std::endl;
				}
				else if("bicubic" == type)
				{
				}
				else
					std::cerr << debug << "Invalid RenderMan Patch type " << type << std::endl;

				// Set material and transform properties ...
				m_current_bilinear_patch->material = assign_current_material(Document);
				set_object_properties(m_current_mesh_instance, "Patch", Document);
			}
			else if("Polygon" == (*pir).image())
			{
				pir++;
				const Hapy::Pree& parameters = *pir++;
				const Hapy::Pree& pair = *parameters.begin();
				const Hapy::Pree& value = *pair.begin();
				const Hapy::Pree& string = *value.begin();
				std::string parameter_name = no_quotes(string.image());
				if(parameter_name == "P")
					{
						return_if_fail(create_mesh(Document));

						const Hapy::Pree& float_array = *(value.begin() + 1);
						coordinates_t points;
						get_rtfloat_array(float_array, points);

						create_polygon(points, m_current_mesh);
						set_object_properties(m_current_mesh_instance, "Polygon", Document);
					}
				//else
					// Find "P"
			}
			else if("PointsGeneralPolygons" == (*pir).image())
			{
				pir++;

				const Hapy::Pree& loop_counts = *pir++;
				const Hapy::Pree& vertex_counts = *pir++;
				const Hapy::Pree& vertex_ids = *pir++;
				const Hapy::Pree& parameters = *pir++;

				assert_warning(loop_counts.rid() == rtint_array.id());
				assert_warning(vertex_counts.rid() == rtint_array.id());
				assert_warning(vertex_ids.rid() == rtint_array.id());
				assert_warning(parameters.rid() == rtparameters.id());

				std::vector<unsigned long> loops;
				std::vector<unsigned long> counts;
				std::vector<unsigned long> ids;

				get_rtint_array(loop_counts, loops);
				get_rtint_array(vertex_counts, counts);
				get_rtint_array(vertex_ids, ids);

				const Hapy::Pree& pair = *parameters.begin();
				const Hapy::Pree& value = *pair.begin();
				//const Hapy::Pree& string = *value.begin();
				const Hapy::Pree& float_array = *(value.begin() + 1);

				std::vector<double> points;
				get_rtfloat_array(float_array, points);

				// Create a new polyhedron
				return_if_fail(create_polyhedron(Document));

				// Add points ...
				unsigned long pts = points.size();
				assert_warning(pts % 3 == 0);

				std::vector<k3d::point*> geometric_points;
				for(unsigned long p = 0; p < pts/ 3; p++)
				{
					k3d::point* const point = new k3d::point(points[p*3], points[p*3 + 1], points[p*3 + 2]);
					return_if_fail(point);

					geometric_points.push_back(point);
					m_current_mesh->points.push_back(point);
				}

				// Create faces ...
				std::vector<unsigned long>::const_iterator current_vertex_count = counts.begin();
				std::vector<unsigned long>::const_iterator current_vertex_id = ids.begin();
				for(std::vector<unsigned long>::const_iterator loop = loops.begin(); loop < loops.end(); loop++)
				{
					unsigned long loop_number = *loop;
					for(unsigned long l = 0; l < loop_number; l++)
					{
						unsigned long n_vertices = *current_vertex_count++;

						k3d::split_edge* previous_edge = 0;
						k3d::face* face = 0;

						for(unsigned long vs = 0; vs < n_vertices; vs++)
							{
								k3d::point* const point = geometric_points[*current_vertex_id++];
								return_if_fail(point);

								k3d::split_edge* edge = new k3d::split_edge(point);
								return_if_fail(edge);

								if(!face)
									{
										face = new k3d::face(edge);
										return_if_fail(face);
										m_current_polyhedron->faces.push_back(face);
									}
								else
									{
										previous_edge->face_clockwise = edge;
									}

								m_current_polyhedron->edges.push_back(edge);
								previous_edge = edge;
							}

						// Close loop
						if(face)
							{
								previous_edge->face_clockwise = face->first_edge;
							}
					}
				}

				// Set material and transform properties ...
				m_current_polyhedron->material = assign_current_material(Document);
				set_object_properties(m_current_mesh_instance, "Polygons", Document);
			}
			else if("Cone" == (*pir).image())
			{
				k3d::iobject* const cone = k3d::create_document_plugin(k3d::classes::Cone(), Document);
				assert_warning(cone);

				k3d::set_property_value(*cone, "height", k3d::from_string<double>((++pir)->image(), 0));
				k3d::set_property_value(*cone, "radius", k3d::from_string<double>((++pir)->image(), 0));
				k3d::set_property_value(*cone, "thetamax", k3d::radians(k3d::from_string<double>((++pir)->image(), 0)));

				k3d::set_property_value(*cone, "material", assign_current_material(Document));
				set_object_properties(cone, "Cone", Document);
			}
			else if("Cylinder" == (*pir).image())
			{
				k3d::iobject* const cylinder = k3d::create_document_plugin(k3d::classes::Cylinder(), Document);
				assert_warning(cylinder);

				k3d::set_property_value(*cylinder, "radius", k3d::from_string<double>((++pir)->image(), 0));
				k3d::set_property_value(*cylinder, "zmin", k3d::from_string<double>((++pir)->image(), 0));
				k3d::set_property_value(*cylinder, "zmax", k3d::from_string<double>((++pir)->image(), 0));
				k3d::set_property_value(*cylinder, "thetamax", k3d::radians(k3d::from_string<double>((++pir)->image(), 0)));

				k3d::set_property_value(*cylinder, "material", assign_current_material(Document));
				set_object_properties(cylinder, "Cylinder", Document);
			}
			else if("Disk" == (*pir).image())
			{
				k3d::iobject* const disk = k3d::create_document_plugin(k3d::classes::Disk(), Document);
				assert_warning(disk);

				k3d::set_property_value(*disk, "height", k3d::from_string<double>((++pir)->image(), 0));
				k3d::set_property_value(*disk, "radius", k3d::from_string<double>((++pir)->image(), 0));
				k3d::set_property_value(*disk, "thetamax", k3d::radians(k3d::from_string<double>((++pir)->image(), 0)));

				k3d::set_property_value(*disk, "material", assign_current_material(Document));
				set_object_properties(disk, "Disk", Document);
			}
			else if("Hyperboloid" == (*pir).image())
			{
				k3d::iobject* const hyperboloid = k3d::create_document_plugin(k3d::classes::Hyperboloid(), Document);
				assert_warning(hyperboloid);

				k3d::set_property_value(*hyperboloid, "p1x", k3d::from_string<double>((++pir)->image(), 0));
				k3d::set_property_value(*hyperboloid, "p1y", k3d::from_string<double>((++pir)->image(), 0));
				k3d::set_property_value(*hyperboloid, "p1z", k3d::from_string<double>((++pir)->image(), 0));
				k3d::set_property_value(*hyperboloid, "p2x", k3d::from_string<double>((++pir)->image(), 0));
				k3d::set_property_value(*hyperboloid, "p2y", k3d::from_string<double>((++pir)->image(), 0));
				k3d::set_property_value(*hyperboloid, "p2z", k3d::from_string<double>((++pir)->image(), 0));
				k3d::set_property_value(*hyperboloid, "thetamax", k3d::radians(k3d::from_string<double>((++pir)->image(), 0)));

				k3d::set_property_value(*hyperboloid, "material", assign_current_material(Document));
				set_object_properties(hyperboloid, "Hyperboloid", Document);
			}
			else if("Paraboloid" == (*pir).image())
			{
				k3d::iobject* const paraboloid = k3d::create_document_plugin(k3d::classes::Paraboloid(), Document);
				assert_warning(paraboloid);

				k3d::set_property_value(*paraboloid, "radius", k3d::from_string<double>((++pir)->image(), 0));
				k3d::set_property_value(*paraboloid, "zmin", k3d::from_string<double>((++pir)->image(), 0));
				k3d::set_property_value(*paraboloid, "zmax", k3d::from_string<double>((++pir)->image(), 0));
				k3d::set_property_value(*paraboloid, "thetamax", k3d::radians(k3d::from_string<double>((++pir)->image(), 0)));

				k3d::set_property_value(*paraboloid, "material", assign_current_material(Document));
				set_object_properties(paraboloid, "Paraboloid", Document);
			}
			else if("Sphere" == (*pir).image())
			{
				k3d::iobject* const sphere = k3d::create_document_plugin(k3d::classes::Sphere(), Document);
				assert_warning(sphere);

				k3d::set_property_value(*sphere, "radius", k3d::from_string<double>((++pir)->image(), 0));
				k3d::set_property_value(*sphere, "zmin", k3d::from_string<double>((++pir)->image(), 0));
				k3d::set_property_value(*sphere, "zmax", k3d::from_string<double>((++pir)->image(), 0));
				k3d::set_property_value(*sphere, "thetamax", k3d::radians(k3d::from_string<double>((++pir)->image(), 0)));

				k3d::set_property_value(*sphere, "material", assign_current_material(Document));
				set_object_properties(sphere, "Sphere", Document);
			}
			else if("Torus" == (*pir).image())
			{
				k3d::iobject* const torus = k3d::create_document_plugin(k3d::classes::Torus(), Document);
				assert_warning(torus);

				k3d::set_property_value(*torus, "majorradius", k3d::from_string<double>((++pir)->image(), 0));
				k3d::set_property_value(*torus, "minorradius", k3d::from_string<double>((++pir)->image(), 0));
				k3d::set_property_value(*torus, "phimin", k3d::radians(k3d::from_string<double>((++pir)->image(), 0)));
				k3d::set_property_value(*torus, "phimax", k3d::radians(k3d::from_string<double>((++pir)->image(), 0)));
				k3d::set_property_value(*torus, "thetamax", k3d::radians(k3d::from_string<double>((++pir)->image(), 0)));

				k3d::set_property_value(*torus, "material", assign_current_material(Document));
				set_object_properties(torus, "Torus", Document);
			}
			else if("Blobby" == (*pir).image())
			{
				/*const Hapy::Pree& nleaf =*/ *++pir;
				const Hapy::Pree& codes_array = *++pir;
				const Hapy::Pree& floats_array = *++pir;
				const Hapy::Pree& names_array = *++pir;
				const Hapy::Pree& parameters = *++pir;

				assert_warning(codes_array.rid() == rtint_array.id());
				assert_warning(floats_array.rid() == rtfloat_array.id());
				assert_warning(names_array.rid() == rtstring_array.id());
				assert_warning(parameters.rid() == rtparameters.id());

				std::vector<unsigned long> codes;
				std::vector<double> floats;

				get_rtint_array(codes_array, codes);
				get_rtfloat_array(floats_array, floats);

				// Populate blobby
				return_if_fail(create_mesh(Document));
				std::vector<k3d::blobby::opcode*> blobbies;

				std::vector<unsigned long>::iterator code = codes.begin();
				unsigned long code_val;
				while(code != codes.end())
				{
					code_val = *code++;
					if(code_val == 1000)
					{
						// Constant
						/*unsigned long values_index =*/ *code++;
					}
					else
					if(code_val == 1001)
					{
						// Ellipsoid
						unsigned long values_index = *code++;

						k3d::matrix4 m = get_blobby_matrix(floats.begin() + values_index);
						k3d::point* p = new k3d::point(m[0][3], m[1][3], m[2][3]);
						m[0][3] = m[1][3] = m[2][3] = 0;
						m_current_mesh->points.push_back(p);

						blobbies.push_back(new k3d::blobby::ellipsoid(p, m));
					}
					else
					if(code_val == 1002)
					{
						// Segment
						unsigned long values_index = *code++;

						k3d::matrix4 m = get_blobby_matrix(floats.begin() + values_index + 7);
						k3d::point* start = new k3d::point(*(floats.begin() + values_index), *(floats.begin() + values_index + 1), *(floats.begin() + values_index + 2));
						k3d::point* end = new k3d::point(*(floats.begin() + values_index + 3), *(floats.begin() + values_index + 4), *(floats.begin() + values_index + 5));
						m_current_mesh->points.push_back(start);
						m_current_mesh->points.push_back(end);

						blobbies.push_back(new k3d::blobby::segment(start, end, *(floats.begin() + values_index + 6), m));
					}
					else
					if(code_val == 1003)
					{
						// Repelling plane
						/*unsigned long values_index =*/ *code++;
						/*unsigned long string_index =*/ *code++;
					}
					else
					if(code_val == 0)
					{
						// Add operator
						unsigned long operators = *code++;

						k3d::blobby::add* add = new k3d::blobby::add();
						blobbies.push_back(add);

						for(unsigned long n = 0; n < operators; n++)
							add->add_operand(blobbies[*code++]);
					}
					else
					if(code_val == 1)
					{
						// Mult operator
						unsigned long operators = *code++;

						k3d::blobby::multiply* mult = new k3d::blobby::multiply();
						blobbies.push_back(mult);

						for(unsigned long n = 0; n < operators; n++)
							mult->add_operand(blobbies[*code++]);
					}
					else
					if(code_val == 2)
					{
						// Max operator
						unsigned long operators = *code++;

						k3d::blobby::max* max = new k3d::blobby::max();
						blobbies.push_back(max);

						for(unsigned long n = 0; n < operators; n++)
							max->add_operand(blobbies[*code++]);
					}
					else
					if(code_val == 3)
					{
						// Min operator
						unsigned long operators = *code++;

						k3d::blobby::min* min = new k3d::blobby::min();
						blobbies.push_back(min);

						for(unsigned long n = 0; n < operators; n++)
							min->add_operand(blobbies[*code++]);
					}
					else
					if(code_val == 4)
					{
						// Substract operator
						unsigned long subtrahend = *code++;
						unsigned long minuend = *code++;
						assert_warning(subtrahend < blobbies.size());
						assert_warning(minuend < blobbies.size());

						blobbies.push_back(new k3d::blobby::subtract(blobbies[subtrahend], blobbies[minuend]));
					}
					else
					if(code_val == 5)
					{
						// Divide operator
						unsigned long dividend = *code++;
						unsigned long divisor = *code++;
						assert_warning(dividend < blobbies.size());
						assert_warning(divisor < blobbies.size());

						blobbies.push_back(new k3d::blobby::divide(blobbies[dividend], blobbies[divisor]));
					}
					else
					if(code_val == 6)
					{
						// Negate operator
						/*unsigned long negand =*/ *code++;
					}
					else
					if(code_val == 7)
					{
						// Identity operator
						/*unsigned long idempotentate =*/ *code++;
					}
					else
						assert_not_reached();
				}

				m_current_mesh->blobbies.push_back(new k3d::blobby(blobbies.back()));
				set_object_properties(m_current_mesh_instance, "Blobby", Document);
			}
		}
	}
	else
	if(Node[0].rid() == transformation.id())
	{
		Hapy::Pree::const_iterator transf_item = (Node[0]).begin();
		if(transf_item->image() == "Identity")
		{
			if(!m_inside_world)
				frame_transformation = k3d::identity3D();
			else
				*m_transformations.rbegin() = k3d::identity3D();
		}
		else if(transf_item->image() == "TransformPoints")
		{
		}
		else
		{
			Hapy::Pree f = (*transf_item);
			Hapy::Pree::const_iterator transf_item = f.begin();
			if("ConcatTransform" == (*transf_item).image())
			{
				const Hapy::Pree& matrix = *++transf_item;
				assert_warning(matrix.rid() == rtfloat_array.id());

				k3d::matrix4 m;
				get_rtfloat_matrix(matrix, m);

				if(!m_inside_world)
					frame_transformation = k3d::identity3D();
				else
					*m_transformations.rbegin() = *m_transformations.rbegin() * m;
			}
			else if("Transform" == (*transf_item).image())
			{
				const Hapy::Pree& matrix = *++transf_item;
				assert_warning(matrix.rid() == rtfloat_array.id());

				k3d::matrix4 m;
				get_rtfloat_matrix(matrix, m);

				if(!m_inside_world)
					frame_transformation = k3d::identity3D();
				else
					*m_transformations.rbegin() = m;
			}
			else if("Translate" == (*transf_item).image())
			{
				double t1 = k3d::from_string<double>((++transf_item)->image(), 0);
				double t2 = k3d::from_string<double>((++transf_item)->image(), 0);
				double t3 = k3d::from_string<double>((++transf_item)->image(), 0);

				if(!m_inside_world)
					frame_translation += k3d::vector3(t1, t2, t3);
				else
					*m_translations.rbegin() += k3d::vector3(t1, t2, t3);
			}
			else if("Rotate" == (*transf_item).image())
			{
				double angle = k3d::from_string<double>((++transf_item)->image(), 0);
				double axis1 = k3d::from_string<double>((++transf_item)->image(), 0);
				double axis2 = k3d::from_string<double>((++transf_item)->image(), 0);
				double axis3 = k3d::from_string<double>((++transf_item)->image(), 0);
				k3d::matrix4 r_matrix = k3d::rotation3D(k3d::radians(angle), k3d::vector3(axis1, axis2, axis3));

				if(!m_inside_world)
					frame_rotation = frame_rotation * r_matrix;
				else
					*m_rotations.rbegin() = *m_rotations.rbegin() * r_matrix;
			}
			else if("Scale" == (*transf_item).image())
			{
				double s1 = k3d::from_string<double>((++transf_item)->image(), 0);
				double s2 = k3d::from_string<double>((++transf_item)->image(), 0);
				double s3 = k3d::from_string<double>((++transf_item)->image(), 0);

				if(!m_inside_world)
					frame_scaling = mult_scales(frame_scaling, k3d::vector3(s1, s2, s3));
				else
					*m_scalings.rbegin() = mult_scales(*m_scalings.rbegin(), k3d::vector3(s1, s2, s3));
			}
		}
	}
	else
	if(Node[0].rid() == shader.id())
	{
		Hapy::Pree::const_iterator shader_node = (Node[0]).begin();
		for(; shader_node != (Node[0]).end(); shader_node++)
		{
			Hapy::Pree f = (*shader_node);
			Hapy::Pree::const_iterator shader_node = f.begin();
			if("LightSource" == (*shader_node).image())
			{
				std::string name = no_quotes((++shader_node)->image());
				/*int val = */k3d::from_string<int>((++shader_node)->image(), 0);

				m_current_shader = create_document_plugin(k3d::classes::RenderManLightShader(), Document, "Light shader " + name);
				m_current_material = create_document_plugin(k3d::classes::RenderManLight(), Document, "Light " + name);
				assert_warning(k3d::set_property_value(*m_current_material, "shader", m_current_shader));

				k3d::set_property_value(*m_current_shader, "shader_name", name);

				// rtparameters
				const Hapy::Pree& parameters = *++shader_node;
				for(Hapy::Pree::const_iterator current_pair = parameters.begin(); current_pair != parameters.end(); current_pair++)
				{
					const Hapy::Pree& pair = *current_pair;
					const Hapy::Pree& pair_value = *pair.begin();
					const Hapy::Pree& string = *pair_value.begin();
					const std::string variable = no_quotes(string.image());
					const Hapy::Pree& value = *(pair_value.begin() + 1);

					if(m_declarations[variable] == "float")
					{
						const Hapy::Pree& float_val = *(value.begin() + 1);
						k3d::set_property_value(*m_current_shader, variable, k3d::from_string<double>(float_val.image(), 0));
					}
					else
					if(m_declarations[variable] == "color")
					{
						const Hapy::Pree& float_vals = *(value.begin() + 1);
						k3d::set_property_value(*m_current_shader, variable, k3d::from_string<k3d::color>(float_vals.image(), k3d::color(1.0, 1.0, 1.0)));
					}
					else
					if(m_declarations[variable] == "point")
					{
						const Hapy::Pree& float_vals = *(value.begin() + 1);
						k3d::set_property_value(*m_current_shader, variable, k3d::from_string<k3d::vector3>(float_vals.image(), k3d::vector3(0.0, 0.0, 0.0)));
					}
					else
						std::cerr << debug << "Variable type not declared for " << variable << " : " << m_declarations[variable] << std::endl;
				}
			}
			else
			if("Surface" == (*shader_node).image())
			{
				std::string name = no_quotes((++shader_node)->image());
				if(name != "null")
				{
					m_current_shader = create_document_plugin(k3d::classes::RenderManSurfaceShader(), Document, "Surface shader " + name);
					m_current_material = create_document_plugin(k3d::classes::RenderManMaterial(), Document, "Surface material " + name);
					assert_warning(k3d::set_property_value(*m_current_material, "surface_shader", m_current_shader));

					k3d::set_property_value(*m_current_shader, "shader_name", name);
					k3d::set_property_value(*m_current_material, "color", m_current_color);
					k3d::set_property_value(*m_current_material, "opacity", m_current_opacity);
					k3d::set_property_value(*m_current_material, "matte", m_current_matte ? true : false);

					// rtparameters
					const Hapy::Pree& parameters = *++shader_node;
					for(Hapy::Pree::const_iterator current_pair = parameters.begin(); current_pair != parameters.end(); current_pair++)
					{
						const Hapy::Pree& pair = *current_pair;
						const Hapy::Pree& pair_value = *pair.begin();
						const Hapy::Pree& string = *pair_value.begin();
						const std::string variable = no_quotes(string.image());
						const Hapy::Pree& value = *(pair_value.begin() + 1);

						if(m_declarations[variable] == "float")
						{
							const Hapy::Pree& float_val = *(value.begin() + 1);
							k3d::set_property_value(*m_current_shader, variable, k3d::from_string<double>(float_val.image(), 0));
						}
						else
						if(m_declarations[variable] == "color")
						{
							const Hapy::Pree& float_vals = *(value.begin() + 1);
							k3d::set_property_value(*m_current_shader, variable, k3d::from_string<k3d::color>(float_vals.image(), k3d::color(1.0, 1.0, 1.0)));
						}
						else
							std::cerr << debug << "Variable type not declared for " << variable << " : " << m_declarations[variable] << std::endl;
					}
				}
			}
		}
	}
	else
	{
		Hapy::Pree::const_iterator pi = Node.begin();
		for(; pi != Node.end(); pi++)
			parse_subpree(*pi, Document);
	}

	return;
}

bool rib_reader_implementation::read_file(k3d::idocument& Document, const boost::filesystem::path& FilePath)
{
	// Try to open the input file ...
	boost::filesystem::ifstream rib_file(FilePath);
	if(!rib_file.good())
		{
			std::cerr << warning << __PRETTY_FUNCTION__ << ": error opening [" << FilePath.native_file_string() << "]" << std::endl;
			return_val_if_fail(0, false);
		}

	std::string buffer("");
	while(!rib_file.eof())
		{
			std::string linebuffer;
			k3d::getline(rib_file, linebuffer);
			buffer += linebuffer + "\n";
		}

	if(!parser.parse(buffer))
	{
		std::cerr << debug << parser.result().location() << " -> parsing failed" << std::endl;
		return_val_if_fail(0, false);
	}

	// Process parse tree ...
	const Hapy::Pree& main_node = parser.result().pree;
	Hapy::Pree::const_iterator pi = main_node.begin();
	for(; pi != main_node.end(); pi++)
		parse_subpree(*pi, Document);

	return true;
}

k3d::iplugin_factory& rib_reader_factory()
{
	return rib_reader_implementation::get_factory();
}

} // namespace libk3dgeometry


