// 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 application_window class, which implements the per-application UI
		\author Tim Shead (tshead@k-3d.com)
*/

#include "about_box.h"
#include "application_window.h"
#include "black_box_recorder.h"
#include "button.h"
#include "command_node_inspector.h"
#include "document_window.h"
#include "k3ddialog.h"
#include "menu_item.h"
#include "options_dialog.h"
#include "script_editor.h"
#include "tutorial_menu.h"
#include "tutorial_recorder.h"

#include <k3dsdk/application.h>
#include <k3dsdk/classes.h>
#include <k3dsdk/idocument.h>
#include <k3dsdk/iuser_interface.h>
#include <k3dsdk/iviewport.h>
#include <k3dsdk/iviewport_host.h>
#include <k3dsdk/plugins.h>
#include <k3dsdk/property.h>
#include <k3dsdk/result.h>
#include <k3dsdk/scripting.h>
#include <k3dsdk/transform.h>
#include <k3dsdk/user_interface.h>

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

#include <fstream>
#include <iomanip>
#include <sstream>

#ifdef RootWindow
#undef RootWindow
#endif // RootWindow

namespace k3d
{

/////////////////////////////////////////////////////////////////////////////
// application_window::implementation

/// Implements the multi-window UI Application Window
class application_window::implementation :
	public k3dDialog
{
	typedef k3dDialog base;

public:
	implementation(icommand_node& Parent) :
		base(&Parent, "window", new options_window_geometry_store())
	{
		return_if_fail(LoadGTKMLTemplate("application_window.gtkml"));
		
		if(get_menu_item("file_new"))
			get_menu_item("file_new")->signal_activate().connect(SigC::slot(*this, &implementation::on_file_new));
		if(get_button("new_document"))
			get_button("new_document")->signal_activate().connect(SigC::slot(*this, &implementation::on_file_new));

		if(get_menu_item("tools_black_box_recorder"))
			get_menu_item("tools_black_box_recorder")->signal_activate().connect(SigC::slot(*this, &implementation::on_tools_black_box_recorder));
		if(get_menu_item("tools_record_tutorial"))
			get_menu_item("tools_record_tutorial")->signal_activate().connect(SigC::slot(*this, &implementation::on_tools_record_tutorial));
		if(get_menu_item("tools_command_node_inspector"))
			get_menu_item("tools_command_node_inspector")->signal_activate().connect(SigC::slot(*this, &implementation::on_tools_edit_command_node_inspector));
		if(get_menu_item("tools_script_editor"))
			get_menu_item("tools_script_editor")->signal_activate().connect(SigC::slot(*this, &implementation::on_tools_edit_script));
		if(get_menu_item("tools_play_script"))
			get_menu_item("tools_play_script")->signal_activate().connect(SigC::slot(*this, &implementation::on_tools_play_script));
		if(get_menu_item("tools_options"))
			get_menu_item("tools_options")->signal_activate().connect(SigC::slot(*this, &implementation::on_tools_options));
		if(get_menu_item("help_tutorials"))
			get_menu_item("help_tutorials")->signal_activate().connect(SigC::slot(*this, &implementation::on_help_tutorials));
		if(get_menu_item("help_online"))
			get_menu_item("help_online")->signal_activate().connect(SigC::slot(*this, &implementation::on_help_online));
		if(get_menu_item("help_manual"))
			get_menu_item("help_manual")->signal_activate().connect(SigC::slot(*this, &implementation::on_help_manual));
		if(get_menu_item("help_about"))
			get_menu_item("help_about")->signal_activate().connect(SigC::slot(*this, &implementation::on_help_about));

		// We want to be notified when the main app closes ...
		application().close_signal().connect(SigC::slot(*this, &implementation::on_application_close));

		// We want notifications whenever a new document is ready-for-use, so we can (optionally) create our document-specific UI ...
		application().new_document_ready_signal().connect(SigC::slot(*this, &implementation::on_new_document_ready));

		Show();
	}

	/// Populates a new document with some common objects to make the UI friendlier
	bool populate_document(idocument& Document)
	{
		k3d::iobject* const axes = create_document_plugin(k3d::classes::Axes(), Document, "Axes");
		return_val_if_fail(axes, false);
		
		k3d::iobject* const material = create_document_plugin(k3d::classes::RenderManMaterial(), Document, "Material");
		return_val_if_fail(material, false);
		
		k3d::iobject* const material_shader = create_document_plugin(k3d::classes::RenderManSurfaceShader(), Document, "MaterialShader");
		return_val_if_fail(material_shader, false);
		
		k3d::iobject* const light = create_document_plugin(k3d::classes::RenderManLight(), Document, "Light");
		return_val_if_fail(light, false);
		
		k3d::iobject* const light_shader = create_document_plugin(k3d::classes::RenderManLightShader(), Document, "LightShader");
		return_val_if_fail(light_shader, false);
		
		k3d::iobject* const camera = create_document_plugin(k3d::classes::RenderManEngine(), Document, "Camera");
		return_val_if_fail(camera, false);
		
		k3d::iobject* const camera_viewport = create_document_plugin(k3d::classes::Viewport(), Document, "CameraViewport");
		return_val_if_fail(camera_viewport, false);
		
		k3d::iobject* const time_source = create_document_plugin(k3d::classes::TimeSource(), Document, "TimeSource");
		return_val_if_fail(time_source, false);

		k3d::set_position(*light, k3d::vector3(-20.0, 20.0, -30.0));
		k3d::set_position(*camera, k3d::vector3(-20.0, 20.0, -20.0));
		k3d::set_orientation(*camera, k3d::angle_axis(k3d::quaternion(k3d::euler_angles(k3d::radians(0.0), k3d::radians(37.0), k3d::radians(45.0), k3d::euler_angles::ZXYstatic))));

		k3d::set_property_value(*material_shader, "shader_name", std::string("k3d_plastic"));		
		k3d::set_property_value(*material, "surface_shader", material_shader);
		
		k3d::set_property_value(*light_shader, "shader_name", std::string("k3d_pointlight"));
		k3d::set_property_value(*light, "shader", light_shader);

		k3d::idag::dependencies_t dependencies;
		dependencies.insert(std::make_pair(k3d::get_typed_property<k3d::matrix4>(*camera_viewport, "input_matrix"), k3d::get_typed_property<matrix4>(*camera, "output_matrix")));
		Document.dag().set_dependencies(dependencies);

		k3d::iviewport* const viewport = dynamic_cast<k3d::iviewport*>(camera_viewport);
		if(viewport)
			{
				k3d::iviewport_host* const viewport_host = dynamic_cast<k3d::iviewport_host*>(camera);
				if(viewport_host)
					viewport->set_host(viewport_host);

				if(k3d::application().user_interface())
					k3d::application().user_interface()->show_viewport(*viewport);
			}
			
		return true;
	}

private:
	/// Called by the signal system when the application is closed
	void on_application_close()
	{
		delete this;
	}

	/// Called by the signal system when a document is created
	void on_new_document_ready(idocument& Document)
	{
		new k3d::document_window(Document);
	}
	
	/// Called if the user clicks on the window manager close button
	void OnDelete(sdpGtkEvent* Event)
	{
		assert_warning(Event);
	
		// Don't allow GTK to close the window - we'll be closed later by the application ...
		((sdpGtkEventWidgetDeleteEvent*)Event)->SetResult(true);

		// Shut the application down ...
		if(application().safe_to_close_signal().emit())
			application().exit();
	}

	/// Called to create a new document
	void on_file_new()
	{
		k3d::idocument* const document = k3d::application().create_document();
		return_if_fail(document);

		populate_document(*document);
	}
	
	/// Called when the user wants to start the black box recorder for troubleshooting
	void on_tools_black_box_recorder()
	{
		boost::filesystem::path filepath;
		if(!get_file_path("log", "Record Black Box Data:", true, boost::filesystem::path(), filepath))
			return;

		new black_box_recorder(*dynamic_cast<icommand_node*>(&application()), filepath);
	}

	/// Called when the user wants to record a tutorial (interactive script)
	void on_tools_record_tutorial()
	{
		new tutorial_recorder(*dynamic_cast<icommand_node*>(&application()));
	}

	/// Called when the user wants to open a command node inspector window
	void on_tools_edit_command_node_inspector()
	{
		new command_node_inspector(*dynamic_cast<icommand_node*>(&application()));
	}

	/// Called when the user wants to edit / test scripts
	void on_tools_edit_script()
	{
		new script_editor(*dynamic_cast<icommand_node*>(&application()));
	}

	/// Called when the user wants to play a script
	void on_tools_play_script()
	{
		boost::filesystem::path filepath;
		if(!get_file_path("script", "Play K-3D Script:", false, boost::filesystem::path(), filepath))
			return;

		// Make it happen ...
		bool recognized = false;
		bool executed = false;
		boost::filesystem::ifstream file(filepath);

		execute_script(file, filepath.native_file_string(), iscript_engine::context_t(), recognized, executed);

		if(!recognized)
		{
			if(application().user_interface())
			application().user_interface()->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, e.g. \"//javascript\"\n"
				"in the first 12 characters of a script for K-3D's built-in JavaScript engine.  If you are writing a K-3D script, check the documentation\n"
				"for the scripting language you're writing in to see how to make it recognizable.",
				"Play " + filepath.native_file_string() + ":");
			return;
		}

		if(!executed)
		{
			if(application().user_interface())
				application().user_interface()->error_message("Error executing script", "Play " + filepath.native_file_string() + ":");
			return;
		}
	}

	/// Called when the user wants to modify the program options
	void on_tools_options()
	{
		new options_dialog(*dynamic_cast<icommand_node*>(&application()));
	}

	/// Called to display available interactive tutorials
	void on_help_tutorials()
	{
		create_tutorial_menu(dynamic_cast<icommand_node&>(application()));
	}

	/// Called to open the K-3D website, http://k3d.sourceforge.net
	void on_help_online()
	{
		if(application().user_interface())
			application().user_interface()->browser_navigate("http://www.k-3d.org");
	}

	/// Called to open the K-3D Manual
	void on_help_manual()
	{
		if(application().user_interface())
			application().user_interface()->browser_navigate((application().share_path() / "../doc/k3d/user_reference/index.html").native_file_string());
	}

	/// Called to display an "about" box with program version and copyright information
	void on_help_about()
	{
		new about_box();
	}
};

} // namespace

namespace k3d
{

application_window::application_window(icommand_node& ParentNode) :
	m_implementation(new implementation(ParentNode))
{
}

application_window::~application_window()
{
	delete m_implementation;
}

bool application_window::populate_document(idocument& Document)
{
	return m_implementation->populate_document(Document);
}

} // namespace k3d

