var table_search = new Class({
	
	// caching
	arr_rows: new Array(),
	obj_scroll: null,
	
	// state
	int_last_match_idx: -1,
	str_last_char: '',
	str_row_selector: '',

	initialize: function(str_row_selector, mix_container, int_jump_offset) {
		
		// default jump offset to -45 if it's not set
		if(!int_jump_offset) {
			int_jump_offset = -45;
		}
		
		this.str_row_selector = str_row_selector;
		
		// cache table row elements and the scroll effect
		this.arr_rows = $(document.body).getElements(str_row_selector);
        
        // wait for the YUI table to initialize
        if(this.arr_rows.length == 0 || ($type(mix_container) == 'string' && !$(document.body).getElement(mix_container))) {
            
            this.initialize.delay(500, this, [str_row_selector, mix_container, int_jump_offset]);
            return;
        }

		if($type(mix_container) == 'string') {
			mix_container = $(document.body).getElement(mix_container);
		}
        
		this.obj_scroll = new Fx.Scroll(mix_container, { link: 'cancel', offset: { y: int_jump_offset }});
		
		// listen to keypresses on the document
		document.addEvent('keydown', this._event_search.bindWithEvent(this));
        
        // any clicks on yui table headings automatically reset the search (mostly for YUI enhanced tables)
        $(document.body).getElements('.yui-dt-hd thead th').addEvent('click', this.reset.bind(this));
	},
	
	_event_search: function(obj_event) {
		
		// retrieve pressed key
		var str_char = String.fromCharCode(obj_event.code);
        
        //
        // TODO: ignore any non a-z keys
        //
        
		// perform the search
		this.search(str_char);
	},
	
	search: function(str_char) {
		
		// retrieve rows again if we need to (may have been invalidated by a call to reset)
		if(!this.arr_rows) {
			this.arr_rows = $(document.body).getElements(this.str_row_selector);
		}
		
		var arr_rows = this.arr_rows;
		
		// remove last match class (if it exists)
		if(this.int_last_match_idx >= 0) {
		    arr_rows[this.int_last_match_idx].removeClass('highlight');
		}
		
		// reset the search offsets etc. if this is a new search (i.e. not
		// a repeat of the previous key press)
		if(str_char != this.str_last_char) {
			this.int_last_match_idx = -1;
			this.str_last_char = str_char;
		}
		
		var max = arr_rows.length;
		var count = 0;
		var int_start_i = this.int_last_match_idx < (max - 1) ? this.int_last_match_idx + 1: 0;

		for(var i = int_start_i, ilen = arr_rows.length; i < ilen; i++) {
			
			var elm_tr = arr_rows[i];
			var elm_td = elm_tr.getElement('td');
			
			var str_char_to_match = (elm_td.getFirst() || elm_td).get('html').charAt(0);
			
            // check if the char matches (upper and lower case versions)
			if(str_char_to_match.toLowerCase() == str_char || str_char_to_match.toUpperCase() == str_char) {
                
                // highlight row
				elm_tr.addClass('highlight');
                
                // scroll to the highlighted row
				this.obj_scroll.toElement(elm_td);
	
                // store this matching index
				this.int_last_match_idx = i;
                
				break;
			}
			
            // count how many items we've checked since we loop around the array of rows, meaning
            // we might start at index 100, go to the last one, loop back to the first index and
            // continue - this count allows us to break out of the loop if nothing is found
			count++;
			
            // check we're not looping endlessly
			if(count > max) {
				break;
			}
			
            // do we need to loop back to the beginning of the rows?
			if(i == (ilen - 1)) {
				i = -1;
			}
		}
    },
    
    reset: function() {

		// remove last match class (if it exists)
		if(this.int_last_match_idx >= 0) {
		    this.arr_rows[this.int_last_match_idx].removeClass('highlight');
		}

		// reset the search offsets etc.
        this.int_last_match_idx = -1;
        this.str_last_char = '';
		
		// invalidate the row cache
		this.arr_rows = null;
    }
});
