'use strict';

/**
 * Helper for simple and easy creating the shortcuts.
 */
window.nxShortcuts = {

	/** {Array} The list of all active shortcuts */
	_list: [],

	/**
	 * Add a shortcut.
	 *
	 * @param {string} command The key combination of the shortcut to add.
	 * @param {function} action The action to perform.
	 */
	add: function (command, action) {
		this._list.push({
			command: command,
			action: action
		});
	},

	/**
	 * Remove a shortcut
	 *
	 * @params {string} command The key combination of the shortcut to remove.
	 */
	remove: function (command) {
		for (var i in this._list) {
			var item = this._list[i];

			// Find and remove
			if (item.command === command) {
				this._list.splice(parseInt(i, 10), 1);
			}
		}
	},

	/**
	 * Get the list of all shortcuts.
	 *
	 * @return {object} The key is the key combination for the shortcut and value is the action to perform.
	 */
	getList: function () {
		return this._list;
	},

	/**
	 * Test the key stoke for the the registered action.
	 *
	 * @param {KeyboardEvent} event The keypress event.
	 *
	 * @return {boolean} True if the shortcut has been found and action performed.
	 */
	_check: function (event) {
		for (var i in this._list) {

			// Get the action and the command
			var item = this._list[i];
			var command = this._parse(item.command);

			// Match
			var match = true;
			for (var key in command) {
				if (command.hasOwnProperty(key) && command[key] !== null && command[key] !== event[key]) {
					match = false;
					break;
				}
			}

			// Run the action
			if (match) {
				item.action(event);
				event.preventDefault();
				return true;
			}
		}

		return false;
	},

	/**
	 * Parse the set key combination into an array of traceable keys.
	 *
	 * @param {string} command The key combination.
	 *
	 * @return {{shiftKey: boolean, ctrlKey: boolean, altKey: boolean, keyCode: null}}
	 */
	_parse: function (command) {
		var result = {
			shiftKey: false,
			ctrlKey: false,
			altKey: false,
			keyCode: null
		};

		// Split the command
		var cmd = command.split(/[ ]+/);
		for (var i in cmd) {
			if (cmd.hasOwnProperty(i)) {
				var key = cmd[i];

				switch (key) {

					// Shift, ctrl and alt
					case 'shift':
						result.shiftKey = true;
						break;
					case 'ctrl':
						result.ctrlKey = true;
						break;
					case 'alt':
						result.altKey = true;
						break;

					// Special keys
					case 'esc':
					case 'escape':
						result.keyCode = 27;
						break;
					case 'space':
						result.keyCode = 0x20;
						break;
					case 'del':
					case 'delete':
						result.keyCode = 46;
						break;
					case 'backspace':
						result.keyCode = 0x08;
						break;
					case 'enter':
						result.keyCode = 0x0A;
						break;

					default:
						if (key.length === 1) {
							result.keyCode = this._ord(key);

						} else if (key.match(/^\d+$/)) {
							result.keyCode = key;

						} else if (typeof console !== 'undefined') {
							console.warn('nxShortcut: Unknown keyword `' + key + '` in command `' + command + '`.');
						}
				}
			}
		}

		return result;
	},

	/**
	 * Get the ordinal of a character, multi-byte safe.
	 *
	 * @param {string} char The character to get the ordinal from.
	 *
	 * @return {int} The ordinal of the supplied string.
	 */
	_ord: function (char) {
		var ch = char.toUpperCase();
		var code = ch.charCodeAt(0);
		if (0xD800 <= code && code <= 0xDBFF) {
			var hi = code;
			if (ch.length === 1) {
				return code;
			}
			var low = ch.charCodeAt(1);
			return ((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000;
		}

		return code;
	},

	// Tests
	Test: {

		// Execute the test
		run: function () {

			// Test cases
			var testCases = {
				'a': {shiftKey: false, ctrlKey: false, altKey: false, keyCode: 65},
				'A': {shiftKey: false, ctrlKey: false, altKey: false, keyCode: 65},
				'space': {
					shiftKey: false,
					ctrlKey: false,
					altKey: false,
					keyCode: 0x20
				},
				'ctrl 4': {
					shiftKey: false,
					ctrlKey: true,
					altKey: false,
					keyCode: 52
				},
				'ctrl enter': {
					shiftKey: false,
					ctrlKey: true,
					altKey: false,
					keyCode: 10
				},
				'ctrl shift p': {
					shiftKey: true,
					ctrlKey: true,
					altKey: false,
					keyCode: 80
				},
				'ctrl space': {
					shiftKey: false,
					ctrlKey: true,
					altKey: false,
					keyCode: 32
				},
				'ctrl backspace': {
					shiftKey: false,
					ctrlKey: true,
					altKey: false,
					keyCode: 8
				},
				'a ctrl': {
					shiftKey: false,
					ctrlKey: true,
					altKey: false,
					keyCode: 65
				},
				'shift del': {
					shiftKey: true,
					ctrlKey: false,
					altKey: false,
					keyCode: 46
				},
				'esc': {
					shiftKey: false,
					ctrlKey: false,
					altKey: false,
					keyCode: 27
				}
			};

			// Execute tests
			for (var test in testCases) {
				this.unitTest(test, nxShortcuts._parse(test), testCases[test]);
			}

		},

		// Simple unit test
		unitTest: function (message, test, expected) {
			if (JSON.stringify(test) === JSON.stringify(expected)) {
				console.info([message, test, expected]);
			} else {
				console.warn([message, test, expected]);
			}
		}
	}
};

// Attach to the document
window.addEventListener('keydown', function (event) {
	nxShortcuts._check(event);
});
