// 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 OBJWriter K-3D object, which exports Wavefront .obj files
		\author Tim Shead (tshead@k-3d.com)
*/

#include <k3dsdk/ideletable.h>
#include <k3dsdk/idocument.h>
#include <k3dsdk/ifile_format.h>
#include <k3dsdk/igeometry_write_format.h>
#include <k3dsdk/imaterial.h>
#include <k3dsdk/imaterial_collection.h>
#include <k3dsdk/k3dilocation.h>
#include <k3dsdk/k3dimesh.h>
#include <k3dsdk/k3dipath.h>
#include <k3dsdk/k3dipathcollection.h>
#include <k3dsdk/k3dipathpoint.h>
#include <k3dsdk/k3dipathpointcollection.h>
#include <k3dsdk/k3dipoint.h>
#include <k3dsdk/k3dipointcollection.h>
#include <k3dsdk/module.h>
#include <k3dsdk/objects.h>
#include <k3dsdk/result.h>
#include <k3dsdk/string_modifiers.h>
#include <k3dsdk/vectors.h>

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

#include <map>

namespace
{

typedef std::map<k3dIPoint*, unsigned long> PointMap;

// Output functors
class write_mesh :
	public k3dIPointVisitor,
	public k3dIPathVisitor
{
public:
	write_mesh(PointMap& pointmap, unsigned long& pointindex, boost::filesystem::ofstream& file) :
		m_PointMap(pointmap),
		m_PointIndex(pointindex),
		m_File(file),
		m_CurrentMaterial(0)
	{
	}

	void VisitPoint(k3dIPoint* const Point)
	{
		m_PointMap[Point] = m_PointIndex++;

		k3d::vector3 coords = Point->Location()->LocalXYZ();
		m_File << "v " << coords << std::endl;
	}

	void VisitPath(k3dIPath* const Path)
	{
		k3dIPathPointCollection* const pathpointserver = Path->Points();
		return_if_fail(pathpointserver->PointCount());

		// Handle materials as we go ...
		if(m_CurrentMaterial != Path->Materials()->material())
			{
				m_CurrentMaterial = Path->Materials()->material();
				if(m_CurrentMaterial)
					{
						k3d::iobject* const object = dynamic_cast<k3d::iobject*>(m_CurrentMaterial);
						assert_warning(object);

						m_File << "usemtl " << object->name() << std::endl;
					}
			}

		// Write the path ...
		m_File << "f";

		for(K3DPATHPOINTHANDLE pathpointhandle = pathpointserver->FirstPathPoint(); pathpointhandle; pathpointhandle = pathpointserver->NextPathPoint(pathpointhandle))
			m_File << " " << m_PointMap[pathpointserver->PathPoint(pathpointhandle)->Point()];

		m_File << std::endl;
	}

private:
	PointMap& m_PointMap;
	unsigned long& m_PointIndex;
	boost::filesystem::ofstream& m_File;

	// Keep track of the material as we go ...
	k3d::imaterial* m_CurrentMaterial;

};

/////////////////////////////////////////////////////////////////////////////
// obj_writer_implementation

class obj_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 "obj" == 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<obj_writer_implementation>, k3d::interface_list<k3d::igeometry_write_format> > factory(
			k3d::uuid(0x473cebdd, 0xc6a949f1, 0x9b56c218, 0xe511ed18),
			"OBJWriter",
			"Wavefront ( .obj )",
			"");

		return factory;
	}
};

bool obj_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;

	// Keep track of 1-based point indices as we write them ...
	PointMap pointmap;
	unsigned long pointindex = 1;

	// Get the set of available meshes ...
	k3d::objects_t meshes = k3d::find_objects<k3dIMesh>(Document.objects());

	// For each mesh ...
	for(k3d::objects_t::iterator meshobject = meshes.begin(); meshobject != meshes.end(); meshobject++)
		{
			k3dIMesh* const mesh = dynamic_cast<k3dIMesh*>(*meshobject);
			return_val_if_fail(mesh, false);

			// Write points ...
			write_mesh functor(pointmap, pointindex, file);
			mesh->Points()->VisitPoints(&functor);

			// Write paths ...
			mesh->Paths()->VisitPaths(&functor);
		}

	return true;
}

} // namespace

namespace libk3dgeometry
{

k3d::iplugin_factory& obj_writer_factory()
{
	return obj_writer_implementation::get_factory();
}

} // namespace libk3dgeometry


