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

#include "data.h"
#include "high_res_timer.h"
#include "ideletable.h"
#include "idocument.h"
#include "imouse_event_observer.h"
#include "iplugin_factory.h"
#include "iproperty_collection.h"
#include "istate_recorder.h"
#include "objects.h"
#include "string_modifiers.h"
#include "utility.h"

#include <boost/spirit/core.hpp>
using namespace boost::spirit;

namespace k3d
{

namespace detail
{

template<typename functor_t>
struct class_id_filter_t
{
	explicit class_id_filter_t(const uuid ClassID, functor_t Functor) : class_id(ClassID), functor(Functor) {}

	void operator()(k3d::iobject* Object) { if(Object->factory().class_id() == class_id) functor(Object); }

	const uuid class_id;
	functor_t functor;
};

template<typename functor_t>
class_id_filter_t<functor_t> class_id_filter(const uuid ID, functor_t Functor)
{
	return class_id_filter_t<functor_t>(ID, Functor);
}

template<typename functor_t>
struct name_filter_t
{
	explicit name_filter_t(const std::string Name, functor_t Functor) : name(Name), functor(Functor) {}

	void operator()(k3d::iobject* Object)
	{
		if(Object->name() == name)
			{
				functor(Object);
			}
	}

	const std::string name;
	functor_t functor;
};

template<typename functor_t>
name_filter_t<functor_t> name_filter(const std::string Name, functor_t Functor)
{
	return name_filter_t<functor_t>(Name, Functor);
}

} // namespace detail

iobject* find_object(iobject_collection& Objects, const iobject::id_type ObjectID)
{
	const objects_t::const_iterator end = Objects.collection().end();
	for(objects_t::const_iterator object = Objects.collection().begin(); object != end; ++object)
		if((*object)->id() == ObjectID)
			return *object;

	return 0;
}

iobject* find_object(iobject_collection& Objects, iproperty& Property)
{
	const objects_t::const_iterator end = Objects.collection().end();
	for(objects_t::const_iterator object = Objects.collection().begin(); object != end; ++object)
		{
			iproperty_collection* const property_collection = dynamic_cast<iproperty_collection*>(*object);
			if(!property_collection)
				continue;

			const iproperty_collection::properties_t& properties = property_collection->properties();
			if(std::find(properties.begin(), properties.end(), &Property) != properties.end())
				return *object;
		}

	return 0;
}

const objects_t find_objects(iobject_collection& Objects, const uuid ClassID)
{
	objects_t results;
	std::for_each(Objects.collection().begin(), Objects.collection().end(), detail::class_id_filter(ClassID, inserter(results)));

	return results;
}

const objects_t find_objects(iobject_collection& Objects, const std::string ObjectName)
{
	objects_t results;
	std::for_each(Objects.collection().begin(), Objects.collection().end(), detail::name_filter(ObjectName, inserter(results)));

	return results;
}

const std::string unique_name(iobject_collection& Objects, const std::string& Name)
{
	// For each object in the collection ...
	for(k3d::iobject_collection::objects_t::const_iterator handle = Objects.collection().begin(); handle != Objects.collection().end(); handle++)
		{
			iobject* const object = *handle;

			// Name doesn't match, so keep going ...
			if(Name != object->name())
				continue;

			// Got a duplicate name, so recursively try something else ...
			std::string base(Name);
			unsigned int copy = 1;

			parse(Name.c_str(), lexeme_d[*(anychar_p - space_p)][assign(base)] >> !(int_p[assign(copy)]), space_p);

			return unique_name(Objects, k3d::trim(base) + ' ' + k3d::to_string(copy+1));
		}

	return Name;
}

void delete_objects(idocument& Document, const objects_t& Objects)
{
	// If any of these objects have the mouse focus, take it away ...
	k3d::iobject* const mouse_focus = dynamic_cast<k3d::iobject*>(Document.mouse_focus());
	if(mouse_focus && std::count(Objects.begin(), Objects.end(), mouse_focus))
		Document.set_mouse_focus(0);

	// Let the objects know that they're about to be deleted ...
	for(objects_t::const_iterator object = Objects.begin(); object != Objects.end(); ++object)
		(*object)->deleted_signal().emit();

	// Remove them from the document object collection ...
	Document.objects().remove_objects(Objects);

	// Make sure the object gets cleaned-up properly after a redo ...
	for(objects_t::const_iterator object = Objects.begin(); object != Objects.end(); ++object)
		k3d::undoable_delete(dynamic_cast<k3d::ideletable*>(*object), Document);
}

} // namespace k3d


