(function($) {

$.widget("ui.vv_button", {

	init: function() {
		var self = this;
		var notValid;
		var btn = this.options;
		var mng = btn.mng;
		if(!(mng && mng._factory) ) 
			notValid = 'manager-missing';
		else	
			notValid = mng._factory._setType(self.element, btn);
		if(notValid) { 
			var error = new Error(notValid);
			error.name = notValid; 
			throw error; 
		}
		mng._factory._init(self.element, btn);
		self.setOptions({
			disabled: btn.disabled,
			selected: btn.selected,
			visible	: btn.visible
		});
	}, 
	
	destroy: function() {
		var self = this;
		var btn = self.options;
		var klass = btn.klass;
		self.element.removeClass(btn.mng.name + '-' + klass.name)
			.removeClass(klass.name + '-' + klass.render[btn._state])
			.unbind('.vv_button');
		if (btn.klass.mode == 'radio') {
			btn.mng._factory._unselect(self.element.get(0), btn);
			self.element.removeClass(btn.group.name)
		}
		$.widget.prototype.destroy.call(self);
	},
	
	getButton: function() {
		return this.options;
	},
	
	remove: function() {
		var self = this;
		var el = self.element; 
		self.destroy();
		el.remove();
	},
	
	setDisabled: function(disabled) {
		var self = this;
		var btn = self.options;
		var mode = btn.klass.mode;
		btn.disabled = disabled;
		btn.mng._factory._render(self.element.get(0), null, btn);
	},

	setOptions: function(options) {
		var self = this;
		if(typeof(options.value) == 'string')
			self.setValue(options.value);
		if (typeof(options.title) == 'string')
			self.setTitle(options.title);
		if (typeof(options.selected) == 'boolean')
			self.setSelected(options.selected)
		if(typeof(options.disabled) == 'boolean')
			self.setDisabled(options.disabled);
		if (typeof(options.visible) == 'boolean') 
			self.setVisible(options.visible);
		if(options.data != undefined)
			self.setData('data', options.data); 
	},
	
	setSelected: function(selected) {
		var self = this;
		var btn = self.options;
		var mode = btn.klass.mode;
		if (mode == 'switch' || mode == 'radio') 
			btn.mng._factory._select(self.element.get(0), btn, 'select', selected);		
	},
	
	setTitle: function(title) {
		var self = this;
		var btn = self.options;
		btn.title = title;
		btn.mng._factory._setTitle(self.element, btn);
	},
	
	setValue: function(value) {
		var self = this;
		var btn = self.options;
		var impl = Vivieb[btn.type + 'BtnMng'];
		btn.value = value;
		impl._setValue(self.element, btn);
		delete btn._state;
		btn.mng._factory._render(self.element.get(0), null, btn);
	},
	
	setVisible: function(visible) {
		var self = this;
		var btn = self.options;
		var mode = btn.klass.mode;
		if (mode == 'radio' && !visible && btn.selected)
			btn.mng._factory._unselect(self.element.get(0), btn)
		self.element.css('visibility', visible ? 'visible' : 'hidden');
	}
});

$.extend($.ui.vv_button, {
	getter: ['getButton'],
	defaults: {
		mng			: null,		// ButtonMng used as manager for this button
		className	: '', 		// Name of the button class as defined in mng 
		visible		: true		// true (default) if the button is initially visible		
//		value		: '',		// the value (label) of the button
//		disabled	: false,	// true if the button is initially disabled (default: false)
//		selected	: false,	// true if the button is initially selected (default: false). only for switch e radio classes
//		groupName	: '',		// the name of the group that owns the button. Only for radio classes
//		data		: null		// optional additional data that will be attached to the corresponding Button object passed to the handlers
	}
});



Vivieb.ButtonFactory = {
	
	_staticStates	: ['disabled', 'normal'],
	_iconStates		: ['disabled', 'normal', 'hover'],
	_buttonStates	: ['disabled', 'normal', 'hover', 'pressed'],
	_switchStates	: ['off-disabled', 'off', 'off-hover', 'on', 'on-hover', 'on-disabled'],
	_radioStates	: ['off-disabled', 'off', 'off-hover', 'on', 'on-hover', 'on-disabled'],
	
	defaults: {
		path: 'images/',
		ext	: 'png'	
	},
	
	_mngs: [],

	
//	 * no parameters: destroy _mngs;
//	 * string name parameter: destroy only _mngs[name]
//	 * Event parameter: window unload event handler
		
	destroy: function() {
		var self = this;
		if(typeof(arguments[0]) == 'string') {
			var mng = $.vv_getItem(self._mngs, 'name', arguments[0]);
			if(mng) mng.destroy();
			return;
		}
		else if(arguments[0] && arguments[0].data)
			self = arguments[0].data;
		if(self._mngs)
			for(var i=0, ln=self._mngs.length; i<ln; i++)
				self._mngs[i].destroy();
	},
	
	getManager: function(name) {
		var self = this;
		var mng = $.vv_getItem(self._mngs, 'name', name);
		if (mng == null) {
			mng = new Vivieb.ButtonManager(this, name);
			self._mngs.push(mng);
		}
		return mng;
	},
	
// private

	_check: function(jElem, btn) {
		if(!btn.className)
			return "class-missing";
		var klass = $.vv_getItem(btn.mng._classes, 'name', btn.className); 
		if(!klass)
			return "class-not-defined";
		var impl = Vivieb[btn.type + 'BtnMng'];
		if(!impl)
			return "invalid-class";
		if(!impl._check(jElem))
			return "mismatch-element-type";			
		return null;
	},
	
	_doClick: function(element, btn) {
		this._select(element, btn, 'click');
		var clickHandler = btn.klass.actions.click;
		if ($.isFunction(clickHandler)) 
			clickHandler.call(element, btn);
	},
	
	_getGroup: function(mng, name) {
		if(!name) name = 'default'
		var group = $.vv_getItem(mng._groups, 'name', name);
		if (!group) {
			group = {name: name};
			mng._groups.push(group);
		}
		return group;
	},

	_getState: function(evType, mode, selected) {
		var state;
		if(evType == 'disable')
			state = selected ? 5 : 0;
		else if(mode == 'static')
			state = 1;
		else if(mode == 'icon')
			switch(evType) {
			case 'mouseenter'	: state = 2; break;
			case 'mouseleave'	: state = 1; break;
			case 'mousedown'	: state = 2; break;
			case 'mouseup'		: state = 2; break;
			default: state = 1;
			}
		else if(mode == 'button')
			switch(evType) {
			case 'mouseenter'	: state = 2; break;
			case 'mouseleave'	: state = 1; break;
			case 'mousedown'	: state = 3; break;
			case 'mouseup'		: state = 2; break;
			default: state = 1;
			}
		else if(mode == 'switch' || mode == 'radio')
			switch(evType) {
			case 'mouseenter'	: state = selected ? 4 : 2; break;
			case 'mouseleave'	: state = selected ? 3 : 1; break;
			case 'mousedown'	: state = selected ? 4 : 2; break;
			case 'mouseup'		: state = selected ? 4 : 2; break;
			case 'click'		: state = selected ? 4 : 2; break;
			default: state = (selected ? 3 : 1);
			}
		return state;
	},

	_getType: function(el) {
		var tag = el.tagName.toLowerCase();
		if(tag == 'input' && el.type == 'button')
			return '_c';
		if(tag == 'img')
			return '_i';
		if (tag == 'div' || tag == 'span' || tag == 'li')
			return '_l';
		return null;
	},
	
	_init: function(jElem, btn) {
		var mng = btn.mng;
		var factory = mng._factory;
		if(!btn.className)
			btn.className = 'default';
		var klass =  $.vv_getItem(mng._classes, 'name', btn.className);
		if(!klass) {
			klass = {name: 'default', mode: 'static'};
			mng._classes.push(klass); 
		}
		delete btn.className;
		btn.klass = klass;
		jElem.addClass(mng.name + '-' + klass.name);
		var impl = Vivieb[btn.type + 'BtnMng'];
		if (impl) {
			impl._init(jElem, btn);
			impl._setValue(jElem, btn);
		}
		factory._setTitle(jElem, btn);
		if(klass.mode == 'radio') {
			btn.group = this._getGroup(mng, btn.groupName);
			jElem.addClass(btn.groupName);
			delete btn.groupName;
		}
		factory._render(jElem.get(0), null, btn);
		var events = ['mouseenter', 'mouseleave', 'mouseup', 
			'mousedown', 'click', 'dblclick'];
		jElem.bind(events.join('.vv_button ') + '.vv_button', 
				btn, factory._mouseHandler);
	},
	
	_mouseHandler: function(ev) {
		var btn = ev.data;
		if(!btn || btn.disabled) 
			return;
		var evType = ev.type == 'dblclick' ? 'click' : ev.type;
		if (evType == 'click') 
			btn.mng._factory._doClick(this, btn);
		else {
			btn.mng._factory._render(this, evType, btn);
			var handler = btn.klass.actions[evType];
			if ($.isFunction(handler)) 
				handler.call(this, btn);
		}
		ev.stopPropagation();
	},
	
	_render: function(element, evType, btn) {
		var klass = btn.klass;
		var state = this._getState(btn.disabled ? 'disable' : evType, 
			klass.mode, btn.selected);
		if(btn._state == state)
			return;
		if(!isNaN(btn._state))
			$(element).removeClass(klass.name + '-' + klass.render[btn._state]);
		$(element).addClass(klass.name + '-' + klass.render[state]);
		btn._state = state;	
		var impl = Vivieb[btn.type + 'BtnMng']; 
		if(impl._render)
			impl._render(element, btn, state);
	},
	
	_select: function(element, btn, evType, selected) {
		switch(btn.klass.mode) {
			case 'switch':
				if (evType == 'click') 
					selected = !btn.selected;
				else if (btn.selected == selected) 
					return;
				btn.selected = selected;
				btn.mng._factory._render(element, evType, btn);
				break;
			case 'radio':
				if (evType == 'click') {
					if(btn.selected)
						return;
					selected = true;
				}
				var factory = btn.mng._factory;
				if (selected) {
					var unselElem = btn.group.selected || null;
					var unselBtn = unselElem == null ? null : $(unselElem).vv_button('getButton');
					var beforeSelect = btn.klass.actions.beforeSelect;
					var doSelect = true;
					if ($.isFunction(beforeSelect)) 
						doSelect = beforeSelect.call({
							element: element,
							btn: btn,
							unselElement: unselElem,
							unselBtn: unselBtn
						}) !== false;
					if (!doSelect) 
						return;
					
					btn.group.selected = element;
					btn.selected = true;
					if (unselElem) 
						factory._unselect(unselElem, unselBtn);
					factory._render(element, evType, btn);
				}
				else 
					factory._unselect(element, btn);
				break;
		}		
	},
	
	_setTitle: function(jElem, btn) {
		if(btn.title != undefined)
			jElem.attr('title', btn.title);
		else if(!jElem.attr('title') && btn.value != undefined)
			jElem.attr('title', btn.value);
	},
	
	_setType: function(jElem, btn) {
		var el = jElem.get(0);
		var type = this._getType(el);
		if(type == null)
			return "invalid-element-type";
		if(type == '_l' && jElem.find('a:last').length == 0)
			jElem.wrapInner('<a href="#"></a>');
		btn.type = type;
	}, 

	_unselect: function(element, btn) {
		btn.selected = false;
		if(btn.group && btn.group.selected == element)
			delete btn.group.selected;
		btn.mng._factory._render(element, null, btn);
		var unselect = btn.klass.actions.unselect;
		if($.isFunction(unselect)) 
			unselect.call(element,btn);
	}	
};


$(window).bind('unload', Vivieb.ButtonFactory, Vivieb.ButtonFactory.destroy);


	
Vivieb._BtnMng = {

	initialize: function(factory, name){
		var self = this;
		self._factory = factory;
		self.name = name;
		self._classes = [];
		self._cache = [];
		self._groups = [];
	},
	
	addClass: function(options) {
		if(!(options && options.name))
			return null;
		var self = this;
		
		var klass = {
			name	: options.name,
			mode	: options.mode || 'static',
			actions	: $.extend(true, {}, options.actions)
		}
		$.vv_addUniqueItem(self._classes, 'name', klass);
		var render = options.render || {};
		if(typeof(render.path) == 'string') {
			if(!$.vv_endWith(render.path, '/'))
				render.path += '/';
			klass.path = render.path;
		}
		if(typeof(render.ext) == 'string')
			klass.ext = render.ext;
		var states = self._factory['_' + klass.mode + 'States'];
		var r = klass.render = [];
		for(var i=0, ln=states.length; i<ln; i++) 
			r.push(render[$.vv_camelize(states[i])] || states[i]);
	},
	
	destroy: function() {
		var self = this;
		var classes = self._classes;
		if(!classes)
			return;
		for(var i=0, ln=classes.length; i<ln; i++)
			$('.' + self.name + '-' + classes[i].name).vv_button('destroy');
		delete self._classes;
		delete self._cache;
		delete self._groups;
		$.vv_removeItem(self._factory._mngs, self);
		delete self._factory;
	},
	
	getClass: function(className) {
		return $.vv_getItem(this._classes, 'name', className);
	},
	
	isManaged: function(element, className) {
		var self = this;
		var classes = self._classes;
		var jElem = $(element);
		if(className && self.getClass(className) != null)
			return jElem.hasClass(self.name + '-' + className);
		for(var i=0, ln=classes.length; i<ln; i++)
			if(jElem.hasClass(self.name + '-' + classes[i].name))
				return true;	
		return false;
	},
	
	removeClass: function(className) {
		var self = this;
		var idx = $.vv_indexOfKey(self._classes, 'name', className);
		if(idx != -1)
			self._classes.remove(idx);
	}

};

Vivieb.ButtonManager = new Class(Vivieb._BtnMng);



Vivieb._cBtnMng = {

	_init: function(jElem, btn) {
		if (btn.value == undefined) 
			btn.value = jElem.val() || jElem.attr('id') || '';
	},

	_setValue: function(jElem, btn) {
		jElem.val(btn.value || '');
	}
};



Vivieb._iBtnMng = {

	staticPF	: ['_fx', '_f'],
	iconPF		: ['_fx', '_f', '_fh'],
	buttonPF	: ['_fx', '_f', '_fh', '_fd'],
	switchPF	: ['_fx', '_f', '_fh', '_o', '_oh', '_ox'],
	radioPF		: ['_fx', '_f', '_fh', '_o', '_oh', '_ox'],
	
	_init: function(jElem, btn) {
		if(btn.value == undefined)
			btn.value = jElem.attr('id') || ''; 
		if(btn.title == undefined)
			btn.title = jElem.attr('title') || btn.value || ''; 
	},
	
	_setValue: function(jElem, btn) {
		jElem.attr('alt', btn.value || '');
		this._preload(btn.mng, btn.klass, btn.value || '');
	},

	_preload: function(mng, klass, basicName) {
		var cache = $.vv_getItem(mng._cache, 'name', klass.name);
		if(!cache) {
			cache = {name: klass.name, imgs: []};
			mng._cache.push(cache);
		}
		var btnImgs = $.vv_getItem(cache.imgs, 'name', basicName);
		if(!btnImgs)
		{
			btnImgs = {name: basicName, states: []};
			var postfixes = this[klass.mode + 'PF'];
			var url = (klass.path || mng._factory.defaults.path) + basicName;
			var ext = (klass.ext || mng._factory.defaults.ext);
			for(var i=0, ln=postfixes.length; i<ln; i++) {
				var img = new Image();
				img.src = url + postfixes[i] + '.' + ext;
				btnImgs.states.push(img);
			}
			cache.imgs.push(btnImgs)
		}
	},
	
	_render: function(element, btn, state) {
		
		var cache = $.vv_getItem(btn.mng._cache, 'name', btn.klass.name);
		var btnImgs = $.vv_getItem(cache.imgs, 'name', btn.value);
		if(!btnImgs) 
			throw new Error('images-cache-missing');
		var img = btnImgs.states[state];		
		if(!img) 
			throw new Error('image-preload-missing (state:'+ state + ')');
		element.src = img.src;
	}
};


Vivieb._lBtnMng = {

	_init: function(jElem, btn) {
		if(btn.value == undefined)
			btn.value = jElem.find('a:last').text() || jElem.attr('id') || '';
		if(btn.title == undefined)
			btn.title = jElem.attr('title') || btn.value; 
	},
	
	_setValue: function(jElem, btn) {
		jElem.find('a:last').text(btn.value || '');
	}
};

$.expr[':'].vv_button = function(el, i, m) {
	
	var type = Vivieb.ButtonFactory._getType(el);
	if(type == null)
		return false;
	var mng = Vivieb.ButtonFactory.getManager(m[3]);
	if(!mng)
		return false;
	// last item could be undefined! Remove it.
	if(!m[m.length -1])
		m.remove(m.length -1);
	if(m.length == 4)
		return mng.isManaged(el);
	var i=4;
	var types = [];
	var selected = false;
	while(i<m.length) {
		var klass = m[i];
		if (klass.charAt(0) == '.') {
			switch (klass) {
				case '.classic':
					types.push('_c');
					break;
				case '.image':
					types.push('_i');
					break;
				case '.link':
					types.push('_l');
					break;
			}
		}
		else 
			if (!selected) 
				selected = mng.isManaged(el, klass);
		i++;
	}
	if(!selected && (m.length > type.length + 4))
		return false;
	for(var j=0, ln=types.length; j<ln; j++)
		if(types[j] == type)
			return true;

	return false;
	
};

})(jQuery);