// 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 AyamReader K-3D object, which reads Ayam .ay 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/iapplication.h>
#include <k3dsdk/idag.h>
#include <k3dsdk/ideletable.h>
#include <k3dsdk/idocument.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/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 <boost/filesystem/fstream.hpp>
#include <boost/filesystem/path.hpp>

#include <iostream>
#include <sstream>
#include <stack>

namespace
{

// K-3D is left handed and Ayam right handed

// Core object IDs
const int AY_ROOT		=	0;
const int AY_NPATCH		=	1;
const int AY_NCURVE		=	2;
const int AY_LEVEL		=	3;
const int AY_LIGHT		=	4;
const int AY_BOX		=	5;
const int AY_BPATCH		=	6;
const int AY_VIEW		=	7;
const int AY_CAMERA		=	8;
const int AY_INSTANCE		=	9;
const int AY_SPHERE		=	10;
const int AY_DISK		=	11;
const int AY_CONE		=	12;
const int AY_CYLINDER		=	13;
const int AY_PARABOLOID		=	14;
const int AY_HYPERBOLOID	=	15;
const int AY_TORUS		=	16;
const int AY_RIINC		=	17;
const int AY_MATERIAL		=	18;
const int AY_ICURVE		=	19;
const int AY_REVOLVE		=	20;
const int AY_EXTRUDE		=	21;
const int AY_SWEEP		=	22;
const int AY_SKIN		=	23;
const int AY_CAP		=	24;
const int AY_PATCHMESH		=	25;
const int AY_POLYMESH		=	26;
const int AY_CONCATNC		=	27;
const int AY_CLONE		=	28;
const int AY_SUBDIVMESH		=	29;

// Shader argument types
const int SHADER_ARG_SCALAR	=	0;
const int SHADER_ARG_POINT	=	1;
const int SHADER_ARG_NORMAL	=	2;
const int SHADER_ARG_VECTOR	=	3;
const int SHADER_ARG_COLOR	=	4;
const int SHADER_ARG_STRING	=	5;
const int SHADER_ARG_MATRIX	=	6;

// Level types
const int LEVEL_END		=	0;
const int LEVEL_LEVEL		=	1;
const int LEVEL_UNION		=	2;
const int LEVEL_DIFFERENCE	=	3;
const int LEVEL_INTERSECT	=	4;
const int LEVEL_PRIMITIVE	=	5;

// Knot vector types
const int KNOT_BEZIER		=	0;
const int KNOT_BSPLINE		=	1;
const int KNOT_NURBS		=	2;
const int KNOT_CUSTOM		=	3;

// View types
const int VIEW_FRONT		=	0;
const int VIEW_SIDE		=	1;
const int VIEW_TOP		=	2;
const int VIEW_PERSP		=	3;
const int VIEW_TRIM		=	4;


k3d::quaternion RotationQuaternion(const k3d::vector3& axis, const double cos_angle)
{
	// The quaternion requires half angles
	double angle = std::acos(k3d::Clamp<double>(-1.0, cos_angle, 1.0));

	return k3d::quaternion(std::cos(angle / 2.0), axis * std::sin(angle / 2.0));
}

k3d::quaternion LookAt(k3d::vector3 direction, k3d::vector3 up_vector)
{
	k3d::vector3 axis = direction.Normalize();
	k3d::vector3 up = up_vector.Normalize();

	// Find the rotation that lines up the view directions ...
	k3d::quaternion direction_rotation = k3d::quaternion(1.0, k3d::vector3(0.0, 0.0, 0.0));
	k3d::vector3 norm_axis = k3d::vector3(axis.n[1], -axis.n[0], 0.0);
	if(norm_axis * norm_axis > 1e-8)
		{
			direction_rotation = RotationQuaternion(norm_axis.Normalize(), -axis.n[2]);
		}

	// Now find the rotation that aligns the up vectors ...
	k3d::quaternion inverse_direction_rotation(direction_rotation.w, -1 * direction_rotation.v);

	// Rotate the world y vector to see where it ends up ...
	k3d::quaternion y(0.0, k3d::vector3(0.0, 1.0, 0.0));
	k3d::quaternion new_y = direction_rotation * y * inverse_direction_rotation;

	// Build the rotation needed to align the up vectors in the final position ...
	k3d::vector3 angle = (up - axis * (up * axis)).Normalize();
	k3d::quaternion y_rotation = RotationQuaternion(k3d::vector3(0.0, 0.0, 1.0), new_y.v * angle);

	// Put the two rotations together
	return direction_rotation * y_rotation;
}

// Ayam doesn't create RenderMan primitives with the same orientation as K-3D
void PrimitiveTransformation(k3d::iobject* object)
{
	// Get current object rotation
	k3d::angle_axis aa = boost::any_cast<k3d::angle_axis>(k3d::get_property_value(*object, "orientation"));

	// Make it a quaternion
	k3d::quaternion qaa(aa);

	// Rotate object 90 degrees around X
	k3d::quaternion transf = k3d::quaternion(k3d::angle_axis(k3d::radians(90.0), k3d::vector3(1.0, 0.0, 0.0)));

	// Set updated rotation
	k3d::set_property_value(*object, "orientation", k3d::angle_axis(qaa*transf));

	// Get current object scale
	k3d::vector3 scale = boost::any_cast<k3d::vector3>(k3d::get_property_value(*object, "scale"));
	// Swap Y and Z
	std::swap(scale.n[1], scale.n[2]);
	// Set updated scale
	k3d::set_property_value(*object, "scale", scale);
}

/////////////////////////////////////////////////////////////////////////////
// ayam_reader_implementation

class ayam_reader_implementation :
	public k3d::ifile_format,
	public k3d::igeometry_read_format,
	public k3d::ideletable
{
public:
	unsigned long priority()
	{
		return 0;
	}

	bool query_can_handle(const boost::filesystem::path& FilePath)
	{
		return "ay" == 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<ayam_reader_implementation>, k3d::interface_list<k3d::igeometry_read_format> > factory(
			k3d::uuid(0x175b6856, 0x659db40d, 0x4d783caa, 0xfa348ece),
			"AyamReader",
			"Ayam Modeler ( .ay )",
			"");

		return factory;
	}

private:
	int ayam_minor_version;

	double ri_x_samples;
	double ri_y_samples;
	int ri_pixel_filter_width;
	int ri_pixel_filter_height;
	int ri_texture_memory;
	int output_pixel_width;
	int output_pixel_height;

	// File helpers
	int get_int()
	{
		std::string buffer;
		getline(input_file, buffer);
		return k3d::from_string<int>(buffer, 0);
	}

	double get_float()
	{
		std::string buffer;
		getline(input_file, buffer);
		return k3d::from_string<double>(buffer, 0);
	}

	k3d::vector3 get_vector3()
	{
		double x = get_float();
		double y = get_float();
		double z = get_float();

		return k3d::vector3(x, y, z);
	}

	k3d::vector4 get_vector4()
	{
		double x = get_float();
		double y = get_float();
		double z = get_float();
		double w = get_float();

		return k3d::vector4(x, y, z, w);
	}

	k3d::iobject* CreateObject(const k3d::uuid plugin_id, k3d::idocument& Document)
	{
		k3d::iobject* const object = k3d::create_document_plugin(plugin_id, Document);
		assert_warning(object);

		return object;
	}

	k3d::iobject* CreateInstance(k3d::idocument& Document)
	{
		// Render Mesh plugin
		k3d::iobject* instance = CreateObject(k3d::classes::MeshInstance(), Document);
		assert_warning(instance);

		return instance;
	}

	// Load core objects
	void LoadRoot()
	{
		double variance = get_float();
		ri_x_samples = get_float();
		ri_y_samples = get_float();
		int filter = get_int();
		ri_pixel_filter_width = get_int();
		ri_pixel_filter_height = get_int();
		double gain = get_float();
		double gamma = get_float();
		double rgba_one = get_float();
		double rgba_min = get_float();
		double rgba_max = get_float();
		double rgba_dither = get_float();
		int minsamples = get_int();
		int maxsamples = get_int();
		int maxray = get_int();
		double shadow_bias = get_float();
		int prman = get_int();
		int radsteps = get_int();
		int patchsamples = get_int();

		std::string textures;
		getline(input_file, textures);
		std::string archives;
		getline(input_file, archives);
		std::string shaders;
		getline(input_file, shaders);

		ri_texture_memory = get_int();
		int geommem = get_int();
		output_pixel_width = get_int();
		output_pixel_height = get_int();

		int has_atmosphere = get_int();
		if(has_atmosphere)
			LoadShader();

		int has_imager = get_int();
		if(has_imager)
			LoadShader();
	}

	void LoadView(k3d::iobject* camera)
	{
		int width = get_int();
		int height = get_int();

		int type = get_int();
		k3d::vector3 up(0.0, 0.0, 0.0);
		if(type != VIEW_TOP)
			up.n[1] = 1.0;
		else
			up.n[2] = -1.0;

		double grid = get_float();
		int drawsel = get_int();
		int drawlevel = get_int();
		int redraw = get_int();
		int drawgrid = get_int();
		int usegrid = get_int();
		int local = get_int();
		int shade = get_int();

		double fromx = get_float();
		double fromy = get_float();
		double fromz = get_float();
		double tox = get_float();
		double toy = get_float();
		double toz = get_float();
		double upx = get_float();
		double upy = get_float();
		double upz = get_float();
		double roll = get_float();
		double zoom = get_float();

		int posx = get_int();
		int posy = get_int();

		double rotx = get_float();
		double roty = get_float();
		double rotz = get_float();

		if(ayam_minor_version >= 2)
			{
				std::string bgimage;
				getline(input_file, bgimage);

				int drawbg = get_int();
			}

		if(ayam_minor_version >= 4)
			{
				double near_plane = get_float();
				double far_plane = get_float();
			}

		// Set properties ...
		k3d::set_property_value(*camera, "position", k3d::vector3(fromx, fromy, fromz));
		k3d::quaternion q = LookAt(k3d::vector3(fromx - tox, fromy - toy, fromz - toz), k3d::vector3(upx, upy, upz));
		k3d::set_property_value(*camera, "orientation", k3d::angle_axis(q.Normalize()));
	}

	void LoadInstance(k3d::iobject* instance)
	{
		k3d::set_property_value(*instance, "offsetx", translation.n[0]);
		k3d::set_property_value(*instance, "offsety", translation.n[1]);
		k3d::set_property_value(*instance, "offsetz", translation.n[2]);
		k3d::set_property_value(*instance, "rotatex", -rotation.n[0]);
		k3d::set_property_value(*instance, "rotatey", rotation.n[1]);
		k3d::set_property_value(*instance, "rotatez", rotation.n[2]);
	}

	void MakeKnotVector(int knot_type, std::vector<double>& knots, int knot_count, int order)
	{
		switch(knot_type)
			{
				case KNOT_BEZIER:
				{
					for(int n = 0; n < knot_count/2; n++)
						knots.push_back(0.0);

					for(int n = knot_count/2; n < knot_count; n++)
						knots.push_back(1.0);
				}
				break;

				case KNOT_BSPLINE:
				{
					double d_kc = static_cast<double>(knot_count);
					for(int n = 0; n < knot_count; n++)
						{
							double d_n = static_cast<double>(n);
							knots.push_back(d_n/d_kc);
						}
				}
				break;

				case KNOT_NURBS:
				{
					for(int n = 0; n < order; n++)
						knots.push_back(0.0);

					int j = 1;
					int kts = 1 + knot_count - (order*2);
					for(int n = order; n < knot_count - order; n++)
						knots.push_back(static_cast<double>(j++)/static_cast<double>(kts));
					for(int n = knot_count - order; n < knot_count; n++)
						knots.push_back(1.0);
				}
				break;

				case KNOT_CUSTOM:
				{
					for(int n = 0; n < knot_count; n++)
						{
							double k = get_float();
							knots.push_back(k);
						}
				}
				break;

				default:
					assert_warning("unkown knot type");
			}
	}

	k3d::mesh* LoadNCurve()
	{
		k3d::mesh* const mesh = new k3d::mesh();
		k3d::nucurve* const nucurve = new k3d::nucurve();
		k3d::nucurve_group* const nucurve_group = new k3d::nucurve_group();
		mesh->nucurve_groups.push_back(nucurve_group);
		nucurve_group->curves.push_back(nucurve);

		int length = get_int();
		int order = get_int();
		int knot_type = get_int();

		nucurve->order = order;

		int knot_count = length + order;
		MakeKnotVector(knot_type, nucurve->knots, knot_count, order);

		// Control vertices
		for(int n = 0; n < length; n++)
			{
				std::string buffer;
				getline(input_file, buffer);
				std::istringstream stream(buffer);

				double x, y, z, w;
				stream >> x >> y >> z >> w;

				k3d::point* p = new k3d::point(x, y, z);
				mesh->points.push_back(p);
				nucurve->control_points.push_back(k3d::nucurve::control_point(p, w));
			}

		assert_warning(is_valid(*nucurve));

		// Properties
		int closed = get_int();
		double glu_sampling_tolerance = get_float();
		int display_mode = get_int();

		if(ayam_minor_version >= 1)
			{
				int createmp = get_int();
			}

		return mesh;
	}

	k3d::mesh* LoadNPatch()
	{
		k3d::mesh* const mesh = new k3d::mesh();
		k3d::nupatch* const nupatch = new k3d::nupatch();
		mesh->nupatches.push_back(nupatch);

		int width = get_int();
		int height = get_int();
		int u_order = get_int();
		int v_order = get_int();
		int u_knot_type = get_int();
		int v_knot_type = get_int();

		nupatch->u_order = u_order;
		nupatch->v_order = v_order;

		int u_knot_count = width + u_order;
		int v_knot_count = height + v_order;

		MakeKnotVector(u_knot_type, nupatch->u_knots, u_knot_count, u_order);
		MakeKnotVector(v_knot_type, nupatch->v_knots, v_knot_count, v_order);

		// Control vertices
		for(int n = 0; n < width*height; n++)
			{
				std::string buffer;
				getline(input_file, buffer);
				std::istringstream stream(buffer);

				double x, y, z, w;
				stream >> x >> y >> z >> w;

				k3d::point* p = new k3d::point(x, y, z);
				mesh->points.push_back(p);
				nupatch->control_points.push_back(k3d::nupatch::control_point(p, w));
			}

		assert_warning(is_valid(*nupatch));

		// Properties
		double glu_sampling_tolerance = get_float();
		int display_mode = get_int();

		return mesh;
	}

	void LoadMaterial()
	{
		int registered = get_int();
		int color_r = get_int();
		int color_g = get_int();
		int color_b = get_int();
		int op_r = get_int();
		int op_g = get_int();
		int op_b = get_int();
		double shading_rate = get_float();
		int shading_interpolation = get_int();
		int dbound = get_int();
		double dbound_val = get_float();
		int true_displacement = get_int();
		int cast_shadows = get_int();
		int camera = get_int();
		int reflection = get_int();
		int shadow = get_int();

		int has_s_shader = get_int();
		if(has_s_shader)
			LoadShader();
		int has_d_shader = get_int();
		if(has_d_shader)
			LoadShader();
		int has_i_shader = get_int();
		if(has_i_shader)
			LoadShader();
		int has_shader = get_int();
		if(has_shader)
			LoadShader();

		if(ayam_minor_version >= 4)
			{
				int sides = get_int();
			}
	}

	void LoadSkin()
	{
		int interpolate = get_int();
		int uorder = get_int();
		int uknot_type = get_int();
		int has_start_cap = get_int();
		int has_end_cap = get_int();
		int glu_display_mode = get_int();
		double glu_sampling_tolerance = get_float();
	}

	void LoadLight()
	{
		int shadows = get_int();
		int samples = get_int();
		int type = get_int();
		int on = get_int();
		double intensity = get_float();
		int color_r = get_int();
		int color_g = get_int();
		int color_b = get_int();
		double cone_angle = get_float();
		double cone_delta_angle = get_float();
		double beam_distribution = get_float();
		int use_sm = get_int();
		int sm_resolution = get_int();
		double from_x = get_float();
		double from_y = get_float();
		double from_z = get_float();
		double to_x = get_float();
		double to_y = get_float();
		double to_z = get_float();

		int has_shader= get_int();
		if(has_shader)
			{
				LoadShader();
			}

		if(ayam_minor_version >= 5)
			{
				int local = get_int();
			}
	}

	// Core objects attributes
	void GetAttributes()
	{
		int params = get_int();
		if(params)
			{
				translation = get_vector3();
				rotation = get_vector3();
				quaternion = get_vector4();
				scale = get_vector3();
			}

		// Can have children
		children = get_int();
		// Inherits tranformation properties
		inherit_transf = get_int();
		hide = get_int();
		hide_children = get_int();

		getline(input_file, name);
	}

	void SetAttributes(k3d::idocument& Document, k3d::iobject* object, const std::string& default_name)
	{
		k3d::set_property_value(*object, "position", translation);
		k3d::set_property_value(*object, "orientation", k3d::angle_axis(k3d::quaternion(k3d::euler_angles(k3d::radians(-rotation.n[0]), k3d::radians(rotation.n[1]), k3d::radians(rotation.n[2]), k3d::euler_angles::XYZstatic))));
		k3d::set_property_value(*object, "scale", scale);

		if(name.empty())
			object->set_name(default_name);
		else
		{
			std::cerr << "Name : " << name << std::endl;
			object->set_name(name);
		}

		//std::cerr << "Parent : " << children << ", inherit : " << inherit_transf << std::endl;

assert_warning(0);
/*
		// Set parent in hierarchy
		if(!hierarchy_levels.empty())
			{
				Document.hierarchy().SetObjectParent(object, hierarchy_levels.top());
			}
*/
	}

	// Load objects tags
	void GetTags()
	{
		int tags = get_int();
		for(int i = 0; i < tags; i++)
			{
				std::string name;
				getline(input_file, name);

				//if(name registered)
				std::string value;
				getline(input_file, value);
			}
	}

	void LoadShader()
	{
		std::string name;
		getline(input_file, name);

		int type = get_int();
		int argc = get_int();

		for(int j = 0; j < argc; j++)
			{
				std::string arg_name;
				getline(input_file, arg_name);

				int arg_type = get_int();
				switch(arg_type)
					{
						case SHADER_ARG_SCALAR:
						{
							double scalar = get_float();
						}
						break;

						case SHADER_ARG_POINT:
						case SHADER_ARG_NORMAL:
						case SHADER_ARG_VECTOR:
						{
							k3d::vector3 v = get_vector3();
						}
						break;

						case SHADER_ARG_COLOR:
						{
							k3d::color color = k3d::color(get_float(), get_float(), get_float());
						}
						break;

						case SHADER_ARG_STRING:
						{
							std::string s;
							getline(input_file, s);
						}
						break;

						case SHADER_ARG_MATRIX:
						{
							std::string m;
							getline(input_file, m);
						}
						break;

						default:
						break;
					}
			}
	}

	boost::filesystem::ifstream input_file;

	std::stack<k3d::iobject*> hierarchy_levels;
	k3d::iobject* last_object;

	k3d::vector3 translation;
	k3d::vector3 rotation;
	k3d::vector4 quaternion;
	k3d::vector3 scale;
	int children;
	int inherit_transf;
	int hide;
	int hide_children;
	std::string name;

};

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

	std::string line_buffer;
	getline(input_file, line_buffer);
	if(line_buffer.empty() && !input_file.good())
		{
			std::cerr << __PRETTY_FUNCTION__ << ": empty file [" << FilePath.native_file_string() << "]" << std::endl;
			return false;
		}

	// Format name : "Ayam"
	if(line_buffer != "Ayam")
		{
			std::cerr << __PRETTY_FUNCTION__ << ": " << FilePath.native_file_string() << " not an Ayam file" << std::endl;
			return false;
		}

	// Ayam version
	getline(input_file, line_buffer);
	if(line_buffer.empty() && !input_file.good())
		{
			std::cerr << __PRETTY_FUNCTION__ << ": " << FilePath.native_file_string() << " couldn't get Ayam version" << std::endl;
			return false;
		}

	double ayam_version = k3d::from_string<double>(line_buffer, 0);
	ayam_minor_version = 0;
	if(ayam_version > 1.0)
		ayam_minor_version = static_cast<int>(10 * (ayam_version - 1.0));

	// Read objects ...
	while(input_file.good())
	{
		// An object is marked by one or more bells
		bool skip = true;
		do
		{
			std::ifstream::int_type c = input_file.get();
			if(static_cast<char>(c) == '\a')
				{
					while(static_cast<char>(input_file.peek()) == '\a')
						input_file.get();

					break;
				}
			else
				{
					if(skip)
						{
							std::cerr << "Skipping : " << std::endl;
							skip = false;
						}

					std::cerr << static_cast<char>(c);
				}
		}
		while(input_file.good());

		if(!input_file.good())
			return true;

		// Get object type ...
		int has_name;
		input_file >> has_name;
		input_file.get();
		int type = -1;
		if(has_name)
			{
				// Named object
				std::string type_name;
				getline(input_file, type_name);
std::cerr << "Named object : " << type_name << std::endl;
			}
		else
			{
				// Object with ID
				type = get_int();
			}

		int has_child = 0;
		if(ayam_minor_version)
			has_child = get_int();

		GetAttributes();

		GetTags();

		switch(type)
			{
				case AY_ROOT:
				{
					LoadRoot();
				}
				break;

				case AY_LEVEL:
				{
					int type = get_int();
					if(type == LEVEL_END)
						{
							if(!hierarchy_levels.empty())
								hierarchy_levels.pop();
							break;
						}

					k3d::iobject* transform = CreateObject(k3d::classes::Transform(), Document);
					SetAttributes(Document, transform, "Transform");
					switch(type)
						{
							case LEVEL_LEVEL:
								hierarchy_levels.push(transform);
							break;
							case LEVEL_UNION:
								hierarchy_levels.push(transform);
							break;
							case LEVEL_DIFFERENCE:
								hierarchy_levels.push(transform);
							break;
							case LEVEL_INTERSECT:
								hierarchy_levels.push(transform);
							break;
							case LEVEL_PRIMITIVE:
								hierarchy_levels.push(transform);
							break;
							default:
								assert_warning("unknown level type");
							break;
						}

					last_object = transform;
				}
				break;

				case AY_VIEW:
				{
					k3d::iobject* camera = CreateObject(k3d::classes::RenderManEngine(), Document);
					SetAttributes(Document, camera, "View");
					LoadView(camera);
				}
				break;

				case AY_INSTANCE:
				{
					k3d::iobject* instance = CreateInstance(Document);
					LoadInstance(instance);

					k3d::idag::dependencies_t dependencies;
					dependencies[k3d::get_property(*instance, "input")] = k3d::get_property(*last_object, "output");
					Document.dag().set_dependencies(dependencies);

assert_warning(0);
/*
					Document.hierarchy().SetObjectParent(instance, Document.hierarchy().ObjectParent(last_object));
*/

					SetAttributes(Document, instance, "Instance");
				}
				break;

				case AY_BOX:
				{
					get_float(); // width
					get_float(); // length
					get_float(); // height

					//last_object = ;
				}
				break;

				case AY_SPHERE:
				{
					k3d::iobject* sphere = CreateObject(k3d::classes::Sphere(), Document);
					get_int(); // closed
					k3d::set_property_value(*sphere, "radius", get_float());
					k3d::set_property_value(*sphere, "zmin", get_float());
					k3d::set_property_value(*sphere, "zmax", get_float());
					k3d::set_property_value(*sphere, "sweepangle", k3d::radians(get_float()));

					SetAttributes(Document, sphere, "Sphere");
					PrimitiveTransformation(sphere);

					last_object = sphere;
				}
				break;

				case AY_DISK:
				{
					k3d::iobject* disk = CreateObject(k3d::classes::Disk(), Document);
					k3d::set_property_value(*disk, "radius", get_float());
					k3d::set_property_value(*disk, "height", get_float());
					k3d::set_property_value(*disk, "sweepangle", k3d::radians(get_float()));

					SetAttributes(Document, disk, "Disk");
					PrimitiveTransformation(disk);

					last_object = disk;
				}
				break;

				case AY_CONE:
				{
					k3d::iobject* cone = CreateObject(k3d::classes::Cone(), Document);
					get_int(); // closed
					k3d::set_property_value(*cone, "radius", get_float());
					k3d::set_property_value(*cone, "height", get_float());
					k3d::set_property_value(*cone, "sweepangle", k3d::radians(get_float()));

					SetAttributes(Document, cone, "Cone");
					PrimitiveTransformation(cone);

					last_object = cone;
				}
				break;

				case AY_CYLINDER:
				{
					k3d::iobject* cylinder = CreateObject(k3d::classes::Cylinder(), Document);
					get_int(); // closed
					k3d::set_property_value(*cylinder, "radius", get_float());
					k3d::set_property_value(*cylinder, "zmin", get_float());
					k3d::set_property_value(*cylinder, "zmax", get_float());
					k3d::set_property_value(*cylinder, "sweepangle", k3d::radians(get_float()));

					SetAttributes(Document, cylinder, "Cylinder");
					PrimitiveTransformation(cylinder);

					last_object = cylinder;
				}
				break;

				case AY_PARABOLOID:
				{
					k3d::iobject* paraboloid = CreateObject(k3d::classes::Paraboloid(), Document);
					get_int(); // closed
					k3d::set_property_value(*paraboloid, "radius", get_float());
					k3d::set_property_value(*paraboloid, "height", get_float());
					k3d::set_property_value(*paraboloid, "zmin", get_float());
					k3d::set_property_value(*paraboloid, "sweepangle", k3d::radians(get_float()));

					SetAttributes(Document, paraboloid, "Paraboloid");
					PrimitiveTransformation(paraboloid);

					last_object = paraboloid;
				}
				break;

				case AY_HYPERBOLOID:
				{
					k3d::iobject* hyperboloid = CreateObject(k3d::classes::Hyperboloid(), Document);
					int closed = get_int();

					// Vector3
					std::string buffer;
					getline(input_file, buffer);
					std::istringstream stream(buffer);
					double x, y, z;
					stream >> x >> y >> z;
					k3d::set_property_value(*hyperboloid, "p1x", x);
					k3d::set_property_value(*hyperboloid, "p1y", y);
					k3d::set_property_value(*hyperboloid, "p1z", z);

					getline(input_file, buffer);
					std::istringstream stream2(buffer);
					stream2 >> x >> y >> z;
					k3d::set_property_value(*hyperboloid, "p2x", x);
					k3d::set_property_value(*hyperboloid, "p2y", y);
					k3d::set_property_value(*hyperboloid, "p2z", z);

					k3d::set_property_value(*hyperboloid, "sweepangle", k3d::radians(get_float()));

					SetAttributes(Document, hyperboloid, "Hyperboloid");
					PrimitiveTransformation(hyperboloid);

					last_object = hyperboloid;
				}
				break;

				case AY_TORUS:
				{
					k3d::iobject* torus = CreateObject(k3d::classes::Torus(), Document);
					get_int(); // closed
					k3d::set_property_value(*torus, "majorradius", get_float());
					k3d::set_property_value(*torus, "minorradius", get_float());
					k3d::set_property_value(*torus, "phimin", get_float());
					k3d::set_property_value(*torus, "phimax", get_float());
					k3d::set_property_value(*torus, "sweepangle", k3d::radians(get_float()));

					SetAttributes(Document, torus, "Torus");
					PrimitiveTransformation(torus);

					last_object = torus;
				}
				break;

				case AY_POLYMESH:
				{
					int mesh_type = get_int();

					unsigned long poly_count = static_cast<unsigned int>(get_int());
					std::vector<unsigned long> loops;
					for(unsigned long n = 0; n < poly_count; n++)
						loops.push_back(static_cast<unsigned long>(get_int()));

					unsigned long loop_count = static_cast<unsigned int>(get_int());
					std::vector<unsigned long> nverts;
					for(unsigned long n = 0; n < loop_count; n++)
						nverts.push_back(static_cast<unsigned long>(get_int()));

					unsigned long vertice_counts = static_cast<unsigned int>(get_int());
					std::vector<unsigned long> verts;
					for(unsigned long n = 0; n < vertice_counts; n++)
						verts.push_back(static_cast<unsigned long>(get_int()));


std::cerr << "POMESH :" << std::endl;
std::cerr << "poly_count :" << poly_count << std::endl;
std::cerr << "loop_count :" << loop_count << std::endl;
std::cerr << "vertice_counts :" << vertice_counts << std::endl;
					unsigned long point_count = static_cast<unsigned int>(get_int());
std::cerr << "point_count :" << point_count << std::endl;
					int has_normals = get_int();

					for(unsigned long n = 0; n < point_count; n++)
						{
							std::string buffer;
							getline(input_file, buffer);
							std::istringstream stream(buffer);

							double x, y, z;
							stream >> x >> y >> z;

							if(has_normals)
								getline(input_file, buffer);
						}
				}
				break;

				case AY_NCURVE:
				{
					k3d::mesh* mesh = LoadNCurve();
					k3d::iobject* frozen_mesh = CreateObject(k3d::classes::FrozenMesh(), Document);
					k3d::set_property_value(*frozen_mesh, "input", mesh);
					SetAttributes(Document, frozen_mesh, "NCurve");

					k3d::iobject* instance = CreateInstance(Document);

					k3d::idag::dependencies_t dependencies;
					dependencies[k3d::get_property(*instance, "input")] = k3d::get_property(*frozen_mesh, "output");
					Document.dag().set_dependencies(dependencies);

assert_warning(0);
/*
					Document.hierarchy().SetObjectParent(instance, Document.hierarchy().ObjectParent(frozen_mesh));
*/

					last_object = frozen_mesh;
				}
				break;

				case AY_NPATCH:
				{
					k3d::mesh* mesh = LoadNPatch();
					k3d::iobject* frozen_mesh = CreateObject(k3d::classes::FrozenMesh(), Document);
					k3d::set_property_value(*frozen_mesh, "input", mesh);
					SetAttributes(Document, frozen_mesh, "NPatch");

					k3d::iobject* instance = CreateInstance(Document);

					k3d::idag::dependencies_t dependencies;
					dependencies[k3d::get_property(*instance, "input")] = k3d::get_property(*frozen_mesh, "output");
					Document.dag().set_dependencies(dependencies);

assert_warning(0);
/*
					Document.hierarchy().SetObjectParent(instance, Document.hierarchy().ObjectParent(frozen_mesh));
*/

					last_object = frozen_mesh;
				}
				break;

				case AY_MATERIAL:
				{
					LoadMaterial();
				}
				break;

				case AY_SKIN:
				{
					k3d::iobject* transform = CreateObject(k3d::classes::Transform(), Document);
					LoadSkin();
					SetAttributes(Document, transform, "Skin");
					hierarchy_levels.push(transform);
				}
				break;

				case AY_LIGHT:
				{
					LoadLight();
				}
				break;

				case AY_REVOLVE:
				{
					k3d::iobject* transform = CreateObject(k3d::classes::Transform(), Document);

					double thetamax = get_float();
					int has_upper_cap = get_int();
					int has_lower_cap = get_int();
					int has_start_cap = get_int();
					int has_end_cap = get_int();
					int glu_display_mode = get_int();
					double glu_sampling_tolerance = get_float();

					SetAttributes(Document, transform, "Revolve");
					hierarchy_levels.push(transform);
				}
				break;

				default:
					std::cerr << "Unknown Ayam object type : " << type << std::endl;
			}

		if(ayam_minor_version == 0)
			{
				has_child = get_int();
			}
	}

	return true;
}

} // namespace

namespace libk3dgeometry
{

k3d::iplugin_factory& ayam_reader_factory()
{
	return ayam_reader_implementation::get_factory();
}

} // namespace libk3dgeometry


