/**
 * eGroupWare egw_action framework - Shortcut/Keyboard input manager
 *
 * @link http://www.egroupware.org
 * @author Andreas Stöckel <as@stylite.de>
 * @copyright 2011 by Andreas Stöckel
 * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
 * @package egw_action
 * @version $Id$
 */

/*egw:uses
	jquery.jquery;
	egw_action;
*/

/**
 * Define the key constants (IE doesn't support "const" keyword)
 */

var EGW_KEY_BACKSPACE = 8;
var EGW_KEY_TAB = 9;
var EGW_KEY_ENTER = 13;
var EGW_KEY_ESCAPE = 27;
var EGW_KEY_DELETE = 46;

var EGW_KEY_SPACE = 32;

var EGW_KEY_PAGE_UP = 33;
var EGW_KEY_PAGE_DOWN = 34;

var EGW_KEY_ARROW_LEFT = 37;
var EGW_KEY_ARROW_UP = 38;
var EGW_KEY_ARROW_RIGHT = 39;
var EGW_KEY_ARROW_DOWN = 40;

var EGW_KEY_0 = 48;
var EGW_KEY_1 = 49;
var EGW_KEY_2 = 50;
var EGW_KEY_3 = 51;
var EGW_KEY_4 = 52;
var EGW_KEY_5 = 53;
var EGW_KEY_6 = 54;
var EGW_KEY_7 = 55;
var EGW_KEY_8 = 56;
var EGW_KEY_9 = 57;

var EGW_KEY_A = 65;
var EGW_KEY_B = 66;
var EGW_KEY_C = 67;
var EGW_KEY_D = 68;
var EGW_KEY_E = 69;
var EGW_KEY_F = 70;
var EGW_KEY_G = 71;
var EGW_KEY_H = 72;
var EGW_KEY_I = 73;
var EGW_KEY_J = 74;
var EGW_KEY_K = 75;
var EGW_KEY_L = 76;
var EGW_KEY_M = 77;
var EGW_KEY_N = 78;
var EGW_KEY_O = 79;
var EGW_KEY_P = 80;
var EGW_KEY_Q = 81;
var EGW_KEY_R = 82;
var EGW_KEY_S = 83;
var EGW_KEY_T = 84;
var EGW_KEY_U = 85;
var EGW_KEY_V = 86;
var EGW_KEY_W = 87;
var EGW_KEY_X = 88;
var EGW_KEY_Y = 89;
var EGW_KEY_Z = 90;

var EGW_KEY_MENU = 93;

var EGW_KEY_F1 = 112;
var EGW_KEY_F2 = 113;
var EGW_KEY_F3 = 114;
var EGW_KEY_F4 = 115;
var EGW_KEY_F5 = 116;
var EGW_KEY_F6 = 117;
var EGW_KEY_F7 = 118;
var EGW_KEY_F8 = 119;
var EGW_KEY_F9 = 120;
var EGW_KEY_F10 = 121;
var EGW_KEY_F11 = 122;
var EGW_KEY_F12 = 123;

var EGW_VALID_KEYS = [
	8, 9, 13, 27, 46, 32, 33, 34, 37, 38, 39, 40, 48, 49, 50, 51, 52, 53, 54,
	55, 56, 57, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80,
	81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 93, 112, 113, 114, 115, 116, 117, 118,
	119, 120, 121, 122, 123
]

/**
 * The tranlation function converts the given native key code into one of the
 * egw key constants as listed above. This key codes were chosen to match the
 * key codes of IE and FF.
 */
var egw_keycode_translation_function = function(_nativeKeyCode) {
	// Map the numpad to the 0..9 keys
	if (_nativeKeyCode >= 96 && _nativeKeyCode <= 105)
	{
		_nativeKeyCode -= 48
	}

	return _nativeKeyCode;
}

/**
 * Checks whether the given keycode is in the list of valid key codes. If not,
 * returns -1.
 */
function egw_keycode_makeValid(_keyCode) {
	var idx = EGW_VALID_KEYS.indexOf(_keyCode);
	if (idx >= 0) {
		return _keyCode;
	}

	return -1;
}

function _egw_nodeIsInInput(_node)
{
	if ((_node != null) && (_node != document))
	{
		var tagName = _node.tagName.toLowerCase();
		if (tagName == "input" || tagName == "select" || tagName == 'textarea' || tagName == 'button')
		{
			return true;
		}
		else
		{
			return _egw_nodeIsInInput(_node.parentNode);
		}
	}
	else
	{
		return false;
	}
}

/**
 * Register the onkeypress handler on the document 
 */
$j(document).ready(function() {

	// Fetch the key down event and translate it into browser-independent and
	// easy to use key codes and shift states
	$j(document).keydown( function(e) {

		// Translate the given key code and make it valid
		var keyCode = e.which;
		keyCode = egw_keycode_translation_function(keyCode);
		keyCode = egw_keycode_makeValid(keyCode);

		// Only go on if this is a valid key code - call the key handler
		if (keyCode != -1)
		{
			// Check whether the event came from an input field - if yes, only
			// allow function keys (like F1) to be captured by our code
			var inInput = _egw_nodeIsInInput(e.target);
			if (!inInput || (keyCode >= EGW_KEY_F1 && keyCode <= EGW_KEY_F12))
			{
				if (egw_keyHandler(keyCode, e.shiftKey, e.ctrlKey || e.metaKey, e.altKey))
				{
					// If the key handler successfully passed the key event to some
					// sub component, prevent the default action
					e.preventDefault();
				}
			}
		}
	});
});

/**
 * Required to catch the context menu
 */
$j(window).on("contextmenu",document, function(event) {
	// Check for actual key press
	if(!(event.originalEvent.x == 1 && event.originalEvent.y == 1)) return true;
	if(!event.ctrlKey && egw_keyHandler(EGW_KEY_MENU, event.shiftKey, event.ctrlKey || event.metaKey, event.altKey))
	{
		// If the key handler successfully passed the key event to some
		// sub component, prevent the default action
		event.preventDefault();
		return false;
	}
	return true;
});


/**
 * Creates an unique key for the given shortcut
 */
function egw_shortcutIdx(_keyCode, _shift, _ctrl, _alt)
{
	return "_" + _keyCode + "_" + 
		(_shift ? "S" : "") +
		(_ctrl ? "C" : "") +
		(_alt ? "A" : "");
}

var egw_registeredShortcuts = {}

/**
 * Registers a global shortcut. If the shortcut already exists, it is overwritten.
 * @param int _keyCode is one of the keycode constants
 * @param bool _shift whether shift has to be set
 * @param bool _ctrl whether ctrl has to be set
 * @param bool _alt whether alt has to be set
 * @param function _handler the function which will be called when the shortcut
 * 	is evoked. An object containing the shortcut data will be passed as first
 * 	parameter.
 * @param object _context is the context in which the function will be executed
 */
function egw_registerGlobalShortcut(_keyCode, _shift, _ctrl, _alt, _handler, _context)
{
	// Generate the hash map index for the shortcut
	var idx = egw_shortcutIdx(_keyCode, _shift, _ctrl, _alt);

	// Register the shortcut
	egw_registeredShortcuts[idx] = {
		"handler": _handler,
		"context": _context,
		"shortcut": {
			"keyCode": _keyCode,
			"shift": _shift,
			"ctrl": _ctrl,
			"alt": _alt
		}
	}
}

/**
 * Unregisters the given shortcut.
 */
function egw_unregisterGlobalShortcut(_keyCode, _shift, _ctrl, _alt) {
	// Generate the hash map index for the shortcut
	var idx = egw_shortcutIdx(_keyCode, _shift, _ctrl, _alt);

	// Delete the entry from the hash map
	delete egw_registeredShortcuts[idx];
}

/**
 * the egw_keyHandler function handles various key presses. The boolean
 * _shift, _ctrl, _alt values have been translated into platform independent
 * values (for apple devices).
 */
function egw_keyHandler(_keyCode, _shift, _ctrl, _alt) {

	// Check whether there is a global shortcut waiting for the keypress event
	var idx = egw_shortcutIdx(_keyCode, _shift, _ctrl, _alt);
	if (typeof egw_registeredShortcuts[idx] != "undefined")
	{
		var shortcut = egw_registeredShortcuts[idx];

		// Call the registered shortcut function and return its result.
		shortcut.handler.call(shortcut.context, shortcut.shortcut);

		return true;
	}
	else
	{
		// Pass the keypress to the currently focused action object

		// Get the object manager and fetch the container of the currently
		// focused object
		var appMgr = egw_getAppObjectManager(false);
		if (appMgr)
		{
			var focusedObject = appMgr.getFocusedObject();

			if (!focusedObject)
			{
				// If the current application doesn't have a focused object,
				// check whether the application object manager contains an object
				// with the EGW_AO_FLAG_DEFAULT_FOCUS flag set.
				var cntr = null;
				for (var i = 0; i < appMgr.children.length; i++)
				{
					var child = appMgr.children[i];
					if (egwBitIsSet(EGW_AO_FLAG_DEFAULT_FOCUS, child.flags))
					{
						cntr = child;
						break;
					}
				}

				// Get the first child of the found container and focus the first
				// object
				if (cntr && cntr.children.length > 0)
				{
					cntr.children[0].setFocused(true);
					focusedObject = cntr.children[0];
				}
			}

			if (focusedObject)
			{

				// Handle the default keys (arrow_up, down etc.)
				var cntr = focusedObject.getContainerRoot();
				var handled = false;

				if (cntr)
				{
					handled = cntr.handleKeyPress(_keyCode, _shift, _ctrl, _alt);
				}

				// Execute the egw_popup key handler of the focused object
				if (!handled) {
					return focusedObject.executeActionImplementation(
					{
						"keyEvent": {
							"keyCode": _keyCode,
							"shift": _shift,
							"ctrl": _ctrl,
							"alt": _alt
						}
					}, "popup", EGW_AO_EXEC_SELECTED);
				}

				return handled;
			}
		}

	}

	return false;
}