// 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::button::control class
		\author Anders Dahnielson (anders@dahnielson.com)
*/

#include "button.h"
#include "gtkml.h"

#include <k3dsdk/application.h>
#include <k3dsdk/idocument.h>
#include <k3dsdk/iobject.h>
#include <k3dsdk/ioptions.h>
#include <k3dsdk/result.h>
#include <k3dsdk/scripting.h>
#include <k3dsdk/string_modifiers.h>

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

#include <boost/filesystem/path.hpp>

#include <fstream>

// We have an unfortunate class with X ...
#ifdef RootWindow
#undef RootWindow
#endif // RootWindow

namespace k3d
{

namespace button
{

namespace detail
{

const std::string control_button("scriptbutton");

}

/////////////////////////////////////////////////////////////////////////////
// control::state

class control::state
{
public:
	/// Stores a filepath to a script
	boost::filesystem::path script_file;
	/// Stores an inline script
	std::string script;
};

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

control::control(k3d::iunknown* CommandNodeParent, const std::string& CommandNodeName) :
	base(CommandNodeParent, CommandNodeName),
	m_hotkey(*this),
	m_state(new state())
{
}

control::control(k3d::iunknown* CommandNodeParent, const std::string& CommandNodeName, const std::string& Label, const std::string& Pixmap) :
	base(CommandNodeParent, CommandNodeName),
	m_hotkey(*this),
	m_state(new state())
{
	// Create and load our UI template ...
	std::stringstream uitemplate;
	uitemplate << "<gtkml>";
	uitemplate << "<button name=\"scriptbutton\" relief=\"normal\">";
	
	if(Pixmap.empty())
		uitemplate << Label;
	else
		uitemplate << "<pixmap>" << Pixmap << "</pixmap>";
	
	uitemplate << "<event signal=\"clicked\" name=\"scriptbutton\"/>";
	uitemplate << "</button>";
	uitemplate << "</gtkml>\n";

	return_if_fail(load_gtkml(uitemplate, "button builtin template", *this));

	Widget(detail::control_button.c_str()).Object()->flags &= ~GTK_CAN_FOCUS;

	RootWidget().Show();
}

control::~control()
{
	delete m_state;
}

void control::show()
{
	Button("scriptbutton").Show();
}

void control::hide()
{
	Button("scriptbutton").Hide();
}

void control::set_sensitive(const bool Sensitive)
{
	Button("scriptbutton").SetSensitive(Sensitive);
}

control::activate_signal_t& control::signal_activate()
{
	return m_signal_activate;
}

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

	// Attributes ...
	std::string file;
	sdpGtkMarkAttribute(Document, Element, "file");
	sdpxml::ParseAttribute(Element, "file", file);
	m_state->script_file = boost::filesystem::path(file, boost::filesystem::native);

	std::string relief("normal");
	sdpGtkMarkAttribute(Document, Element, "relief");
	sdpxml::ParseAttribute(Element, "relief", relief);

	// Create and load our UI template ...
	std::stringstream uitemplate;
	uitemplate << "<gtkml>";
	uitemplate << "<button name=\"scriptbutton\" relief=\"" << relief << "\">" << sdpGtkInternationalText(Document, Element);

	// Children ...
	for(sdpxml::ElementCollection::iterator element = Element.Children().begin(); element != Element.Children().end(); element++)
		{
			if(element->Name() == "k3dhotkey" && m_hotkey.load(Document, *element))
				m_hotkey.event_signal().connect(SigC::slot(*this, &control::on_activate));
			else if(element->Name() == "script")
				m_state->script = k3d::trimleft(sdpGtkInternationalText(Document, *element));
			else if(element->Name() == "i18n")
				continue;
			else
				{
					sdpGtkMarkElement(Document, *element);
					uitemplate << *element;
				}
		}

	uitemplate << "<event signal=\"clicked\" name=\"scriptbutton\"/>";
	uitemplate << "</button>";
	uitemplate << "</gtkml>\n";

	return_val_if_fail(load_gtkml(uitemplate, "button builtin template", *this), false);

	Widget(detail::control_button.c_str()).Object()->flags &= ~GTK_CAN_FOCUS;

	RootWidget().Show();

	return true;
}

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

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

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

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

	if(Event->Name() == detail::control_button)
		on_activate();
	else
		base::OnEvent(Event);
}

void control::on_activate()
{
	// Make a copy of our state (in-case we are deleted during the course of event handling)
	const state current_state(*m_state);
	
	k3d::iscript_engine::context_t context;
	context.push_back(k3d::get_document(*this));
	context.push_back(k3d::get_object(*this));
	
	// Record the event for posterity (tutorials, macros, etc.)
	k3d::record_command(*this, k3d::icommand_node::command_t::USER_INTERFACE, "activate");

	// Notify observers ...
	m_signal_activate.emit();

	// Optionally run associated scripts ...
	if(!current_state.script.empty())
		{
			bool recognized = false;
			bool executed = false;

			k3d::execute_script(current_state.script, "inline", context, recognized, executed);

			if(!recognized)
				{
					std::cerr << error << __PRETTY_FUNCTION__ << " Could not determine inline script language" << std::endl;
					return;
				}

			if(!executed)
				{
					std::cerr << error << __PRETTY_FUNCTION__ << " Error executing inline script" << std::endl;
					return;
				}
		}

	if(!current_state.script_file.empty())
		{
			bool recognized = false;
			bool executed = false;

			boost::filesystem::ifstream file(current_state.script_file);
			k3d::execute_script(file, current_state.script_file.native_file_string(), context, recognized, executed);

			if(!recognized)
				{
					std::cerr << error << __PRETTY_FUNCTION__ << " Could not determine [" << current_state.script_file.native_file_string() << "] script language" << std::endl;
					return;
				}

			if(!executed)
				{
					std::cerr << error << __PRETTY_FUNCTION__ << " Error executing script [" << current_state.script_file.native_file_string() << "]" << std::endl;
					return;
				}
		}
}

} // namespace button

} // namespace k3d


