// 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 RAWWriter which exports polygons as raw ASCII files
		\author Romain Behar (romainbehar@yahoo.com)
*/

#include <k3dsdk/classes.h>
#include <k3dsdk/iapplication.h>
#include <k3dsdk/ideletable.h>
#include <k3dsdk/idocument.h>
#include <k3dsdk/ifile_format.h>
#include <k3dsdk/igeometry_write_format.h>
#include <k3dsdk/imesh_source.h>
#include <k3dsdk/iobject.h>
#include <k3dsdk/iobject_collection.h>
#include <k3dsdk/mesh.h>
#include <k3dsdk/module.h>
#include <k3dsdk/objects.h>
#include <k3dsdk/property.h>
#include <k3dsdk/result.h>
#include <k3dsdk/string_modifiers.h>
#include <k3dsdk/utility.h>

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

#include <map>

namespace libk3dgeometry
{

typedef std::vector<k3d::point*> point_collection_t;
typedef std::vector<unsigned long> polygon_t;
typedef std::vector<polygon_t> polygons_t;

class raw_write
{
public:
	raw_write(k3d::idocument& Document, std::ostream& Stream) :
		m_Document(Document),
		m_Stream(Stream)
	{
	}

	void operator()(k3d::iobject* const Object)
	{
		k3d::imesh_source* const mesh_source = dynamic_cast<k3d::imesh_source*>(Object);
		return_if_fail(mesh_source);

		k3d::mesh* const Mesh = boost::any_cast<k3d::mesh*>(k3d::get_property_value(m_Document.dag(), mesh_source->mesh_source_output()));
		return_if_fail(Mesh);

		// Get the collection of points ...
		k3d::mesh::points_t points;
		std::copy(Mesh->points.begin(), Mesh->points.end(), std::back_inserter(points));

		// Create a mapping of points-to-zero-based-indices ...
		std::map<k3d::point*, unsigned long> point_map;
		unsigned long index = 0;
		for(point_collection_t::iterator point = points.begin(); point != points.end(); ++point)
			point_map[*point] = index++;

		// Build polygon collection ...
		polygons_t polygons;
		for(k3d::mesh::polyhedra_t::const_iterator polyhedron = Mesh->polyhedra.begin(); polyhedron != Mesh->polyhedra.end(); polyhedron++)
			for(k3d::polyhedron::faces_t::const_iterator face = (*polyhedron)->faces.begin(); face != (*polyhedron)->faces.end(); face++)
			{
				polygon_t polygon;

				k3d::split_edge* first = (*face)->first_edge;
				// Skip empty faces
				if(!first)
					continue;

				polygon.push_back(point_map[first->vertex]);

				k3d::split_edge* current_edge = first->face_clockwise;
				while(current_edge)
				{
					polygon.push_back(point_map[current_edge->vertex]);

					current_edge = current_edge->face_clockwise;
					if(current_edge == first)
						break;
				}

				polygons.push_back(polygon);
			}

		// Write object info and header
		m_Stream << "# object: " << Object->name() << std::endl;
		m_Stream << "# num points [" << points.size() << "] num polygons [" << polygons.size() << "]" << std::endl;
		m_Stream << points.size() << " " << polygons.size() << std::endl;

		// Write points
		m_Stream << "# points" << std::endl;
		for(point_collection_t::iterator point = points.begin(); point != points.end(); ++point)
			{
				const k3d::vector3 coords = (*point)->position;
				m_Stream << coords[0] << " " << coords[1] << " " << coords[2] << std::endl;
			}

		// Write polygons
		m_Stream << "# polygons" << std::endl;
		for(polygons_t::iterator polygon = polygons.begin(); polygon != polygons.end(); ++polygon)
			{
				for(polygon_t::iterator point = polygon->begin(); point != polygon->end(); ++point)
					m_Stream << *point << " ";
				m_Stream << std::endl;
			}
	}

private:
	k3d::idocument& m_Document;
	std::ostream& m_Stream;
};

/////////////////////////////////////////////////////////////////////////////
// raw_writer_implementation

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

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

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

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

	bool write_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<raw_writer_implementation>, k3d::interface_list<k3d::igeometry_write_format> > factory(
			k3d::uuid(0xd3bf8f81, 0x44934ebc, 0x80e9c088, 0xe5c0c4fc),
			"RAWWriter",
			"raw ASCII format ( .raw )",
			"");

		return factory;
	}
};

bool raw_writer_implementation::write_file(k3d::idocument& Document, const boost::filesystem::path& FilePath)
{
	// Try to open the output file ...
	boost::filesystem::ofstream file(FilePath);
	return_val_if_fail(file.good(), false);
	file << "# Written by K-3D" << std::endl;

	// Get the set of available meshes ...
	k3d::objects_t meshes(k3d::find_objects(Document.objects(), k3d::classes::MeshInstance()));

	// Write meshes ...
	std::for_each(meshes.begin(), meshes.end(), raw_write(Document, file));

	return true;
}

k3d::iplugin_factory& raw_writer_factory()
{
	return raw_writer_implementation::get_factory();
}

} // namespace libk3dgeometry


