// 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 tutorial_recorder class, which provides a UI for interactively recording scripts
		\author Tim Shead (tshead@k-3d.com)
*/

#include "tutorial_recorder.h"

#include <k3dsdk/application.h>
#include <k3dsdk/ideletable.h>
#include <k3dsdk/iscript_engine.h>
#include <k3dsdk/iuser_interface.h>
#include <k3dsdk/classes.h>
#include <k3dsdk/plugins.h>
#include <k3dsdk/result.h>
#include <k3dsdk/scripting.h>
#include <k3dsdk/user_interface.h>

#include <sdpgtk/sdpgtkevents.h>

#include "gtkurl/gtkurl.h"

/////////////////////////////////////////////////////////////////////////////
// tutorial_recorder

namespace
{

const std::string control_record = "record";
const std::string control_stop = "stop";
const std::string control_play_from_cursor = "play_from_cursor";
const std::string control_recordmessage = "recordmessage";
const std::string control_play = "play";

} // namespace

namespace k3d
{

tutorial_recorder::tutorial_recorder(k3d::icommand_node& Parent) :
	base(&Parent, "tutorialrecorder"),
	m_script_engine(dynamic_cast<k3d::iscript_engine*>(k3d::create_application_plugin(k3d::classes::JavaScriptEngine()))),
	m_recording(true)
{
	// Sanity checks ...
	assert_warning(m_script_engine);

	// We want to be asked before closing the application
	k3d::application().safe_to_close_signal().connect(SigC::slot(*this, &tutorial_recorder::safe_to_overwrite));
	// We want to be notified if the application is closed ...
	k3d::application().close_signal().connect(SigC::slot(*this, &tutorial_recorder::on_application_closed));

	// Load dialog template ...
	return_if_fail(LoadGTKMLTemplate("tutorial_recorder.gtkml"));

	// Enable hot-clickable URLs in the message area ...
	gtkurl_attach(Text("message"));

	// We want to be notified as command events occur ...
	k3d::application().command_signal().connect(SigC::slot(*this, &tutorial_recorder::on_command));

	ToggleButton(control_record).SetState(m_recording);
	update_titlebar();

	// Attach scrollbars to the text widgets ...
	GtkText* message = Text("message");
	Scrollbar("messagescrollbar").SetAdjustment(message->vadj);

	Show();
}

tutorial_recorder::~tutorial_recorder()
{
	delete dynamic_cast<k3d::ideletable*>(m_script_engine);
}

void tutorial_recorder::on_application_closed()
{
	delete this;
}

void tutorial_recorder::on_command(k3d::icommand_node* CommandNode, k3d::icommand_node::command_t::type_t Type, std::string Command, std::string Arguments)
{
	// Sanity checks ...
	return_if_fail(CommandNode);
	return_if_fail(Command.size());

	// If we aren't recording ...
	if(!m_recording)
		return;

	// Skip everything but UI events ...
	if(Type != k3d::icommand_node::command_t::USER_INTERFACE)
		return;

	std::string buffer;
	m_script_engine->convert_command(*CommandNode, Command, Arguments, buffer);
	buffer += "\n";

	gint position = text_control().GetLength();
	text_control().InsertText(buffer.c_str(), buffer.size(), &position);
}

const std::string tutorial_recorder::get_title()
{
	if(filepath().empty())
		return "Untitled Tutorial";
	
	return filepath().leaf();	
}

const std::string tutorial_recorder::get_titlebar_text()
{
	std::string result = "Tutorial Recorder: " + base::get_titlebar_text();

	if(m_recording)
		result += " [recording]";

	return result;
}

void tutorial_recorder::on_create_new_document()
{
	std::string text(get_text());
	m_script_engine->bless_script(text);

	set_text(text);
}

void tutorial_recorder::OnEvent(sdpGtkEvent* Event)
{
	if(Event->Name() == control_record)
		on_record();
	else if(Event->Name() == control_stop)
		on_stop();
	else if(Event->Name() == control_play_from_cursor)
		on_play_from_cursor();
	else if(Event->Name() == control_recordmessage)
		on_record_message();
	else
		base::OnEvent(Event);
}

void tutorial_recorder::on_record()
{
	m_recording = ToggleButton(control_record).GetState();

	if(m_recording)
		{
			Widget(control_stop).SetSensitive(true);
			Widget(control_play).SetSensitive(false);
			Widget(control_play_from_cursor).SetSensitive(false);
			Widget(control_recordmessage).SetSensitive(true);
		}
	else
		{
			Widget(control_stop).SetSensitive(false);
			Widget(control_play).SetSensitive(true);
			Widget(control_play_from_cursor).SetSensitive(true);
			Widget(control_recordmessage).SetSensitive(false);
		}

	update_titlebar();
}

void tutorial_recorder::on_stop()
{
	ToggleButton(control_record).SetState(false);
}

void tutorial_recorder::on_play_from_cursor()
{
	start_running();

	// We have to get the entire script first, so we can identify the correct scripting engine ...
	k3d::iplugin_factory* const language = k3d::recognize_script_language(get_text());
	if(language)
		{
			const std::string script = text_control().GetText(text_control().GetPosition(), -1);
			if(!k3d::execute_script(script, get_title(), k3d::iscript_engine::context_t(), language->class_id()))
				{
					k3d::error_message("Error executing script", "Play " + get_title() + ":");
				}
		}
	else
		{
			k3d::error_message(
				"Could not determine scripting language.  K-3D supports multiple scripting languages, but the language for this script was\n"
				"not recognized. Most K-3D script engines use some type of \"magic token\" at the beginning of a script to recognize it,\n"
				"e.g. \"//javascript\" in the first 12 characters of a script for K-3D's built-in JavaScript engine.  If you are writing a K-3D script,\n"
				"check the documentation for the scripting language you're writing in to see how to make it recognizable.",
				"Play " + get_title() + ":");
		}

	stop_running();
}

void tutorial_recorder::on_record_message()
{
	// Get the message text ...
	const std::string message = Text("message").GetText();

	// Display it for the user ...
	if(k3d::application().user_interface())
		k3d::application().user_interface()->tutorial_message(message);
}

} // namespace k3d

