// 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
		\author Timothy M. Shead (tshead@k-3d.com)
*/

#include <k3dsdk/color.h>
#include <k3dsdk/object.h>
#include <k3dsdk/persistence.h>
#include <k3dsdk/mesh_filter.h>
#include <k3dsdk/module.h>
#include <k3dsdk/plugins.h>
#include <k3dsdk/selection.h>

#include <iterator>

namespace libk3dmesh
{

/////////////////////////////////////////////////////////////////////////////
// tag_color_implementation

class tag_color_implementation :
	public k3d::mesh_filter<k3d::persistent<k3d::object> >
{
	typedef k3d::mesh_filter<k3d::persistent<k3d::object> > base;

public:
	tag_color_implementation(k3d::idocument& Document) :
		base(Document),
		m_new_color(k3d::init_name("new_color") + k3d::init_description("New Color [color]") + k3d::init_value(k3d::color(1, 0, 0)) + k3d::init_document(Document)),
		m_default_color(k3d::init_name("default_color") + k3d::init_description("Default Color [color]") + k3d::init_value(k3d::color(1, 1, 1)) + k3d::init_document(Document)),
		m_tag_points(k3d::init_name("tag_points") + k3d::init_description("Tag Points [boolean]") + k3d::init_value(false) + k3d::init_document(Document)),
		m_tag_point_groups(k3d::init_name("tag_point_groups") + k3d::init_description("Tag Point Groups [boolean]") + k3d::init_value(false) + k3d::init_document(Document)),
		m_tag_edges(k3d::init_name("tag_edges") + k3d::init_description("Tag Edges [boolean]") + k3d::init_value(false) + k3d::init_document(Document)),
		m_tag_faces(k3d::init_name("tag_faces") + k3d::init_description("Tag Faces [boolean]") + k3d::init_value(false) + k3d::init_document(Document)),
		m_tag_polyhedra(k3d::init_name("tag_polyhedra") + k3d::init_description("Tag Polyhedra [boolean]") + k3d::init_value(false) + k3d::init_document(Document)),
		m_tag_linear_curve_groups(k3d::init_name("tag_linear_curve_groups") + k3d::init_description("Tag Linear Groups [boolean]") + k3d::init_value(false) + k3d::init_document(Document)),
		m_tag_cubic_curve_groups(k3d::init_name("tag_cubic_curve_groups") + k3d::init_description("Tag Cubic Curve Groups [boolean]") + k3d::init_value(false) + k3d::init_document(Document)),
		m_tag_bilinear_patches(k3d::init_name("tag_bilinear_patches") + k3d::init_description("Tag Bilinear Patches [boolean]") + k3d::init_value(false) + k3d::init_document(Document)),
		m_tag_bicubic_patches(k3d::init_name("tag_bicubic_patches") + k3d::init_description("Tag Bicubic Patches [boolean]") + k3d::init_value(false) + k3d::init_document(Document))
	{
		register_property(m_new_color);
		register_property(m_default_color);
		register_property(m_tag_points);
		register_property(m_tag_point_groups);
		register_property(m_tag_edges);
		register_property(m_tag_faces);
		register_property(m_tag_polyhedra);
		register_property(m_tag_linear_curve_groups);
		register_property(m_tag_cubic_curve_groups);
		register_property(m_tag_bilinear_patches);
		register_property(m_tag_bicubic_patches);
		
		enable_serialization(k3d::persistence::proxy(m_new_color));
		enable_serialization(k3d::persistence::proxy(m_default_color));
		enable_serialization(k3d::persistence::proxy(m_tag_points));
		enable_serialization(k3d::persistence::proxy(m_tag_point_groups));
		enable_serialization(k3d::persistence::proxy(m_tag_edges));
		enable_serialization(k3d::persistence::proxy(m_tag_faces));
		enable_serialization(k3d::persistence::proxy(m_tag_polyhedra));
		enable_serialization(k3d::persistence::proxy(m_tag_linear_curve_groups));
		enable_serialization(k3d::persistence::proxy(m_tag_cubic_curve_groups));
		enable_serialization(k3d::persistence::proxy(m_tag_bilinear_patches));
		enable_serialization(k3d::persistence::proxy(m_tag_bicubic_patches));

		m_input_mesh.changed_signal().connect(SigC::slot(*this, &tag_color_implementation::on_reset_geometry));
		m_new_color.changed_signal().connect(SigC::slot(*this, &tag_color_implementation::on_reset_geometry));
		m_default_color.changed_signal().connect(SigC::slot(*this, &tag_color_implementation::on_reset_geometry));
		m_tag_points.changed_signal().connect(SigC::slot(*this, &tag_color_implementation::on_reset_geometry));
		m_tag_point_groups.changed_signal().connect(SigC::slot(*this, &tag_color_implementation::on_reset_geometry));
		m_tag_edges.changed_signal().connect(SigC::slot(*this, &tag_color_implementation::on_reset_geometry));
		m_tag_faces.changed_signal().connect(SigC::slot(*this, &tag_color_implementation::on_reset_geometry));
		m_tag_polyhedra.changed_signal().connect(SigC::slot(*this, &tag_color_implementation::on_reset_geometry));
		m_tag_linear_curve_groups.changed_signal().connect(SigC::slot(*this, &tag_color_implementation::on_reset_geometry));
		m_tag_cubic_curve_groups.changed_signal().connect(SigC::slot(*this, &tag_color_implementation::on_reset_geometry));
		m_tag_bilinear_patches.changed_signal().connect(SigC::slot(*this, &tag_color_implementation::on_reset_geometry));
		m_tag_bicubic_patches.changed_signal().connect(SigC::slot(*this, &tag_color_implementation::on_reset_geometry));

		m_output_mesh.need_data_signal().connect(SigC::slot(*this, &tag_color_implementation::on_create_geometry));
	}
	
	void on_reset_geometry()
	{
		m_output_mesh.reset();
	}
	
	k3d::mesh* on_create_geometry()
	{
		// If we don't have any input mesh, we're done ...
		const k3d::mesh* const input = m_input_mesh.property_value();
		if(!input)
			return 0;

		// Otherwise, we make a copy of the input mesh and modify the copy ...
		k3d::mesh* const output = new k3d::mesh();
		k3d::deep_copy(*input, *output);
		modify_geometry(*input, *output);

		return output;
	}

	void set_color(const bool Selected, const bool IgnoreSelection, const k3d::color& NewColor, const k3d::color& DefaultColor, k3d::parameters_t& Parameters)
	{
		if(IgnoreSelection || Selected)
			Parameters["Cs"] = NewColor;
		else if(0 == Parameters.count("Cs"))
			Parameters["Cs"] = DefaultColor;
	}

	void modify_geometry(const k3d::mesh& Source, k3d::mesh& Target)
	{
		const bool ignore_selection = !k3d::contains_selection(Source);
		const k3d::color new_color = m_new_color.property_value();
		const k3d::color default_color = m_default_color.property_value();

		if(m_tag_points.property_value())
			{
				for(k3d::mesh::points_t::iterator point = Target.points.begin(); point != Target.points.end(); ++point)
					set_color((*point)->selected, ignore_selection, new_color, default_color, (*point)->vertex_data);
			}

		if(m_tag_point_groups.property_value())
			{
				for(k3d::mesh::point_groups_t::iterator group = Target.point_groups.begin(); group != Target.point_groups.end(); ++group)
					set_color((*group)->selected, ignore_selection, new_color, default_color, (*group)->constant_data);
			}

		if(m_tag_linear_curve_groups.property_value())
			{
				for(k3d::mesh::linear_curve_groups_t::iterator group = Target.linear_curve_groups.begin(); group != Target.linear_curve_groups.end(); ++group)
					set_color((*group)->selected, ignore_selection, new_color, default_color, (*group)->constant_data);
			}

		if(m_tag_cubic_curve_groups.property_value())
			{
				for(k3d::mesh::cubic_curve_groups_t::iterator group = Target.cubic_curve_groups.begin(); group != Target.cubic_curve_groups.end(); ++group)
					set_color((*group)->selected, ignore_selection, new_color, default_color, (*group)->constant_data);
			}

		if(m_tag_bilinear_patches.property_value())
			{
				for(k3d::mesh::bilinear_patches_t::iterator patch = Target.bilinear_patches.begin(); patch != Target.bilinear_patches.end(); ++patch)
					set_color((*patch)->selected, ignore_selection, new_color, default_color, (*patch)->uniform_data);
			}

		if(m_tag_bicubic_patches.property_value())
			{
				for(k3d::mesh::bicubic_patches_t::iterator patch = Target.bicubic_patches.begin(); patch != Target.bicubic_patches.end(); ++patch)
					set_color((*patch)->selected, ignore_selection, new_color, default_color, (*patch)->uniform_data);
			}

		for(k3d::mesh::polyhedra_t::iterator polyhedron = Target.polyhedra.begin(); polyhedron != Target.polyhedra.end(); ++polyhedron)
			{
				if(m_tag_polyhedra.property_value())
					{
						set_color((*polyhedron)->selected, ignore_selection, new_color, default_color, (*polyhedron)->constant_data);
					}

				if(m_tag_edges.property_value())
					{
						for(k3d::polyhedron::edges_t::const_iterator edge = (*polyhedron)->edges.begin(); edge != (*polyhedron)->edges.end(); ++edge)
							set_color((*edge)->selected, ignore_selection, new_color, default_color, (*edge)->facevarying_data);
					}

				if(m_tag_faces.property_value())
					{
						for(k3d::polyhedron::faces_t::const_iterator face = (*polyhedron)->faces.begin(); face != (*polyhedron)->faces.end(); ++face)
							set_color((*face)->selected, ignore_selection, new_color, default_color, (*face)->uniform_data);
					}
			}
	}
	
	k3d::iplugin_factory& factory()
	{
		return get_factory();
	}

	static k3d::iplugin_factory& get_factory()
	{
		static k3d::plugin_factory<
			k3d::document_plugin<tag_color_implementation>,
			k3d::interface_list<k3d::imesh_source,
			k3d::interface_list<k3d::imesh_sink > > > factory(
				k3d::uuid(0x852e9ffb, 0x8e8b42f3, 0x993eb194, 0x5c342ca4),
				"TagColor",
				"Tags geometry with a color",
				"Objects",
				k3d::iplugin_factory::EXPERIMENTAL);

		return factory;
	}

private:
	k3d_data_property(k3d::color, k3d::immutable_name, k3d::change_signal, k3d::with_undo, k3d::local_storage, k3d::no_constraint) m_new_color;
	k3d_data_property(k3d::color, k3d::immutable_name, k3d::change_signal, k3d::with_undo, k3d::local_storage, k3d::no_constraint) m_default_color;
	k3d_data_property(bool, k3d::immutable_name, k3d::change_signal, k3d::with_undo, k3d::local_storage, k3d::no_constraint) m_tag_points;
	k3d_data_property(bool, k3d::immutable_name, k3d::change_signal, k3d::with_undo, k3d::local_storage, k3d::no_constraint) m_tag_point_groups;
	k3d_data_property(bool, k3d::immutable_name, k3d::change_signal, k3d::with_undo, k3d::local_storage, k3d::no_constraint) m_tag_edges;
	k3d_data_property(bool, k3d::immutable_name, k3d::change_signal, k3d::with_undo, k3d::local_storage, k3d::no_constraint) m_tag_faces;
	k3d_data_property(bool, k3d::immutable_name, k3d::change_signal, k3d::with_undo, k3d::local_storage, k3d::no_constraint) m_tag_polyhedra;
	k3d_data_property(bool, k3d::immutable_name, k3d::change_signal, k3d::with_undo, k3d::local_storage, k3d::no_constraint) m_tag_linear_curve_groups;
	k3d_data_property(bool, k3d::immutable_name, k3d::change_signal, k3d::with_undo, k3d::local_storage, k3d::no_constraint) m_tag_cubic_curve_groups;
	k3d_data_property(bool, k3d::immutable_name, k3d::change_signal, k3d::with_undo, k3d::local_storage, k3d::no_constraint) m_tag_bilinear_patches;
	k3d_data_property(bool, k3d::immutable_name, k3d::change_signal, k3d::with_undo, k3d::local_storage, k3d::no_constraint) m_tag_bicubic_patches;
};

/////////////////////////////////////////////////////////////////////////////
// tag_color_factory

k3d::iplugin_factory& tag_color_factory()
{
	return tag_color_implementation::get_factory();
}

} // namespace libk3dmesh

