// 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 command_node, which provides a default implementation of icommand_node
		\author Tim Shead (tshead@k-3d.com)
*/

#include "application.h"
#include "command_node.h"
#include "icommand_tree.h"
#include "idocument.h"
#include "iobject.h"
#include "result.h"

#include <functional>
#include <iostream>

namespace
{

class filepath
{
public:
	filepath(const std::string& Path) :
		m_path(Path)
	{
	}

	class iterator
	{
	public:
		std::string operator*() const
		{
			return m_path.substr(0, m_path.find("/"));
		}

		iterator& operator++()
		{
			m_path.erase(0, m_path.find("/"));
			m_path.erase(0, 1);
			return *this;
		}

		bool operator!=(const iterator& RHS)
		{
			return m_path != RHS.m_path;
		}

	private:
		friend class filepath;

		iterator()
		{
		}

		iterator(const std::string& Path) :
			m_path(Path)
		{
		}

		std::string m_path;
	};

	iterator begin() const
	{
		return iterator(m_path);
	}

	iterator end() const
	{
		return iterator();
	}

private:
	const std::string m_path;
};

class same_name :
	public std::unary_function<k3d::icommand_node*, bool>
{
public:
	same_name(const std::string& Name) :
		m_name(Name)
	{
	}

	bool operator()(k3d::icommand_node* Node) const
	{
		return Node->command_node_name() == m_name;
	}

private:
	const std::string m_name;
};

} // namespace

namespace k3d
{

/////////////////////////////////////////////////////////////////////////////
// record_command

void record_command(icommand_node& Node, const icommand_node::command_t::type_t Type, const std::string& Command, const std::string& Arguments)
{
	// Sanity checks ...
	assert_warning(Command.size());
	application().command_signal().emit(&Node, Type, Command, Arguments);
}

/////////////////////////////////////////////////////////////////////////////
// command_node_path

const std::string command_node_path(icommand_node& Node)
{
	std::string result;
	for(icommand_node* node = &Node; node; node = application().command_tree().parent(*node))
		result = "/" + node->command_node_name() + result;

	return result;
}

/////////////////////////////////////////////////////////////////////////////
// get_command_node

icommand_node* get_command_node(const std::string& Path)
{
	// Sanity checks ...
	return_val_if_fail(Path.size(), 0);
	return_val_if_fail(Path[0] == '/', 0);

	// At this point, we have an absolute path, so start with the root node, and get rid of the leading slash
	filepath path(Path.substr(1, std::string::npos));
	icommand_node* result = 0;

	// Loop through remaining path components
	for(filepath::iterator subpath = path.begin(); subpath != path.end(); ++subpath)
		{
			const icommand_tree::children_t children(application().command_tree().children(*result));
			const icommand_tree::children_t::const_iterator child = std::find_if(children.begin(), children.end(), same_name(*subpath));
			if(child != children.end())
				result = *child;
			else
				return 0; // We return quietly here, so objects can use get_command_node() to test for the existence of a node at runtime
		}

	return result;
}

/////////////////////////////////////////////////////////////////////////////
// get_command_node

icommand_node* get_command_node(icommand_node& Parent, const std::string& Child)
{
	// Sanity checks ...
	return_val_if_fail(Child.size(), 0);

	const icommand_tree::children_t children(application().command_tree().children(Parent));
	const icommand_tree::children_t::const_iterator child = std::find_if(children.begin(), children.end(), same_name(Child));
	if(child != children.end())
		return *child;
		
	return 0; // We return quietly here, so objects can use get_command_node() to test for the existence of a node at runtime
}

/////////////////////////////////////////////////////////////////////////////
// get_document

idocument* get_document(icommand_node& Node)
{
	// Look for an owning document by travelling up-the-chain
	for(icommand_node* node = &Node; node; node = application().command_tree().parent(*node))
		{
			idocument* const document = dynamic_cast<idocument*>(node);
			if(document)
				return document;
		}

	return 0;
}

/////////////////////////////////////////////////////////////////////////////
// get_object

iobject* get_object(icommand_node& Node)
{
	// Look for an owning object by travelling up-the-chain
	for(icommand_node* node = &Node; node; node = application().command_tree().parent(*node))
		{
			iobject* const object = dynamic_cast<iobject*>(node);
			if(object)
				return object;
		}

	return 0;
}

/////////////////////////////////////////////////////////////////////////////
// get_parent

icommand_node& get_parent(icommand_node& Node)
{
	icommand_node* const parent = application().command_tree().parent(Node);
	return parent ? *parent : Node;
}

/////////////////////////////////////////////////////////////////////////////
// is_descendant

bool is_descendant(icommand_node& Parent, icommand_node& Descendant)
{
	// Look for the parent object by travelling up-the-chain
	for(icommand_node* node = &Descendant; node; node = application().command_tree().parent(*node))
		{
			if(node == &Parent)
				return true;
		}

	return false;
}

/////////////////////////////////////////////////////////////////////////////
// command_node

command_node::command_node(const std::string& Name) :
	m_name(Name)
{
}

command_node::~command_node()
{
}

const std::string command_node::command_node_name()
{
	return m_name;
}

const icommand_node::commands_t command_node::commands()
{
	return m_commands;
}

bool command_node::execute_command(const std::string& Name, const std::string& Arguments)
{
	std::cerr << error << "Command node [" << m_name << "] unhandled command: [" << Name << "] [" << Arguments << "]" << std::endl;
	return false;
}

void command_node::set_command_node_name(const std::string& Name)
{
	if(Name != m_name)
		{
			m_name = Name;
			application().command_tree().changed_signal().emit();
		}
}

void command_node::register_command(const std::string& Name, const std::string& Description, const command_t::type_t Type, const std::string& Command)
{
	// Sanity checks ...
	assert_warning(Name.size());
	assert_warning(Command.size());

	m_commands.push_back(command_t(Name, Description, Type, Command));
}

} // namespace k3d


