/**
 * Custom selects
 */
UB.CustomSelect = new Class({
	Extends: UB,
	
	options: {
	 	classNames: {
			trigger: 'trigger',
			content: 'content',
			optgroup: 'optgroup',
			
			selected: 'selected',
			halfselected: 'halfselected',
			deselected: ''
 		},
		
		maxOptgroupHeight: 100
 	},
	
	elements: {
		original: null, clone: null,
		container: null,
		trigger: null,
		content: null,
		optionLis: [],
		optgroupLis: []
	},
	
	initialize: function (originalEl, options) {
		this.parent(options);
		this.elements.original = $(originalEl);
		
		this._buildCustom();
		this._replaceOriginal();
		this._addScrollBars();
		
	},
	
	_buildCustom: function () {
		// get data
		// hide 
		// this.elements.original.hide();
		var options = this.elements.original.getChildren();
		
		// create elements
		this.elements.container = new Element('div', {
			'class': this.elements.original.get('class')
		});
		this.elements.trigger = new Element('div', {
			'class': this.options.classNames.trigger
		});
		// root UL element
		this.elements.content = new Element('ul', {
			'class': this.options.classNames.content
		})
		
		// options
		$each (options, function (option) {
			
			// default - value for trigger
			if (option.get('tag') == 'option' && option.value == "") {
				this.elements.trigger.set('html', option.get('html'));
				return; // continue
			}
			
			// root options
			else if (option.get('tag') == 'option') {
				var el = this._buildCustomOption(option);
				this.elements.optionLis.push(el); 
			}
			
			// sub / optgroups -> sub UL->LIs
			else if (option.get('tag') == 'optgroup') {
				var el = this._buildCustomParentOption(option);
				this.elements.optgroupLis.push(el);
				// sub ul
				var ul = new Element('ul').inject(el, 'bottom');
				// sub options
				option.getElements('option').each(function (suboption) {
					var li = this._buildCustomOption(suboption);
					this.elements.optionLis.push(li);
					ul.grab(li, 'bottom');
				}, this);
				this.updateParentState(el);
			}
			// add to root ul
			this.elements.content.grab(el, 'bottom');
		}, this);
		
	},
	
	_buildCustomParentOption: function (optgroup) {
		return new Element('li', {
			'html': optgroup.get('label'),
			'class': this.options.classNames.optgroup,
			'events': {
				'click': this.parentOptionClickHandler.bindWithEvent(this)
			},
			'storage': {
				'ubcs:label': optgroup.get('label'),
				'ubcs:optgroup': optgroup
			}
		});
	},
	
	_buildCustomOption: function (option) {
		return new Element('li', {
			'html': option.get('html'),
			'class': option.get('class') + (option.selected? ' selected' : ''),
			'events': {
				'click': this.optionClickHandler.bindWithEvent(this)
			},
			'storage': {
				'ubcs:initiallySelected': option.selected,
				'ubcs:value': option.value,
				'ubcs:option': option
			}
		});
	},
	
	_replaceOriginal: function () {
		// get coordinates realive to parent (checks wich parent is relative to)
		var coords = this.elements.original.getCoordinates( this.elements.original.getOffsetParent() );
		// set container
		this.elements.container.set({
			'styles': {
				'position': 'absolute',
				'top': coords.top,
				'left': coords.left
				//'width': coords.width,
				//'width': this.elements.original.getStyle('width'),
			},
			'events': {
				'mouseenter': this.show.bind(this),
				'mouseleave': this.hide.bind(this)
			}
		});
		
		// hide orig + need to make it multiples and hack to keep the the height
		if (this.elements.original.get('multiple')) {
			this.elements.original.setStyle('visibility', 'hidden');
		}
		else {
			// IE 6 patch
			if (Browser.Engine.trident && Browser.Engine.version < 5) {
				this.elements.original.set({
					'multiple': 'multiple',
					'styles': {
						'height': coords.height,
						'visibility': 'hidden'
					}
				});
			} else {
				// insert fake clone to keep the place
				this.elements.clone = this.elements.original.clone().erase('name').setStyle('visibility', 'hidden');
				this.elements.original.set({
					'multiple': 'multiple',
					'styles': {
						'display': 'none'
					}
				});
				this.elements.clone.inject(this.elements.original, 'after');
			}
		} 
		
		// build
		this.elements.container.adopt(this.elements.trigger, this.elements.content);
		
		// insert to DOM
		this.elements.container.inject(this.elements.original, 'after');
		
		// Slide initially hidden
		//this.elements.content.set('slide', {duration: '500', transition: 'back:out'});
		//this.elements.content.slide('hide');
		this.fxSlideIn =  new Fx.Slide(this.elements.content, {duration: '450', transition: (new Fx.Transition(Fx.Transitions.Back, 1.9)).easeOut});
		this.fxSlideOut =  new Fx.Slide(this.elements.content, {duration: '350', transition: (new Fx.Transition(Fx.Transitions.Back, 1.6)).easeIn});
		this.fxSlideOutPart =  new Fx.Slide(this.elements.content, {duration: '300', transition: 'quad:out'});
		this.fxSlideOut.hide();
	},
	
	_addScrollBars: function ()
	{
		// ciclce optrgoups and add  scrollbars
		$each (this.elements.optgroupLis, function (optGroupEl) {
			var ulEl =  optGroupEl.getElement('ul');
			
			// check if needed
			if (ulEl.getScrollSize().y <= this.options.maxOptgroupHeight) {
				return;
			}
			
			// change els to proper style
			ulEl.setStyles({
				overflow: 'hidden',
				height: this.options.maxOptgroupHeight + "px"
			});
			optGroupEl.setStyle('position', 'relative');
							
			// create the scoller element
			var scrollerEl = new Element('div', {
				'class': 'scrollarea'
			});
			scrollerEl.set('html', '\
				<div class="scrollBack"></div>\
				<div class="scrollBarContainer">\
					<div class="scrollKnob"></div>\
				</div>\
				<div class="scrollForward"></div>'
			);
			scrollerEl.getElement('.scrollBarContainer').setStyle('height', this.options.maxOptgroupHeight - 2 + 'px');

			// add
			scrollerEl.inject(ulEl, 'after');
			
			// initialize the MooScroller
			new MooScroller(ulEl, scrollerEl.getElement('.scrollKnob'));
		}, this);
	},

	show: function () {
		//this.elements.content.slide('in');
		this.fxSlideOut.cancel();
		this.fxSlideIn.slideIn();
		this.fireEvent('show');
	},
	hide: function () {
		//this.elements.content.slide('out');
		if (this.fxSlideIn.isRunning()) {
			this.fxSlideIn.cancel();
			this.fxSlideOutPart.slideOut();
		} else {
			this.fxSlideOut.slideOut();
		}
		this.fireEvent('hide');
	},
	
	optionClickHandler: function (event) {
		// check
		if (event.target.get('tag') != 'li') return;
		
		// prevent to booble to parent li click
		event.stop(); 
		
		// el  and parent if exists
		var el = event.target;
		var parentEl = el.getParent('li');
		
		// selected -> deselect
		if (el.hasClass(this.options.classNames.selected)) {
			this.setState(el, -1);
		} 
		// not elected -> select
		else {
			this.setState(el, 1);
		}
		
		// parent set
		this.updateParentState(parentEl);
		
		// fire event
		this.fireEvent('change', el);
	},
	parentOptionClickHandler: function (event) {
		//this.fireEvent('change');
		var parentEl = event.target;
		
		var state = this.updateParentState(parentEl);
		state = (state <= 0)? 1 : -1;
		
		// set sub els
		parentEl.getElements('li').each(function (el) {
			this.setState(el, state);
		}, this);
		
		// set parent
		this.setState(parentEl, state);
		
		
	},
	updateParentState: function (parentEl) {
		if (! parentEl) return;
		
		// children
		var selected = parentEl.getElements('li[class~=' + this.options.classNames.selected + ']');
		var all = parentEl.getElements('li');
		
		var state;
		if (selected.length == 0) {
			state = -1;
		} else if (selected.length == all.length) {
			state = 1;
		} else {
			state = 0;
		}
		this.setState(parentEl, state);
		return state;
		
	},
	setState: function (el, state) {
		// remove old
		el.removeClass(this.options.classNames.deselected);
		el.removeClass(this.options.classNames.selected);
		el.removeClass(this.options.classNames.halfselected);
		// set new
		switch (state) {
			case -1: // deselected / none
				el.addClass(this.options.classNames.deselected);
				break;
			case 0: // halfselected
				el.addClass(this.options.classNames.halfselected);			
				break;
			case 1: // selected
				el.addClass(this.options.classNames.selected);			
				break;
		}
		// set orginal option selected state
		var originalOption = el.retrieve('ubcs:option');
		if (originalOption) {
			if (state == 1) {
				originalOption.selected = true;
			} else {
				originalOption.selected = false;
			}
		}
	},
	
	reset: function () {
		this.elements.optionLis.each( function (el) {
			this.setState(el,  el.retrieve('ubcs:initiallySelected')? 1 : -1);
		}, this);
		this.elements.optgroupLis.each( function (el) {
			this.updateParentState(el);
		}, this);
	},
	
	getSelectedValues: function () {
		return this.getSelectedHash().getKeys();
	},
	getSelectedTexts: function (joinParent) {
		return this.getSelectedHash(joinParent).getValues();
	},
	getSelectedHash: function (joinParent) {
		var hash = $H({});
		var join = joinParent;
		this.elements.content.getElements('li[class~=' + this.options.classNames.selected + ']').each(function(el){
			var value = el.retrieve('ubcs:value');
			if (! value) { 
				return;
			}
			var text = el.get('html');
			if (joinParent) {
				var parent = el.getParent('li');
				if (parent) {
					text = parent.retrieve('ubcs:label') + joinParent + text;
				}
			}
			hash[value] = text;
		}, this);
		return hash;
	}
 });
 
 
