Control.MultiSelect = Class.create({
	conf: {},
	
	inizialized: false,
	
	selectTag: null,
	optionTags: [],
	
	container: null,
	dropOptions: [],
	optContainer: null,
	optInner: null,
	
	display: null,	
	dispOptions: [],
	
	scrollbar: null,
	scrollbarTrack: null,
	
	dictionary: {},
	
	initialize: function(container,selectTag,options){		
		this.conf = {
			selectMaxHeight: 190,					// max-height in px of the dropdown menu
			onSelect: function(htmlOption){},	// on selection of a checkbox
			onDeselect: function(htmlOption){},  // on deselection of a checkbox
			scrollbarWidth: 17,
			display: null
		};
		Object.extend(this.conf,options || {});
		
		//local variables
		var ms = this;
		var msHtml = [];
		var dispHtml = [];
		
		//member variables
		this.container = $(container);	
		this.selectTag = $(selectTag);		
		this.optionTags = this.selectTag.select("option");
		this.display = $(this.conf.display); 
						
		//generate options
		msHtml.push('<div class="drop-options drop-options-ms"><div class="drop-options-inner">');
		this.optionTags.each(function(optionTag){
			var text = optionTag.innerHTML;
			
			//drop options
			msHtml.push('<div class="option" rel="');
			msHtml.push(optionTag.readAttribute("value") || text );
			msHtml.push('"><div class="option-inner">');
			msHtml.push(text);
			msHtml.push('</div></div>');
			
			//display options
			if(ms.display){
				dispHtml.push('<li style="display:none"><a href="#">');
				dispHtml.push(text);
				dispHtml.push('</a></li>');
			}
		});
		msHtml.push('</div></div>');
		
		//insert html into DOM		
		container.innerHTML = msHtml.join("") + container.innerHTML;
		if(this.display){
			this.display.innerHTML = dispHtml.join("");			
			this.dispOptions = this.display.select("li");
		}
		this.selectTag = container.select("select")[0];		//reassign
		this.optionTags = this.selectTag.select("option");	//reassign
		this.optContainer = container.select(".drop-options")[0];
		this.dropOptions = this.container.select("div.option");
		this.optInner = this.optContainer.select(".drop-options-inner")[0];	
				
		//option events
		var selectedOptions = [];
		ms.dictionary = [];
		this.dropOptions.each(function(dropOption,i){
			//connect all alternatives			
			if(ms.optionTags[i] && ms.dropOptions[i]){
				var updateables = [ms.dropOptions[i].down()];
				if(ms.dispOptions[i]){
					updateables.push(ms.dispOptions[i].down());
				}
				
				var msAlt = {
					index: i,
					value: ms.optionTags[i].value,
					optionTag: ms.optionTags[i],
					dropOption: ms.dropOptions[i],
					dispOption: ms.dispOptions[i] || null,
					updateables: updateables
				};
				ms.optionTags[i].msAlt = msAlt;	
				ms.dropOptions[i].msAlt = msAlt;
				
				if(ms.dispOptions[i]){
					msAlt.updateables.push();
					ms.dispOptions[i].msAlt = msAlt;					
				}
				
				ms.dictionary[msAlt.value] = msAlt;
			}
			
			//option events
			dropOption
				.observe("mouseover",function(event){
					dropOption.addClassName("highlight");
				})
				.observe("mouseout",function(event){
					dropOption.removeClassName("highlight");
				})
				.observe("click",function(event){
					ms.toggleOption(dropOption);
					event.stop();					
				});
			
			if(dropOption.msAlt.optionTag.selected){
				selectedOptions.push(dropOption.msAlt.optionTag);
			}
		});	
		
		//display events
		if(this.display){
			this.dispOptions.each(function(dispOption){
				dispOption
					.select("a")
					.each(function(delButton){
						delButton.observe("click",function(event){
							event.stop();
							ms.deselectOption(dispOption);
						});
					});
			});
		}		
		
		//initialize selected options
		$(selectedOptions).each(function(selectedOption){
			ms.selectOption(selectedOption);
		});
				
		//prevent click bubbling
		this.container.observe("click",function(event){
			event.stop();
		});

		this.recalculateSrollbar();
		
		this.inizialized = true;
	},
	
	/* toggles an option */
	toggleOption: function(opt){
		if(!opt){ return false; }
		if(opt.msAlt){ opt = opt.msAlt; } 	
		return !opt.dropOption.hasClassName("selected")? this.selectOption(opt) : this.deselectOption(opt);
	},
	
	/* selects an option */
	selectOption: function(opt, notNotify){
		if(!opt){ return false; }
		if(opt.msAlt){ opt = opt.msAlt; } 
		var changed = opt.optionTag.selected? false : true;
		opt.dropOption.addClassName("selected");
		opt.optionTag.selected = true;
		if(opt.dispOption){
			opt.dispOption.show();
		}
		if(this.inizialized && changed && !notNotify){
			this.container.fire("filter:changed", {
				filter: this,
				filterValue: this.val()
			});
		}
		this.conf.onSelect(opt);
		return this;
	},
	
	/* deselects an option */
	deselectOption: function(opt, notNotify){
		if(!opt){ return false; }
		if(opt.msAlt){ opt = opt.msAlt; } 
		var changed = opt.optionTag.selected? true : false;
		opt.dropOption.removeClassName("selected");
		opt.optionTag.selected = false;
		if(opt.dispOption){
			opt.dispOption.hide();
		}
		if(this.inizialized && changed && !notNotify){
			this.container.fire("filter:changed", {
				filter: this,
				filterValue: this.val()
			});
		}
		this.conf.onDeselect(opt);		
		return this;
	},
	
	/* updates the text of an option and all his friends, aka displays */
	updateOptionText: function(opt, text, remaining){
		if(!opt){ return false; }
		if(opt.msAlt){ opt = opt.msAlt; } 
		$(opt.updateables).each(function(elem){
			elem.innerHTML = elem.innerHTML.replace(/\(\d{1,}\)\s*$/,"(" + remaining + ")");
			//elem.innerHTML = text + " <span class='remaining'>(" + remaining + ")</span>";
		});
		return this;
	},

	/* updates the visibility of an option and all his friends, aka displays */
	updateOptionVisibility: function(opt, remaining){
		if(!opt){ return false; }
		if(opt.msAlt){ opt = opt.msAlt; }

		if(remaining){
			if(opt.dropOption && !opt.dropOption.visible()){
				opt.dropOption.show();
			}
		}
		else{
			if(opt.dropOption && opt.dropOption.visible()){
				opt.dropOption.hide();
			}
			//deselect if option was selected, hides display too
			this.deselectOption(opt, true);
		}
		return this;
	},
	
	/* deselects all options */
	clear: function(quiet){
		this.optionTags.each(function(option){
			this.deselectOption(option, quiet? true : false);
		}.bind(this));
		return true;
	},
	
	recalculateSrollbar: function(){
		
		//reset inner height to "auto"
		this.optInner.setStyle({"height": "auto"});	
		var optContDimension = this.optInner.getDimensions();
		this.optInner.setStyle({
			"height": (optContDimension.height > this.conf.selectMaxHeight? this.conf.selectMaxHeight+"px" : optContDimension.height+"px")
		});	
		
		//insert scrollbar
		if(!this.scrollbar){
			var track = new Element("div",{"class":"scrollbar-track"}).insert('<div class="scrollbar-handle"><div class="handle-top"></div><div class="handle-bottom"></div></div>');
			this.optContainer.insert({"bottom":track});
					
			var scrollbar = new Control.ScrollBar(this.optInner,track,{
				proportional:true,
				handle_minimum_height:70,
				pxToScroll: 57
			});	
			this.scrollbar = scrollbar;
			this.scrollbarTrack = track;
									
			var upButton = new Element("div",{"class":"scroll-button scroll-button-up"});
			var downButton = new Element("div",{"class":"scroll-button scroll-button-down"});
			track.insert({"bottom":downButton});
			track.insert({"bottom":upButton});
			upButton
				.observe("mousedown",function(event){
					scrollbar.scrollBackwards();
					event.stop();
				})
				.observe("mousemove",function(event){
					event.stop();
				});
			downButton
				.observe("mousedown",function(event){
					scrollbar.scrollForward();
					event.stop();
				})
				.observe("mousemove",function(event){
					event.stop();
				});
		}
		this.scrollbar.recalculateLayout();
		this.optInner.setStyle({
			"paddingRight": (this.scrollbarTrack && this.scrollbarTrack.visible()? this.conf.scrollbarWidth+"px" : "0px")
		});
		this.scrollbar.recalculateLayout();
		return this;
	},	
	
	/* getter and setter for the drop-value
	 * newVal:
	 * 		if left empty, the method acts as a getter
	 * 		to set a value, use an array, containing the 
	 * 		values to select, all other values will be deselected:
	 * 			[ 2,4,6,7,10 ]
	 */
	val: function(newVal){
		if(newVal){
			//setter
			var fc = this;
			fc.clear();			
			fc.container
				.select('[rel="'+newVal.join('"],[rel="')+'"]')
				.each(function(option){
					fc.selectOption(option);
				});
			return fc.val();
		}
		else{
			//getter
			var selected = [];
			$(this.optionTags).each(function(opt){
				if(opt.selected){
					selected.push({name: "", value: (opt.value || opt.innerHTML)})
				}
			});
			return selected.length? selected : false;
		}
	},
	update: function(options){
		var ms = this;
		var cleanOptions = {};
		$(options).each(function(option){
			cleanOptions[option.value] = option;
		});
		var optionCnt = 0;
		$(ms.dropOptions).each(function(dropOption){
			var option =	dropOption &&
							dropOption.msAlt &&
							dropOption.msAlt.value &&
							cleanOptions[dropOption.msAlt.value]?
								cleanOptions[dropOption.msAlt.value] : false;

			if(option){
				option.remaining = parseInt(option.remaining, 10);
				ms.updateOptionText(ms.dictionary[option.value], option.title, option.remaining);
				optionCnt++;
			}
			else{
				option = {
					title: false,
					value: dropOption.msAlt.value,
					remaining: 0,
					selected: false
				}
			}
			ms.updateOptionVisibility(ms.dictionary[option.value], option.remaining);

			//update selected
			if(option.selected === true){
				ms.selectOption(ms.dictionary[option.value], true);
			}
			else if(option.selected === false){	
				ms.deselectOption(ms.dictionary[option.value], true);
			}
		});

		this.recalculateSrollbar();
		return ms;
	},
	hasVisibleOptions: function(){
		//count active options
		var ms = this;
		if(ms.val()){
			return true;
		}
		var activeOpts = 0;
		$(ms.dropOptions).each(function(opt){
			if(opt.visible()){
				activeOpts++;
			}
		});
		//return has selectable options
		return activeOpts>1 ? true : false;
	}
});
Object.Event.extend(Control.MultiSelect);
