var AutoComplete = new Class({	
	options: {
		element: null,
		items: [],
		onchange: null,
		listclass: 'autocomplete',
		emptytextclass: 'emptytextautocomplete',
		textclass: 'textautocomplete',
		selecteditemclass: 'selected',
		nomatchestext: 'No matching items',
		width: 'auto',
		autoshowdelay: 300
	},

	initialize: function(options) {
	    this.setOptions(options);

	    if (this.options.element != null) 
	    {
	    	this.list = new Element('ul').injectInside(document.body);
	    	this.list.addClass(this.options.listclass);
	    	this.list.setStyle('position', 'absolute'); 
	    	this.list.setStyle('display', 'none');
	    	
	    	this.donesomething = false;
	    	
	  	    this.updatelist();
	  
	    	this.changeselection(-1);
	    	
	    	this.showfx = new Fx.Tween(this.list);
	    	this.hidefx = new Fx.Tween(this.list, 
	    	{
	    		onComplete: function() { 
	    			this.list.setStyle('display', 'none');
	    		}.bind(this)
	    	});
	    	
	    	this.options.element.set('autocomplete', 'off');
	    	
	    	this.list.addEvent('scroll', this._scroll.bind(this));
	    	
	    	this.options.element.addEvent('keydown', this._keydown.bind(this));
	    	this.options.element.addEvent('keyup', this._keyup.bind(this));
	    	this.options.element.addEvent('blur', this._blur.bind(this));
	    	this.options.element.addEvent('focus', this._focus.bind(this));
	    	
	    	window.addEvent('resize',function(e){
                if (this.listvisible()) {
                    var coords = this.options.element.getCoordinates();
                    
		            this.list.setStyle('top', coords.bottom);
		            this.list.setStyle('left', coords.left);
                }
            }.bind(this));
	    	
	    	this._keyup(null);
	    	
	    	if (this.exactmatch() && this.options.onchange != null) {
	    	    this.options.onchange(this.options.element);
	    	}
	    }
	},
	
	_scroll: function(e) {
		this.options.element.focus();
	},
	
	_focus: function(e) {
		this.__listinfocus = true;
		
		if (this.options.element.value == '')
		{
		    this.donesomething = false;
		}
		
		var x = function() {
			if (!this.listvisible() && !this.exactmatch()) {
				this.updatelist();
				this.showlist();
			}
		}.bind(this);
		
		x.delay(this.options.autoshowdelay);
	},
	
	_blur: function(e) {
		this.__listinfocus = false;
	
		var x = function() {
			
			if (!this.__listinfocus) {
				this.hidelist();
			}
			
		}.bind(this);
		
		x.delay(500);
	},
	
	_keydown: function(e) {
		switch (e.code)
		{
		case 37: //left
			var mockevent = e;
			mockevent.target = this.getlistitemlink(this.selectedindex);
			this.selectionmade(e);

			break;
		case 38: //up
			this.moveselection(-1);

			break;
		case 39: //right
			this.hidelist();

			break;
		case 40: //down
			if (this.listvisible()) {
				this.moveselection(1);
			} else {
				this.showlist();
			}
			break;
		case 9: //tab    
	        if (this.listvisible() && this.matchingitems() > 0 && this.donesomething && this.selectedindex != -1)
	        {
	            var mockevent = e;
	            mockevent.target = this.getlistitemlink(this.selectedindex);
	            
	            this.selectionmade(mockevent);
	        }
	
			break;
		case 13: //return
			if (this.listvisible()) {
			    var idx = (this.selectedindex == -1 ? 0 : this.selectedindex);
			
				var mockevent = e;
				mockevent.target = this.getlistitemlink(idx);
				this.selectionmade(e);
				return false;
			}
		
			break;
		}
		
		this.donesomething = true;
	},
	
	_keyup: function(e) {
		if (this.options.emptytextclass != '' && this.options.textclass != '')
		{
		    this.options.element.removeClass(this.options.textclass);
		    this.options.element.removeClass(this.options.emptytextclass);
		    
		    if (this.options.element.value.length > 0) 
		        this.options.element.addClass(this.options.textclass);
            else
		        this.options.element.addClass(this.options.emptytextclass);
		}
		
		if (e == null || e.code == 17 || e.control) //stop working with control keys (and when we are just trying to update classes)
			return;
	
		switch (e.code)
		{
		case 37: //left
		case 38: //up
		case 39: //right
		case 40: //down
	        break;
		default: //other keys
			this.updatelist();
			
			if (!this.listvisible() && this.matchingitems() > 0) 
			{
				this.showlist();
			}
		}
	},
	
	selectionmade: function(e) {
		this.options.element.value = e.target.get('text');
		
		var items = this.list.getElements('li');
		
		this.donesomething = true;
		
		for (var i = 0; i < items.length; i++) 
		{
			if (items[i] == e.target.getParent()) {
			
				this.changeselection(i);
				
				this.hidelist();
				
				if (this.options.onchange != null) {
				    this.options.onchange(this.options.element);
				}
				
				break;
			}
		}
		
		this._keyup(null);
		
		return false;
	},
	
	changeselection: function(idx) {
		var items = this.list.getElements('li');
		if (idx == -1)
		    idx = -1;
		else if (idx >= items.length)
			idx = 0;
		else if (idx < 0)
			idx = items.length - 1;
			
		this.selectedindex = idx;
		
		for (var i = 0; i < items.length; i++) {
			if (items[i].getElement('a')) {
				if (i == idx) 
				{
					if (!this.itemisvisibleinviewport(items[i])) 
					{
						var pos = items[i].getPosition(this.list);
					
						this.list.scrollTop = pos.y;
					}
				
					items[i].getElement('a').addClass(this.options.selecteditemclass);
				}
				else
					items[i].getElement('a').removeClass(this.options.selecteditemclass);
			}
		}

	},
	
	itemisvisibleinviewport : function (li) {
		var sTop = this.list.scrollTop;		
		var sBottom = (sTop + this.list.clientHeight);
		
		var pos = li.getPosition(this.list);
		var size = li.getSize();
		
		var top = pos.y; var bottom = pos.y + size.y;
		
		return (sTop <= top && sBottom >= bottom);
	},
	
	moveselection: function (delta) {
		this.changeselection(this.selectedindex + delta);
	},
	
	showlist: function() {
		var coords = null;
		
		var width = 2;
				
		/*if (window.navigator.appName == 'Microsoft Internet Explorer') {
			coords = this.options.element.getBoundingClientRect();
			coords.left = coords.left - 2;
			coords.top = coords.top - 2;
			
			width += this.options.element.getSize().x;
		} else {*/
			coords = this.options.element.getCoordinates();
			
			width += coords.width;
		//}
		
		var items = this.list.getElements('li');
		
		this.list.setStyle('opacity', 0);
		this.list.setStyle('display', 'block');
		
		this.list.setStyle('overflow', 'hidden'); //fix to remove the ie scrollbar area when not needed!
		this.list.setStyle('overflow', 'auto');
		
		this.list.setStyle('top', coords.bottom);
		this.list.setStyle('left', coords.left);

		if (this.options.width == 'auto')
			this.list.setStyle('width', width);
		else
			this.list.setStyle('width', this.options.width);
		
		this.showfx.start('opacity', 1);
	},
	
	hidelist: function() {
		this.hidefx.start('opacity', 0);
	},
	
	instanthidelist: function() {
		this.list.setStyle('display', 'none');
	},
	
	listvisible: function() {
		return (this.list.getStyle('display') == 'none' ? false : true);
	},
	
	getlistitem: function(i) {
		var items = this.list.getElements('li');
			
		return items[i];
	},
	
	getlistitemlink: function(i) {
		var items = this.list.getElements('li');
			
		return items[i].getElement('a');
	},
	
	updatelist: function() {
		var query = this.options.element.value;
		
		var currentselected = null;
		var foundsome = false;
	
		var items = this.list.getElements('li');
		
		for (var i = 0; i < items.length; i++) 
		{
			if (i == this.selectedindex && items[i].getElement('a') != null)
				currentselected = items[i].getElement('a').innerHTML;
			items[i].dispose();
		}
				
		if (!this.exactmatch()) {
		    var x = 0;
		    
			for (var i = 0; i < this.options.items.length; i++)
			{
				if (this.options.items[i].toLowerCase().indexOf(query.toLowerCase()) > -1) 
				{
					var newli = new Element('li').injectInside(this.list);
					var newa = new Element('a').injectInside(newli);

					newa.innerHTML = this.options.items[i];
					newa.href = '#';
					
					newa.listindex = x;

					newa.addEvent('click', this.selectionmade.bind(this));
					newa.addEvent('mouseover', function (e) {
					                                if (this.selectedindex != e.target.listindex) 
					                                    this.changeselection(e.target.listindex);
					                                    
					                                this.donesomething = true;
					                            }.bind(this));
					                            
                    x++;

					foundsome = true;
				}
			}

			if (!foundsome) {
				var newli = new Element('li').injectInside(this.list);
				newli.innerHTML = this.options.nomatchestext;
			} else {
				this.changeselection(-1);
			}
        } 
        else 
        {
            if (currentselected != null) {
                this.options.element.value = currentselected;
                this._keyup(null);
                
                if (this.options.onchange != null)
                    this.options.onchange(this.options.element);
            }
	    			
            this.instanthidelist();
        }
	},
	
	matchingitems: function () {
		return this.list.getElements('li').length;
	},
	
	exactmatch: function() {
		var val = this.options.element.value;
		
		if (val.length > 0) {
			for (var i = 0; i < this.options.items.length; i++) {
				if (this.options.items[i].toLowerCase() == val.toLowerCase()) {
					return true;
				}
			}
		}
		
		return false;
	}
});

AutoComplete.implement(new Events, new Options);