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

#include <k3dsdk/command_node.h>
#include "hotkey.h"
#include <k3dsdk/result.h>

#include <sdpgtk/sdpgtkutility.h>
#include <boost/tokenizer.hpp>

namespace
{

const k3d::key_modifiers convert(const std::string& Modifiers)
{
	typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
	boost::char_separator<char> whitespace_separator;
	tokenizer modifiers(Modifiers, whitespace_separator);

	k3d::key_modifiers result;
	for(tokenizer::iterator modifier = modifiers.begin(); modifier != modifiers.end(); ++modifier)
		{
			if(*modifier == "shift")
				result.set_shift();
			else if(*modifier == "control")
				result.set_control();
			else if(*modifier == "mod1")
				result.set_mod1();
			else if(*modifier == "mod2")
				result.set_mod2();
			else if(*modifier == "mod3")
				result.set_mod3();
			else if(*modifier == "mod4")
				result.set_mod4();
			else if(*modifier == "mod5")
				result.set_mod5();
		}

	return result;
}

} // namespace

namespace k3d
{

///////////////////////////////////////////////////////////////////////////////
// hotkey

hotkey::hotkey(k3d::icommand_node& Owner) :
	m_owner(Owner),
	m_context(OWNER_CONTEXT),
	m_key(0)
{
}

bool hotkey::load(sdpxml::Document& Document, sdpxml::Element& Element)
{
	// Sanity checks ...
	return_val_if_fail(Element.Name() == "k3dhotkey", false);

	// Get context (empty string is allowed - in which case we default to "local" context)
	std::string context;
	sdpGtkMarkAttribute(Document, Element, "context");
	sdpxml::ParseAttribute(Element, "context", context);

	if(context == "application")
		m_context = APPLICATION_CONTEXT;
	else if(context == "document")
		m_context = DOCUMENT_CONTEXT;
	else if(context == "object")
		m_context = OBJECT_CONTEXT;
	else if(context.empty() || context == "owner")
		m_context = OWNER_CONTEXT;

	// Get modifiers (empty string is allowed)
	std::string modifiers_text;
	sdpGtkMarkAttribute(Document, Element, "modifiers");
	sdpxml::ParseAttribute(Element, "modifiers", modifiers_text);
	m_modifiers = convert(modifiers_text);

	// Get the modifier mask (empty string is allowed)
	std::string mask_text;
	sdpGtkMarkAttribute(Document, Element, "mask");
	sdpxml::ParseAttribute(Element, "mask", mask_text);
	m_mask = convert(mask_text);

	// Get key (must be either a single ASCII character or 2-4 hex digits - preceding "0x" is optional)
	std::string key;
	sdpGtkMarkAttribute(Document, Element, "key");
	sdpxml::ParseAttribute(Element, "key", key);

	if(1== key.size())
		{
			m_key = *key.begin();
		}
	else
		{
			std::stringstream buffer(key);
			buffer >> std::hex >> m_key;
		}

	// Connect to the global keyboard object to receive key events ...
	m_keyboard_connection.disconnect();
	m_keyboard_connection = keyboard().event_signal().connect(SigC::slot(*this, &hotkey::on_key_event));

	return true;
}

bool hotkey::on_key_event(k3d::icommand_node& CommandNode, key_modifiers Modifiers, ikeyboard::key_value_t Key)
{
	// If the key doesn't match we're done ...
	if(Key != m_key)
		return false;

	// If the modifiers don't match we're done ...
	if((Modifiers & m_mask) != m_modifiers)
	       return false;

	// If the context doesn't match we're done ...
	switch(m_context)
		{
			case APPLICATION_CONTEXT:
				{
					break;
				}
			case DOCUMENT_CONTEXT:
				{
					k3d::idocument* const document = k3d::get_document(m_owner);
					if(!document)
						return false;

					if(document != k3d::get_document(CommandNode))
						return false;
					break;
				}
			case OBJECT_CONTEXT:
				{
					iobject* const object = k3d::get_object(m_owner);
					if(!object)
						return false;

					if(object != k3d::get_object(CommandNode))
						return false;
					break;
				}
			case OWNER_CONTEXT:
				{
					if(!k3d::is_descendant(k3d::get_parent(m_owner), CommandNode))
						return false;
					break;
				}
			default:
				assert(0); // You added a new context type!
		}

	m_event_signal.emit();
	return true;
}

hotkey::event_signal_t& hotkey::event_signal()
{
	return m_event_signal;
}

} // namespace k3d


