// 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 k3d::property_button namespace, which provides a MVC UI for boolean data sources
		\author Tim Shead (tshead@k-3d.com)
*/

#include "connect_properties.h"
#include "gtkml.h"
#include "property_button.h"

#include <k3dsdk/application.h>
#include <k3dsdk/channel.h>
#include <k3dsdk/color.h>
#include <k3dsdk/state_change_set.h>
#include <k3dsdk/ibezier_channel.h>
#include <k3dsdk/idag.h>
#include <k3dsdk/idocument.h>
#include <k3dsdk/imesh_source.h>
#include <k3dsdk/iproperty.h>
#include <k3dsdk/istate_recorder.h>
#include <k3dsdk/iuser_interface.h>
#include <k3dsdk/mesh.h>
#include <k3dsdk/objects.h>
#include <k3dsdk/ioptions.h>
#include <k3dsdk/plugins.h>
#include <k3dsdk/property.h>
#include <k3dsdk/result.h>
#include <k3dsdk/time_source.h>

#include <sdpgtk/sdpgtkloopevents.h>
#include <sdpgtk/sdpgtkutility.h>

#include <boost/filesystem/fstream.hpp>

namespace
{

const std::string control_clicked = "clicked";
const std::string control_linked = "linked";
const std::string control_unlinked = "unlinked";

sdpxml::Document& gtkml_template()
{
	static sdpxml::Document gtkml("empty");
	if(gtkml.Name() == "empty")
		{
			const boost::filesystem::path template_path(k3d::preferred_language_template(k3d::dialog_template_path() / "property_button.gtkml"));
			boost::filesystem::ifstream stream(template_path);
			assert_warning(gtkml.Load(stream, template_path.native_file_string()));
		}
	
	return gtkml;
}

} // namespace

namespace k3d
{

namespace property_button
{

/////////////////////////////////////////////////////////////////////////////
// control

control::control(iunknown* const CommandNodeParent, const std::string CommandNodeName) :
	base(CommandNodeParent, CommandNodeName)
{
	return_if_fail(load_gtkml(gtkml_template(), "builtin property channel button template", *this));
	RootWidget().Show();
}

control::~control()
{
	// No more events from this point forward ...
	DisconnectAllEvents();

	// Clean-up the GTK+ tree ...
	if(Root())
		RootWidget().Destroy();
}

const std::string control::CustomType() const
{
	return std::string("k3dpropertychannelbutton");
}

bool control::Create(sdpGtkIObjectContainer* const ObjectContainer, sdpxml::Document& Document, sdpxml::Element& Element)
{
	// Sanity checks ...
	assert_warning(ObjectContainer);
	assert_warning(Element.Name() == "k3dpropertychannelbutton");

	return true;
}

bool control::attach(std::auto_ptr<idata_proxy> Data, k3d::istate_recorder* const StateRecorder, const std::string StateChangeName)
{
	// Sanity checks ...
	return_val_if_fail(Data.get(), false);

	// Take ownership of the data source ...
	m_data = Data;

	// Complete our own initialization ...
	return_val_if_fail(base::Attach(StateRecorder, StateChangeName), false);

	// Update the display ...
	update();

	// We want to be notified as dependencies are updated ...
	m_data->document().dag().dependency_signal().connect(SigC::slot(*this, &control::on_dependencies_changed));

	return true;
}

void control::on_dependencies_changed(const idag::dependencies_t& Dependencies)
{
	if(Dependencies.count(&m_data->property()))
		update();
}

void control::update()
{
	// Sanity checks ...
	return_if_fail(m_data.get());

	if(m_data->document().dag().dependency(m_data->property()))
		{
			Widget(control_linked).Show();
			Widget(control_unlinked).Hide();
		}
	else
		{
			Widget(control_linked).Hide();
			Widget(control_unlinked).Show();
		}
}

void control::OnEvent(sdpGtkEvent* Event)
{
	// Sanity checks ...
	assert_warning(Event);

	if(Event->Name() == control_clicked)
		on_clicked();
	else if(Event->Name() == "destroy")
		on_destroy();
	else
		base::OnEvent(Event);
}

void control::on_destroy()
{
	DisconnectAllEvents();
	Clear();
}

bool control::execute_command(const std::string& Command, const std::string& Arguments)
{
	// Sanity checks ...
	if(Command == "activate")
	{
		InteractiveActivateButton("button", application().options().tutorial_speed(), true);
		return true;
	}

	return base::execute_command(Command, Arguments);
}

void control::on_create_object_set_connection(iplugin_factory* Factory)
{
	return_if_fail(Factory);

	// See if we have a time property available that we can connect to ...
	iproperty* const time_property = get_time(m_data->document());
	return_if_fail(time_property);
				
	// Turn this into an undo/redo -able event ...
	if(m_StateRecorder)
		m_StateRecorder->start_recording(create_state_change_set());

	iobject* const property_object = find_object(m_data->document().objects(), m_data->property());
	const std::string name = property_object ? property_object->name() + "." : std::string();
	
	iobject* const object = create_document_plugin(*Factory, m_data->document(), unique_name(m_data->document().objects(), name + m_data->property().name()));
	if(object)
		{
			channel::set_constant_value(*object, m_data->property().value());

			iproperty* const channel_input = get_property(*object, "input");
			iproperty* const channel_output = get_property(*object, "output");
			iproperty* const input = &m_data->property();
			
			idag::dependencies_t dependencies;
			dependencies[channel_input] = time_property;
			dependencies[input] = channel_output;
			m_data->document().dag().set_dependencies(dependencies);
		}

	// Turn this into an undo/redo -able event ...
	if(m_StateRecorder)
		m_StateRecorder->commit_change_set(m_StateRecorder->stop_recording(), m_StateChangeName + " Set New Dependency");

	if(object && application().user_interface())
		application().user_interface()->show(*object);
}

void control::on_set_connection()
{
	create_connect_properties_dialog(m_data->document(), 0, 0, find_object(m_data->document().objects(), m_data->property()), &m_data->property());
}

void control::on_edit_connected(iobject* Object)
{
	return_if_fail(Object);
	
	if(application().user_interface())
		application().user_interface()->show(*Object);
}

void control::on_remove_connection()
{
	record_state_change_set changeset(m_data->document(), m_StateChangeName + " Remove Connection");

	idag::dependencies_t dependencies;
	dependencies[&m_data->property()] = static_cast<iproperty*>(0);
	m_data->document().dag().set_dependencies(dependencies);
}

void control::on_delete_connected(iobject* Object)
{
	return_if_fail(Object);

	record_state_change_set changeset(m_data->document(), m_StateChangeName + " Delete " + Object->name());

	idag::dependencies_t dependencies;
	dependencies[&m_data->property()] = static_cast<iproperty*>(0);
	m_data->document().dag().set_dependencies(dependencies);

	objects_t objects;
	objects.insert(Object);
	delete_objects(m_data->document(), objects);
}

void control::on_clicked()
{
	m_context_new_object_menu = dynamic_menu::control<>();
	m_context_menu = dynamic_menu::control<>();

	// Make sure we've got some storage, first!
	return_if_fail(m_data.get());

	// Record the command for posterity (tutorials) ...
	application().command_signal().emit(this, icommand_node::command_t::USER_INTERFACE, "activate", "");

	factories_t factories;
/*
	if(typeid(mesh*) == m_data->property().type())
		factories = plugins<imesh_source>();
	else */if(typeid(double) == m_data->property().type())
		factories = plugins<ibezier_channel<void> >();
	else if(typeid(color) == m_data->property().type())
		factories = plugins<ibezier_channel<color> >();

	if(!factories.empty())
		{
			for(factories_t::const_iterator factory = factories.begin(); factory != factories.end(); ++factory)
				m_context_new_object_menu.push_back(dynamic_menu::item(this, "context_create_object_set_connection" + (*factory)->name(), (*factory)->name(), SigC::bind(SigC::slot(*this, &control::on_create_object_set_connection), *factory)));
			m_context_new_object_menu.build();
		
			m_context_menu.push_back(dynamic_menu::item("Create Object + Set Connection", m_context_new_object_menu));
			m_context_menu.push_back(dynamic_menu::item());
		}
		
	m_context_menu.push_back(dynamic_menu::item(this, "context_set_connection", "Set Connection", SigC::slot(*this, &control::on_set_connection)));

	iproperty* const dependency = m_data->document().dag().dependency(m_data->property());
	if(dependency)
		{
			iobject* const object = find_object(m_data->document().objects(), *dependency);
			m_context_menu.push_back(dynamic_menu::item(this, "context_edit_connected", "Edit Connected", SigC::bind(SigC::slot(*this, &control::on_edit_connected), object)));
			m_context_menu.push_back(dynamic_menu::item(this, "context_remove_connection", "Remove Connection", SigC::slot(*this, &control::on_remove_connection)));
			m_context_menu.push_back(dynamic_menu::item(this, "context_delete_connected", "Delete Connected", SigC::bind(SigC::slot(*this, &control::on_delete_connected), object)));
		}
			
	m_context_menu.build();
	m_context_menu.popup();
}

} // namespace property_button

} // namespace k3d


