// 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 command_node_inspector class
		\author Tim Shead (tshead@k-3d.com)
*/

#include "command_node_inspector.h"

#include <k3dsdk/application.h>
#include <k3dsdk/icommand_node.h>
#include <k3dsdk/icommand_tree.h>
#include <k3dsdk/iproperty_collection.h>
#include <k3dsdk/iproperty.h>
#include <k3dsdk/result.h>

#include <sdpgtk/sdpgtkevents.h>

namespace
{

void insert_properties(sdpGtkCTree& Tree, GtkCTreeNode& ParentTreeNode, k3d::icommand_node& CommandNode)
{
	k3d::iproperty_collection* const property_collection = dynamic_cast<k3d::iproperty_collection*>(&CommandNode);
	if(!property_collection)
		return;

	const k3d::iproperty_collection::properties_t properties = property_collection->properties();
	for(k3d::iproperty_collection::properties_t::const_iterator property = properties.begin(); property != properties.end(); ++property)
		{
			SDPCSTRING text[] =
			{
				"",
				(*property)->name().c_str(),
				(*property)->description().c_str()
			};

			Tree.InsertNode(&ParentTreeNode, 0, text, 0, 0, 0, 0, 0);
		}
}

void insert_node(sdpGtkCTree& Tree, GtkCTreeNode* const ParentTreeNode, k3d::icommand_node* const CommandNode)
{
	// Sanity checks ...
	return_if_fail(CommandNode);

	// Insert this node ...
	SDPCSTRING text[] =
	{
		CommandNode->command_node_name().c_str(),
		"",
		""
	};

	GtkCTreeNode* const node = Tree.InsertNode(ParentTreeNode, 0, text, 0, 0, 0, 0, 0);
	Tree.SetRowData(node, CommandNode);

	// Insert properties ...
	insert_properties(Tree, *node, *CommandNode);

	// Recursively insert children ...
	k3d::icommand_tree::children_t children = k3d::application().command_tree().children(*CommandNode);
	for(k3d::icommand_tree::children_t::iterator child = children.begin(); child != children.end(); ++child)
		insert_node(Tree, node, *child);
}

} // namespace

namespace k3d
{

/////////////////////////////////////////////////////////////////////////////
// command_node_inspector

command_node_inspector::command_node_inspector(k3d::icommand_node& Parent) :
	base(&Parent, "command_node_inspector", new k3d::options_window_geometry_store()),
	m_Selection(0),
	m_idle_handler(0)
{
	// We want to be notified whenever the command tree is modified
	m_command_tree_connection = k3d::application().command_tree().changed_signal().connect(SigC::slot(*this, &command_node_inspector::on_command_tree_changed));
	// We want to be notified when the application closes
	k3d::application().close_signal().connect(SigC::slot(*this, &command_node_inspector::on_application_closed));

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

	// Populate our controls ...
	reset_control();

	Show();
}

command_node_inspector::~command_node_inspector()
{
	if(m_idle_handler)
		gtk_idle_remove(m_idle_handler);

	m_command_tree_connection.disconnect();
}

void command_node_inspector::on_command_tree_changed()
{
	reset_control();
}

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

// Custom event handling ...
void command_node_inspector::OnEvent(sdpGtkEvent* Event)
{
	assert_warning(Event);
	
	if(Event->Name() == "ontreeclicked")
		on_tree_clicked(Event);
	else
		base::OnEvent(Event);
}

void command_node_inspector::on_tree_clicked(sdpGtkEvent* Event)
{
	assert_warning(Event);
	
	// Get the row we clicked on ...
	int row = -1;
	int column = -1;
	sdpGtkEventWidgetButtonPressEvent* event = (sdpGtkEventWidgetButtonPressEvent*)Event;
	CTree("tree").GetHitInfo(gint(event->Event()->x), gint(event->Event()->y), &row, &column);

	if(-1 == row)
		return;

	// See if this is a command-node (it might just be a property)
	k3d::icommand_node* const node = reinterpret_cast<k3d::icommand_node*>(CList("tree").GetRowData(row));
	if(!node)
		return;

	// Make this a recordable event ...
	k3d::record_command(*node, k3d::icommand_node::command_t::USER_INTERFACE, "highlight");

	// Highlight the node ...
	node->execute_command("highlight", "");
}

void command_node_inspector::reset_control()
{
	if(!m_idle_handler)
		m_idle_handler = gtk_idle_add(raw_reset, this);
}

gint command_node_inspector::raw_reset(gpointer Data)
{
	return reinterpret_cast<command_node_inspector*>(Data)->reset();
}

gint command_node_inspector::reset()
{
	// Get the tree ...
	sdpGtkCTree tree = CTree("tree");

	// Start out fresh ...
	tree.Clear();

	// Freeze updates ...
	tree.Freeze();

	// Create the tree structure ...
	insert_node(tree, 0, dynamic_cast<k3d::icommand_node*>(&k3d::application()));

	// Setup our columns ...
	tree.ShowColumnTitles();
	tree.SetColumnTitle(0, "Nodes");
	tree.SetColumnTitle(1, "Properties");
	tree.SetColumnTitle(2, "Descriptions");
	for(int column = 0; column < 3; column++)
		{
			tree.SetColumnVisibility(column, true);
			tree.SetColumnWidth(column, tree.GetOptimalColumnWidth(column));
		}

	// Restore normal tree updates ...
	tree.Thaw();

	m_idle_handler = 0;
	return false;
}

} // namespace k3d

