/** Hotkeys
 *
 *  18: alt
 *  17: ctrl
 *  16: shift
 *  13: enter
 *   9: tab
 *  38: arrow up
 *  40: arrow down
 *  37: arrow left
 *  39: arrow right
 *  27: escape
 */

var Hotkeys = {

	config: {
		modifier:                    16, // shift
//		modifier:                    false, // no modifier - mac
		admin_key:					 106,
		blur_key:                    27,
		row_prefix:                  'r_',
		row_class:                   't_row',
		mux_separator:               'X',
		hotkey_char_class:           't_hotkey_char',
		hotkey_char_hilight_class: 't_hotkey_char_hilight',
		button_class:                'i_button',
		button_hotkey_prefix:        't_hotkey_btn_',
		row_active_class:            'x_active'
	},

	actions: {},
	modifier_active: false,
	keys_active: {},
	labels: {},
	labels_normal: {},
	enabled: false, // toggeled when entering/leaving inputs
	off: false, // force off
	key_up: 38,
	key_down: 40,
	key_left: 37,
	key_right: 39,
	row: null,
	rows: [],
	last_row_class: null,
	no_list: false,
	list_inited: false,
	disabled_for_focus: false,
	active_char: null,

	add_key: function(chr, func) { // hotkey_add
//eon.log('add hotkey: ' + chr + ' : ' +func);
		Hotkeys.actions[chr] = func;
	},

	add_label: function(chr, label) { // add_button_label
//eon.log('add label: ' + chr + ' : ' + label);
		Hotkeys.labels[chr] = label;
	},

	// implementation of these are added by mod_base if applicable
	pager_next: function() {},
	pager_prev: function() {},

	keydown: function(e) {

//eon.log('keydown');
		if ( !Hotkeys.isEnabled() )
			return;
		var key = (window.event) ? event.keyCode : e.keyCode;
		var chr = String.fromCharCode(key).toLowerCase();

//eon.log(' + key: ' + key + ' : ' +chr);
		// if modifier-key is currently pressed, listen for an action key
		if ( Hotkeys.modifier_active || Hotkeys.config.modifier === false ) {
			// store the pressed key, and execute function on keyup
			if ( Hotkeys.actions[ chr ] )
				Hotkeys.keys_active[ chr ] = true;
			// except for arrow-keys, which are executed on keydown
			else if ( key == Hotkeys.key_up )
				Hotkeys.list_move('up');
			else if ( key == Hotkeys.key_down )
				Hotkeys.list_move('down');
			else if ( key == Hotkeys.key_left )
				Hotkeys.pager_prev();
			else if ( key == Hotkeys.key_right )
				Hotkeys.pager_next();
		}

		// if key is the modifier, remember that & hilight hotkey chars in buttons/links
		if ( Hotkeys.config.modifier !== false && key == Hotkeys.config.modifier && !Hotkeys.modifier_active ) {
			Hotkeys.modifier_active = true;
			Hotkeys.hilight();
/*
			// disable selection - does not work. it's too late to disable selection here. user may have clicked somewhere before pressing the hotkey modifier (sms)
			document.onselectstart = function() { return false; } // IE+Opera
			document.onmousedown = function() { return false; } // FF
*/
		}
	},

	keyup: function(e) {

//eon.log('keyup');
		if ( !Hotkeys.isEnabled() )
			return;
		var key = (window.event) ? event.keyCode : e.keyCode;
		var chr = String.fromCharCode(key).toLowerCase();

		if (key == Hotkeys.config.admin_key)
			admin_settings_popup();

//eon.log(' - key: ' + key  + ' : ' + chr);
		// releasing a key that was pressed when modifier was active? then execute the function.
		if ( Hotkeys.keys_active[ chr ] ) {
eon.log(' *' + chr + ': ' +Hotkeys.actions[chr]);
			Hotkeys.keys_active[ chr ] = false;
			Hotkeys.modifier_active = false;
			Hotkeys.dehilight();
			Hotkeys.active_char = chr;
			Hotkeys.actions[chr]();
			Hotkeys.active_char = null;
		}

		// releasing the modifier?
		if ( key == Hotkeys.config.modifier && Hotkeys.modifier_active == true ) {
eon.log('release modifier');
			Hotkeys.modifier_active = false;
			Hotkeys.dehilight();
/*
			// re-enable selection - does not work - see hotkey_keydown -> disable selection
			document.onselectstart = function() { return true; } // IE+Opera
			document.onmousedown = function() { return true; } // FF
*/
		}
	},

	input_key: function(e) {
		if ( Hotkeys.isEnabled() )
			return;
		var key = (window.event) ? event.keyCode : e.keyCode;
		if (key == Hotkeys.config.blur_key) {
			var elem = (window.event) ? event.srcElement : e.target;
//eon.log('blir input: '+elem);
			elem.blur();
		}
	},

	get_element: function() {
		return Hotkeys.dehilight(Hotkeys.active_char, true);
	},

	// get the row that are currently hovered
	get_row: function() {
		if ( Hotkeys.row == null )
			return null;
		return $( Hotkeys.config.row_prefix + Hotkeys.rows[Hotkeys.row] );
	},

	// get the (table row) id from a (html) row id
	get_row_id: function(row) {
		if ( has_class(row, Hotkeys.config.row_class) ) {
			var pat = new RegExp( '^' + Hotkeys.config.row_prefix );
			return row.id.replace(pat,'');
		}
		return false;
	},

	// get a row's number/key in this.rows from the row's (html?) id
	get_row_number: function(id) {
		for ( var i = 0; i < Hotkeys.rows.length; i++ ) {
			if (Hotkeys.rows[i] == id)
				return i;
		}
		return false;
	},

	// hilight action key in buttons & links
	hilight: function() {
		// links
		var spans = document.getElementsByTagName('span');
		for (var i = 0; i < spans.length; i++) {
			if (has_class( spans[i], Hotkeys.config.hotkey_char_class ))
				change_class( spans[i], Hotkeys.config.hotkey_char_class, Hotkeys.config.hotkey_char_hilight_class );
		}
		// buttons
		var btn_key_pat = new RegExp( '^' + Hotkeys.config.button_hotkey_prefix );
		var btns = document.getElementsByTagName('input');
		for (var i = 0; i < btns.length; i++) {
			if (!has_class( btns[i], Hotkeys.config.button_class ))
				continue;
			var classes = get_classes(btns[i]);
			for (var j = 0; j < classes.length; j++) {
				if ( ! classes[j].match(btn_key_pat) )
					continue;
				var btn_key = classes[j].replace(btn_key_pat, '');
				Hotkeys.labels_normal[ btn_key ] = btns[i].value;
				btns[i].value = Hotkeys.labels[ btn_key ];
			}
		}
		var r = Hotkeys.get_row();
		if (r)
			add_class( r, Hotkeys.config.row_active_class );
	},

	// if find_element_by_char is set to a char, the corresponding action link or button element is returned
	// (this is again used to find the right mod container div)
	// @todo: cleaner fix..
	dehilight: function(find_element_by_char, no_dehilight) {
		// links
		var found_element = null;
		var spans = document.getElementsByTagName('span');
		for (var i = 0; i < spans.length; i++) {
			if (
				has_class( spans[i], Hotkeys.config.hotkey_char_hilight_class ) ||
				( has_class( spans[i], Hotkeys.config.hotkey_char_class ) && no_dehilight )
			) {
				if (!no_dehilight)
					change_class( spans[i], Hotkeys.config.hotkey_char_hilight_class, Hotkeys.config.hotkey_char_class );
				if (find_element_by_char && find_element_by_char == spans[i].innerHTML.toLowerCase())
					found_element = spans[i].parentNode;
			}
		}
		// buttons
                var btn_key_pat = new RegExp( '^' + Hotkeys.config.button_hotkey_prefix );
		var btns = document.getElementsByTagName('input');
		for (var i = 0; i < btns.length; i++) {
			if (!has_class( btns[i], Hotkeys.config.button_class ))
				continue;
			var classes = get_classes(btns[i]);
			for (var j = 0; j < classes.length; j++) {
				if ( ! classes[j].match(btn_key_pat) )
					continue;
				var btn_key = classes[j].replace(btn_key_pat, '');
				if (!no_dehilight)
					btns[i].value = Hotkeys.labels_normal[ btn_key ];
				if (find_element_by_char && find_element_by_char == btn_key)
					found_element = btns[i];
			}
		}
		// list row
		var r = Hotkeys.get_row();
		if (r)
			remove_class( r, Hotkeys.config.row_active_class );

		if (find_element_by_char) {
			if (found_element)
				return found_element;
		}
	},

	init: function() {
		document.onkeydown = Hotkeys.keydown;
		document.onkeyup = Hotkeys.keyup;
		// disable hotkeys when in text inputs
		var inputs = document.getElementsByTagName('input');
		for (var i = 0; i < inputs.length; i++) {
			if (inputs[i].type != 'text')
				continue;
//eon.log('init disable text input: '+inputs[i].id+' / ' +inputs[i].name);
// Hack to allow onfocus/onblur to be set in html code. Should be merged instead, but I couldn't make it work. (sg)
			if (!inputs[i].onfocus)
				inputs[i].onfocus = Hotkeys.disable;
			if (!inputs[i].onblur)
				inputs[i].onblur = Hotkeys.enable;
			inputs[i].onkeydown = Hotkeys.input_key;
		}
		// ..and text areas
		inputs = document.getElementsByTagName('textarea');
		for (var i = 0; i < inputs.length; i++) {
//eon.log('init disable textarea: '+inputs[i].id+' / ' +inputs[i].name);
			inputs[i].onfocus = Hotkeys.disable;
			inputs[i].onblur = Hotkeys.enable;
			inputs[i].onkeydown = Hotkeys.input_key;
		}
		// ..and selects
		inputs = document.getElementsByTagName('select');
		for (var i = 0; i < inputs.length; i++) {
//eon.log('init disable select: '+inputs[i]);
			inputs[i].onfocus = Hotkeys.disable;
			inputs[i].onblur = Hotkeys.enable;
			inputs[i].onkeydown = Hotkeys.input_key;
		}
		if ( !Hotkeys.disabled_for_focus )
			Hotkeys.enable();
	},

	isEnabled: function() { return (Hotkeys.enabled && !Hotkeys.off); },
	enable:  function() { Hotkeys.enabled = true; },
	disable: function() { Hotkeys.enabled = false; },

	disable_for_focus: function() {
//eon.log('hotkeys disable for focus');
		Hotkeys.disabled_for_focus = true;
		Hotkeys.disable();
	},

	list_move: function(direction) {
//eon.log('hotkey list move');
		if ( Hotkeys.no_list )
			return;
		Hotkeys.list_init();
		if ( Hotkeys.row == null ) {
//eon.log('no row. rows: ' + Hotkeys.rows.length);
			if ( Hotkeys.rows.length > 0 )
				Hotkeys.row = 0;
			else {
//eon.log(' ** set no list ** ');
				Hotkeys.no_list = true;
				return;
			}
		} else {
			var r = Hotkeys.get_row();
			if (r) r.className = Hotkeys.last_row_class;
			if (direction == 'up') {
//eon.log(' - hotkey list up');
				if (Hotkeys.row>0)
					Hotkeys.row--;
			} else {
//eon.log(' - hotkey list down');
				if ((Hotkeys.row+1)<(Hotkeys.rows.length))
					Hotkeys.row++;
			}
		}
		var row = Hotkeys.get_row();
		Hotkeys.last_row_class = row.className;
		add_class( row, Hotkeys.config.row_active_class );
	},

	list_over: function(row, is_mouseout) {
		Hotkeys.list_init();
		if ( ! is_mouseout ) {
			var id = Hotkeys.get_row_id(row);
			if ( id === false )
				return;
			var n = Hotkeys.get_row_number(id);
			if ( n === false )
				return;
			Hotkeys.row = n;
			var row_checked = Hotkeys.get_row();
			Hotkeys.last_row_class = row.className;
			add_class( row, Hotkeys.config.row_active_class );
		} else {
			Hotkeys.row = null;
			row.className = Hotkeys.last_row_class;
		}
	},

	list_init: function() {
		if ( Hotkeys.list_inited )
			return;
//eon.log('hotkey list init');
		var e = document.getElementsByTagName('tr');
		for (var i = 0; i < e.length; i++) {
			var id = Hotkeys.get_row_id(e[i]);
			if ( id != false )
				Hotkeys.rows[ Hotkeys.rows.length ] = id;
		}
//eon.log(' - initialized ' +Hotkeys.rows.length+ ' rows');
		Hotkeys.list_inited = true;
	}

}

