//todo: reinitialize function
var AutoComplete = new Class({
	options : {
			minimum: 3,
			format : 'json',
			listClass : '',
			itemClass : '',
			itemHoverClass : '',
			onComplete : Class.empty,
			onCancel : Class.empty
	},
	
	initialize : function(id,url,options){
		this.setOptions(options);
		
		this.list = false;
		this.element = $(id);
		this.bound = {
			keydown : this.keydown.bind(this),
			handler : this.handler.bind(this),
			close : this.close.bind(this),
			mousemove : this.mousemove.bind(this),
			mousedown : this.mousedown.bind(this),
			reposition : this.reposition.bind(this)
		};
		
		this.query = new Request({'url':url,onComplete: this.bound.handler,onFailure: this.bound.close.pass(true)});
		this.element.addEvent('keydown',this.bound.keydown);
		this.element.addEvent('blur',this.bound.close.pass(true));
		
		this.reinitialize();
	},
	
	reinitialize : function(){
		if (this.list) this.list.remove();

		var coords = this.element.getCoordinates();
		this.list = new Element('ul',{
			'styles' : {
				'position' : 'absolute',
				'top' : coords.top+coords.height,
				'left' : coords.left,
				'width' : coords.width,
				'list-style' : 'none',
				'display' : 'none'
			},
			'class' : this.options.listClass
		});
		
		this.list.injectInside(document.body);
		this.isOpen = false;
		this.selected = false;
	},
	
	keydown : function(event){
		if (this.isOpen && event.key == 'enter'){ //don't submit the form if we actually just want to select
			event.stop();
			this.close();
			return false;
		}else if (!this.isOpen && (event.key == 'enter' || event.key == 'down' || event.key == 'up' || event.key =='left' || event.key == 'right' || event.code == 16 || event.code == 17 || event.code == 18 || event.code == 224))
			return true;
		else{
			if (event.key=='up' || event.key == 'down')
				event.stop();			//ie breaks if we wait to stop the event
			this.check.delay(10,this,event);
		}
	},
	
	check : function(event){
		var searchVal = this.element.getProperty('value');
		
		if (this.isOpen && event.key == 'down'){
			this.select(this.selected.getNext());
		}else if (this.isOpen && event.key == 'up'){
			this.select(this.selected.getPrevious());
		}else if (this.isOpen && event.key == 'esc'){
			this.close(true);
			return false;
		}else if (event.key == 'left' || event.key == 'right' || event.key == 'tab')
			return;
		
		if (this.isOpen && searchVal.length < this.options.minimum){
			this.query.cancel();
			return this.close(true);
		}else if (this.isOpen && (event.key == 'down' || event.key == 'up' || event.key =='left' || event.key == 'right' || event.code == 16 || event.code == 17 || event.code == 18 || event.code == 224))
			return;
		else if (searchVal.length >= this.options.minimum){
			this.query.cancel(); //cancel the previous query if it's running to prevent a race
			this.list.setHTML('<li>loading...</li>');
			this.display();
			this.query.send({data:{search:searchVal}});
		}
	},
	
	reposition : function(){
		var coords = this.element.getCoordinates();
		this.list.setStyles({
			'top' : coords.top+coords.height,
			'left' : coords.left
		});
		var border = this.list.getStyle('border-left-width').replace('px','').toInt()+this.list.getStyle('border-right-width').replace('px','').toInt();
		var margin = this.list.getStyle('margin-left').replace('px','').toInt()+this.list.getStyle('margin-right').replace('px','').toInt();
		var padding = this.list.getStyle('padding-left').replace('px','').toInt()+this.list.getStyle('padding-right').replace('px','').toInt();
		var width = this.element.getCoordinates().width;
		this.list.setStyle('width',width-border-margin-padding);
	},
	
	display : function(){
		if (this.isOpen) return;
		this.reinitialize();
		
		this.list.setStyle('display','block');
		this.reposition();
		this.attach();
		this.isOpen = true;
	},
	
	handler : function(text,xml){
		if (this.options.format == 'json'){
			//todo: finish json handling
			this.select(this.list.getFirst());
		}else{
			this.list.setHTML(text);
			this.select(this.list.getFirst());
		}
	},
	
	attach : function(){
		this.list.addEvent('mousemove',this.bound.mousemove);
		this.list.addEvent('mousedown',this.bound.mousedown);
		window.addEvent('resize',this.bound.reposition);
	},
	
	detach : function(){
		this.list.removeEvent('mousemove',this.bound.mousemove);
		this.list.removeEvent('mousedown',this.bound.mousedown);
		window.removeEvent('resize',this.bound.reposition);
	},

	mousemove : function(event){
		var mouse = event.page;
		var children = this.list.getChildren();
		
		for (var i=0;i<children.length;i++){
			var coords = children[i].getCoordinates();
			if (mouse.y>=coords.top && mouse.y < coords.top + coords.height)
				return this.select(children[i]);
		}
	},
	
	mousedown : function(event){
		this.close();
	},
	
	select : function(next){
		if (!next) return false;
		if (this.selected)
			this.selected.removeClass(this.options.itemHoverClass);
		this.selected = next;
		this.selected.addClass(this.options.itemHoverClass);
	},
	
	close : function(cancelling){
		this.query.cancel(); //we're closing so we should stop any running queries
		this.detach();
		this.list.setStyle('display','none');
		this.isOpen = false;
		if (cancelling)
			this.fireEvent('onCancel');
		else
			this.fireEvent('onComplete',[this.element,this.selected]);
	}
	
});

AutoComplete.implement(new Options, new Events);
