 // 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 object, a default implementation of the iobject interface for use as a base class for document objects
		\author Tim Shead (tshead@k-3d.com)
*/

#include "application.h"
#include "icommand_tree.h"
#include "imouse_event_observer.h"
#include "iplugin_factory.h"
#include "iplugin_factory.h"
#include "iselection.h"
#include "object.h"

#include <algorithm>
#include <iostream>

namespace k3d
{

namespace detail
{

/// Concrete implementation of istate_container that adds a command node to the command tree
class add_command_node :
	public istate_container
{
public:
	add_command_node(icommand_node& Node, icommand_node& Parent) :
		m_node(Node),
		m_parent(Parent)
	{
	}
	
	~add_command_node()
	{
	}
	
	void restore_state()
	{
		application().command_tree().add_node(m_node, m_parent);
	}

private:
	icommand_node& m_node;
	icommand_node& m_parent;
};

/// Concrete implementation of istate_container that removes a command node from the command tree
class remove_command_node :
	public istate_container
{
public:
	remove_command_node(icommand_node& Node) :
		m_node(Node)
	{
	}

	~remove_command_node()
	{
	}
			
	void restore_state()
	{
		application().command_tree().remove_node(m_node);
	}

private:
	icommand_node& m_node;
};

} // namespace detail

/////////////////////////////////////////////////////////////////////////////
// object

object::object(idocument& Document) :
	command_node(""),
	property_collection(Document.dag()),
	m_document(Document),
	m_id(0),
	m_name(init_name("name") + init_description("Object name [string]") + init_value<std::string>("") +init_document(Document))
{
	register_property(m_name);

	// Add ourselves to the command tree ...
	application().command_tree().add_node(*this, dynamic_cast<icommand_node&>(m_document));

	// If we're recording undos ...
	istate_change_set* const changeset = m_document.state_recorder().current_change_set();
	if(changeset)
		{
//			changeset->undo_signal().connect(m_deleted_signal.slot());
			changeset->record_old_state(new detail::remove_command_node(*this));
			changeset->record_new_state(new detail::add_command_node(*this, dynamic_cast<icommand_node&>(m_document)));
		}

	// We want to know when we're deleted
	m_deleted_signal.connect(SigC::slot(*this, &object::on_deleted));
	// We want to know whenever our name changes
	m_name.changed_signal().connect(SigC::slot(*this, &object::on_name_changed));
	// We want to know whenever a request comes in to deselect everything
	m_document.selection().deselect_all_signal().connect(SigC::slot(*this, &object::on_deselect_all));
}

object::~object()
{
}

const iobject::id_type object::id()
{
	return m_id;
}

void object::set_id(const id_type ID)
{
	m_id = ID;
}

void object::set_name(const std::string Name)
{
	m_name.set_value(Name);
}

const std::string object::name()
{
	return m_name.value();
}

void object::on_deleted()
{
	// Remove ourselves from the command tree ...
	application().command_tree().remove_node(*this);

	// Signal that our properties are going away ...
	properties_t props(properties());
	for(properties_t::iterator property = props.begin(); property != props.end(); ++property)
		(*property)->deleted_signal().emit();

	// If we're recording undos ...
	istate_change_set* const changeset = m_document.state_recorder().current_change_set();
	if(changeset)
		{
			changeset->record_old_state(new detail::add_command_node(*this, dynamic_cast<icommand_node&>(m_document)));
			changeset->record_new_state(new detail::remove_command_node(*this));
		}
}

void object::on_name_changed()
{
	set_command_node_name(m_name.value());
}

void object::on_deselect_all()
{
	deselect();
}

idocument& object::document()
{
	return m_document;
}

iobject::deleted_signal_t& object::deleted_signal()
{
	return m_deleted_signal;
}

iobject::name_changed_signal_t& object::name_changed_signal()
{
	return m_name.changed_signal();
}

iobject::visibility_changed_signal_t& object::visibility_changed_signal()
{
	return m_visibility_changed_signal;
}

} // namespace k3d


