var DropDownList = Class.create({
				
	options: {
		"highlightClass" : "highlighted" , 
		"selectionClass" : "selected"    , 
		"autoFind"       : true          , 
		"toggleOnClick"  : true          ,
		"multiple"       : false      
	},
					
	initialize: function( proxy , options ){

		this.options         = $H( this.options ).merge( options || {} );		
		this.proxy           = $(proxy);
		this.data            = [];
		this.currentIndexes  = [];
		this.highlightIndex  = -1;
		this.depth           =  0;
				
		//Create dom
        this.element = new Element("div");
		this.element.addClassName("dropdown-list");
		this.element.hide();
        document.body.appendChild( this.element );
		
		//Add tabindex
//		if( !this.element.readAttribute( "tabindex" ) )
//			this.element.writeAttribute("tabindex" , "0");

		if( this.proxy )
			if( !this.proxy.readAttribute( "tabindex" ) )
				this.proxy.writeAttribute("tabindex" , "0");					
		
		//Render					
		this.render();

		//Add Delegated focus event
		if( document.body.addEventListener )
    		document.body.addEventListener('focus', this.onDocumentFocus.bindAsEventListener( this ) , true );
        else
    		document.observe( 'focusin' , this.onDocumentFocus.bindAsEventListener( this ) );

		//Prevent Selection
		this.element.onselectstart= function(){
			return false;
		};
		this.element.onmousedown = function(){
			return false;
		};
		this.element.click = function(){
			return true;
		};
	
		//Add Other Events		
		document.observe( 'click'            , this.onDocumentClick.bindAsEventListener(this));
        document.observe( 'keydown'          , this.onDocumentKeyDown.bindAsEventListener(this));
        document.observe( 'keypress'         , this.onDocumentKeyPress.bindAsEventListener(this));

		this.element.observe(  'click'       , this.onListClick.bindAsEventListener(this) );
		this.element.observe(  'mouseover'   , this.onListMouseOver.bindAsEventListener(this));
            						
		if( this.proxy ){
			this.proxy.observe( 'click'      , this.onProxyClick.bindAsEventListener(this));
		}
				
	},
	
	/* --- Events ------------------------------------------------------ */
	
	observe: function( eventName , handler ){
        this.events  = this.events                  || $H({});
        var registry = this.events.get( eventName ) || $A([]);
        registry.push( handler );
        this.events.set( eventName , registry );
	},
	stopObserving: function( eventName , handler ){
        this.events       = this.events                  || $H({});
        var registry      = this.events.get( eventName ) || $A([]);
        registry          = registry.without( handler );
        this.events.set( eventName , registry );
	},
	fire: function( eventName ){
        this.events  = this.events                  || $H({});
        var registry = this.events.get( eventName ) || $A([]);
        var args     = $A(arguments).slice(1);
        for( var i = 0 ; i < registry.length ; i++ ){
            registry[i].apply( this , args );
        }
	},
	
	/* --- Rendering ------------------------------------------------------ */
	
	render: function(){					

		this.element.update( "" );
		this.container = new Element("table");
		this.element.appendChild( this.container );

		var len       = this.length();				
		for( var i = 0 ; i < len ; i++ ){
            this.renderElement( i );
		}

	},	
	renderElement: function( i ){

		var multiple = this.options.get("multiple");
		var depth    = this.depth;
		var tr       = this.container.insertRow(i);
        var start    = 0;
        
        if( multiple ){
			var td      = $(tr.insertCell( 0 ));	
    		var content = "<input type='checkbox' />";

			td.addClassName( 'checkbox' );
		    td.update( content );
		    
		    start = 1;
        }

		for( var j = 0 ; j < depth ; j++ ){
			var td      = $( tr.insertCell( j + start ) );	
    		var content = "<span>" + this.text( i , j ) + "</span>";
		    td.update( content );   
		}
				
	},
	
	/* --- Data ------------------------------------------------------ */
	
	getElementDepth: function(){
		return this.depth;
	},				
	add: function( value , text ){

		if( !Object.isArray( text ) )
			text = [ text ];

		if( text.length > this.depth )
			this.depth = text.length;

		var index = this.data.length; 
		this.data[ this.data.length ] = { "value" :  value , "text" : text };

		this.renderElement( index );	
		
	},
	length: function(){
		return this.data.length;
	},
	clear: function(){
		this.currentIndexes = [];					
		this.data           = [];
		this.depth          = 0;
		this.render();
	},
	
	/* --- Selection --------------------------------------------------- */	
	index: function(){

        //Return the last selected element
        if( this.currentIndexes.length > 0 ){
            return this.currentIndexes[ this.currentIndexes.length - 1 ];
        }else{
            return -1;
        }
        
	},
	indexes: function(){
		return this.currentIndexes;	
	},
	isSelected: function( index ){
	    return ( this.currentIndexes.indexOf( index ) != -1 );
	},
	clearSelection: function(){
		this.currentIndexes = [];	
	},
	setIndex: function( index , clear ){				
        
		if( !this.isIndexValid( index ) )
		    return;

		var multiple   = this.options.get("multiple");
        var newIndexes = [];

        //Clear Selection
        if( clear === undefined ){
            if( multiple )
                clear = false;
            else
                clear = true;
        }

        if( clear )
        	this.clearSelection();
         
        //Calculate new indexes
        newIndexes  =  this.currentIndexes.without( index ).clone();
        newIndexes.push( index );
          
        //Save the new indexes
		this.currentIndexes = newIndexes;

        //Call callback
		this.fire("dropdownlist:change"  );

	    //Update View
	    this.highlightSelection.bind(this ).defer();

		    					
	},
	removeIndex: function( index ){

		if( !this.isIndexValid( index ) )
		    return;

        //Calculate new indexes
        var newIndexes  =  this.currentIndexes.without( index );

        //Save the new indexes
		this.currentIndexes = newIndexes;
		
        //Call callback
		this.fire("dropdownlist:change"  );
	
	    //Update View
	    this.highlightSelection.bind(this ).defer();
	
	},
	toggleIndex: function( index ){
	
		if( !this.isIndexValid( index ) )
		    return;
	
	    if( !this.isSelected( index ) )
	        this.setIndex( index );
	    else
	        this.removeIndex( index );
	
	},
	isIndexValid: function( index ){
		return index >= 0 && index < this.data.length;
	},
	
	/* --- Helpers --------------------------------------------------- */	
	highlight: function( index ){

		if( !this.isIndexValid( index ) ){
		    return;
		}

		//Get the nodes for those indexes
		var newNode  = this.container.down("tr" ,index );		
		var lastNode = this.isIndexValid( this.highlightIndex )? this.container.down("tr" , this.highlightIndex ) : false;
		
		//Manage highlight
		var highlightClass = this.options.get("highlightClass");

		if (lastNode) {
			lastNode.removeClassName(highlightClass);
		}
		
		if (newNode) {
			newNode.addClassName(highlightClass);
		}
		
	    this.highlightIndex = index;

	},
	highlightSelection: function(){
		
	    var rows           = this.container.select("tr" );		
	    var selectionClass = this.options.get("selectionClass");
	    var newIndexes     = this.currentIndexes;
        var count          = rows.length;
                                                                
        for( var i = 0 ; i < count ; i++ ){

            var row      = rows[ i ];
            var selected = newIndexes.indexOf( i ) != -1;
            
            if( selected ){
                row.addClassName( selectionClass );
            }else{
                row.removeClassName( selectionClass );
            }
     		
            var chk = row.down('td.checkbox input');
            if( chk ){
                chk.checked = selected;
            }
            
        } 

	},
	scroll: function( index ){
		
		if( !this.isIndexValid( index ) ){
            return;
		}
		
        if( !this.container )		
            return
	
		var node = this.container.down("tr" , index );
		
        if( !node ){
            return;
        }
		
	    offset   = node.positionedOffset();
	    
		this.element.scrollLeft = 0;
		this.element.scrollTop  = offset[ 1 ];
				
	},	
	find: function( string ){

        try{
		    var length     = this.length();
		    var startIndex = this.index() + 1;							
		    var re         = new RegExp("^" +  string , "i" );
							
		    for( var i = 0 ; i < length ; i++ ){

			    var index = (startIndex + i) % length;
			    var text  = this.text( index );
															
			    if( text.match( re ) ){
				    return index;
				    break;
			    }
			
		    }
        }catch( ex ){
    		return false;        
        }		
        
		return false;
		
	},
	text: function( i , j ){
		
		if( i == undefined ){
			i = this.index();
		}					
		
		if( j == undefined ){
			j = 0;
		}

		if( i == -1 )	
			return undefined;
							
		return this.data[ i ].text[ j ];					
		
	},
	texts: function( j ){
				
        var i        = 0;
        var indexes  = this.indexes();
        var c        = indexes.length;
        var texts    = [];

        for( i = 0 ; i < c ; i++ ){
            var index = indexes[i];
            texts[ texts.length ] = this.text( index , j );
        }

		return texts;

	},
	value: function( i ){

		if( i == undefined ){
			i = this.index();
		}
		
		if( i == -1 ){	
			return undefined;
        }
        
		return this.data[ i ].value;
			
	},
	values: function( ){

        var i        = 0;
        var indexes  = this.indexes();
        var c        = indexes.length;
        var values   = [];

        for( i = 0 ; i < c ; i++ ){
            var index = indexes[i];
            values[ values.length ] = this.value( index );
        }

		return values;

	},
	
	/* --- Show / Hide  ------------------------------------------------ */	
	show: function(){

		//Dont show if already visible
		if( this.showing() ){
		   return;
		}
		
		//Set Position
        var position   = this.proxy.cumulativeOffset();
        var dimensions = this.proxy.getDimensions();
		
		//Calculate Requerired Size
		var left   = position[0];
		var top    = position[1] + dimensions.height;
		var width  = dimensions.width;
		this.element.setStyle({ 
		                        'position'  : 'absolute'   , 
		                        'width'     : width + 'px' , 
		                        'top'       : top   + 'px' , 
		                        'left'      : left  + 'px'
		                      });

        //Highlight the current selected element
        this.setIndex( this.index() );

        //Scroll to the current selected item
		this.scroll( this.index() );

        //Show
		this.element.show();
				

	},
    showing: function(){
        return this.element.visible();
    },
	hide: function(){
		this.element.hide();
	},
	toggle: function(){

	    if( this.showing() ){
	        this.hide();
	    }else{
	        this.show();
	    }
	    
	},
	/* --- Event Handlers  ------------------------------------------- */	
	onDocumentFocus: function( e ){

		var el   = Event.element( e );		
        var ours = el == this.element || 
		           el == this.proxy || 
        		   el.descendantOf( this.element ) || 
    		       el.descendantOf( this.proxy );
    		       
        this.active = ours;

        if( !ours ){
	        this.hide();        
        }

	},
    onDocumentClick: function( e ){
		
		var el = e.element( );		
        var ours = el == this.element || 
		           el == this.proxy || 
        		   el.descendantOf( this.element ) || 
    		       el.descendantOf( this.proxy );
    		       
        if( !e.isLeftClick() )
            return;
    		   
        if( !ours ){    
    	    this.active = false;
	        this.hide();
        }
            
    },
	onProxyClick: function( e ){	

        //Mark as active
        this.active = true;
        
        //Toggle DropDown
	    var toggleOnClick = this.options.get("toggleOnClick");
        if( toggleOnClick ){	
            this.toggle();
        }
        
	},
	onListClick: function( e ){

        var el       = e.element();
        var tr       = el.tagName == "TR" ? el : el.up("tr");	
		var multiple = this.options.get("multiple");

        //Mark as Active
        this.active = true;

        //Manage Selection
        if( !tr ) 
		 return;

        //tr.rowIndex wasn't working in ie            
        var table = tr.up("table");	
        var index = this.container.select("tr").indexOf( tr );
        if( index == -1 )
			return;

        if( multiple ){
            this.toggleIndex( index );
        }else{
            this.setIndex( index );
            this.hide();
        }
            
        e.stop();

            
	},
	onListMouseOver: function( e ){

        var el    = e.element();
        var tr    = el.tagName == "TR" ? el : el.up("tr");	

        if( !tr ) return;

        //tr.rowIndex wasn't working in ie
        var table = tr.up("table");	
        var index = this.container.select("tr").indexOf( tr );
        if( index != -1 ){
            this.highlight( index );
            e.stop();
        }

	},
	onDocumentKeyPress: function( e ){

	    //Check that the element has "focus"
        if( this.active != true )
            return;

		var handled  = false;
		var charcode = Prototype.Gecko ? e.charCode : e.charCode || e.keyCode;
        var autoFind = this.options.get("autoFind");        
    	var multiple = this.options.get("multiple");
    	    	            
        //Check for selection by space
        if( charcode == 32 || charcode === ' ' ){

            if( multiple ){
               this.show();
               this.toggleIndex( this.highlightIndex );
            }else{
               this.toggle();
               this.setIndex( this.highlightIndex );
            }
            
            handled = true;        
            
        }else if( charcode && autoFind ){
        		
            var i = this.find( String.fromCharCode( charcode ));

            if (i === false)
                return;
            
            if( multiple ){
               this.show();
               this.highlight( i );
               this.scroll( i );
            }else{
               this.setIndex( i );
               this.highlight( i );
               this.scroll( i );
            }
                                    
        }

	    if( handled ){
	        e.stop();
        }
        
	},
	onDocumentKeyDown: function( e ){

	    //Check that the element has "focus"
        if( this.active != true )
            return;

		var key      = e.keyCode;
		var handled  = false;
		var multiple = this.options.get("multiple");
					
		switch( key ){													
				
			case Event.KEY_ESC       :	this.hide();			                            				
			 							break;

			case Event.KEY_RETURN    :  this.setIndex( this.highlightIndex );
			                            this.toggle();
										handled = true;
			 							break;
																	
			case Event.KEY_RIGHT     :
			case Event.KEY_UP        :  
			                            var index = this.highlightIndex - 1;
			                            this.highlight( index );
			                            if( multiple )  this.show();
			                            if( !multiple ) this.setIndex( index );
                    					this.scroll( index );
										handled = true;
										break;
			case Event.KEY_LEFT      :     
			case Event.KEY_DOWN      :	
			                            var index = this.highlightIndex + 1;
			                            this.highlight( index );
			                            if( multiple )  this.show();
			                            if( !multiple ) this.setIndex( index );
                    					this.scroll( index );
										handled = true;													
										break;
										
			case Event.KEY_PAGEUP    : 
			case Event.KEY_HOME      :  
						                var index = 0;
			                            this.highlight( index );
			                            if( multiple )  this.show();
			                            if( !multiple ) this.setIndex( index );
                    					this.scroll( index );
										handled = true;						
										break;
																
			case Event.KEY_PAGEDOWN  : 
			case Event.KEY_END       :  
						                var index = this.length() - 1;
			                            this.highlight( index );
			                            if( multiple )  this.show();
			                            if( !multiple ) this.setIndex( index );
                    					this.scroll( index );
										handled = true;
										break;
		}
		
	
	    if( handled )
	        e.stop();
	
	}
});
