Prototype.require('Object.Event');

if (!Control) var Control = { };

/**
 * Tabs controller
 * 
 * Usage:
 *  var tabs = new Control.Tabs($('wrapper'), {cyclic : true});
 *  tabs.observe('select', function(event, caller) {
 *  	console.log(caller+' selected!');
 *  })
 *  .observe('unselect', function(event, caller) {
 *  	console.log(caller+' unselected!');
 *  });
 * 
 * Options :
 *  - selected : index of the selected tab
 *  - show : function triggered when the tab is shown
 *  - hide : function triggered when the tab is hidden
 *  - cyclic : if true the next() function will go to the first when the last tab is selected (inverse for previous())
 *  
 *  - delay : if specified the next function will be automatically called every [delay] seconds
 *  - auto : if true the automatic tab slider will be launched when the object is created
 *  - max : if specified the max number of tabs
 * 
 * Events :
 *  - select (event, caller) : 
 *  - unselect (event, caller) :
 *  
 * Structure:
 * 
 */
Control.Tabs = Class.create({
	initialize: function(container, options) {
		if(!$(container))
			throw "Could not find the element: " + container;
		this.container = $(container);
		this._selected = null;
		this.tabs = [];
		
		this.options = this.options || {};
		this.timer = null;
		
		this._locked = false;
		
		Object.extend(this.options, {	      
		      //toggle : 'onclick',
			  selected : 0,
		      show: Element.show,
		      hide: Element.hide,
		      cyclic: false,
		      delay: null,
		      auto: true,
		      max: 5
		    }
		);
	    Object.extend(this.options, options || {});
	    
		//onload behavior
		$(document).ready(this._onReady.bind(this));
	},
	_onReady: function() {
		var containerId = this.container.identify();
		
		this.tabs = $$('#'+containerId+' .tab-content');
		this.tabs.invoke('hide');
		
		//Connect selector buttons		
		$$('#'+containerId+' .tab-action')
		.each(function(element) {
			this.addLink(element);
		}, this);
	
		if(this.options.selected != null) {
			this.select(this.options.selected);
		}
		
		if(this.options.auto) {
			this.start();
		}
		return this;
	},
	addLink: function(element) {
		element = $(element);

	    var targetName = element.getTabTarget();
	    var target = $(targetName);
	    
	    if(target || ['previous', 'next', 'first', 'last'].indexOf(targetName) >= 0) {
	    	element.observe('click', this.onClick.bind(this));
		}
	    return this;
	},
	onClick: function(event) {
		if(!this._lock()) {
			return;
		}
		this.stop();
		var element = event.findElement('a') || event.findElement('button');
		
		if(element) {
			//Call the action on the target
			this[element.getTabAction()].call(this, element.getTabTarget());
		}
    	
    	this._unlock();
		event.stop();
	},
	select: function(element) {	
		var index = this.indexOf(element);
		if(index < 0) {
			throw "" + element + " is not a valid tab.";
		} else if(index != this._selected) {
			//hide previous tab
			if(this._selected != null) {
				this.unselect();
			}
			this._selected = index;

			//Display the tab
			this._show(this.tabs[this._selected]);
			
			//Restart updater
			this.start();
			
			this.notify('select', this.tabs[this._selected]);
		}
		return this;
	},
	unselect: function() {
		if(this._selected != null) {
			this.notify('unselect', this.tabs[this._selected]);
			this._hide(this.tabs[this._selected]);
		}
		this._selected = null;
		return this;
	},
	selected: function() {
		return this.tabs[this._selected];
	},
	get: function(element) {
		return (typeof(element) != 'undefined') ? this.tabs[this.indexOf(element)] : this.tabs;
	},
	indexOf: function(element) {
		if(typeof(element) == 'undefined') {
	        return;
		} else if(typeof element == 'number') {
			count = this.tabs.size();
			element = Math.floor(element);
			return this.options.cyclic ? (element + count) % count : Math.max(Math.min(element, count - 1), 0);
		} else if(element == 'first') {
			return 0;
		} else if(element == 'last') {
			return this.size() - 1;
		} else if(element == 'previous') {
			count = this.size();
			return this.options.cyclic ? (this._selected + count - 1) % count : Math.max(0, this._selected - 1);
		} else if(element == 'next') {
			count = this.size();
			return this.options.cyclic ? (this._selected + 1) % count : Math.min(count - 1, this._selected + 1);
		} else {
			return this.tabs.indexOf($(element));
		}
		return -1;
	},
	remove: function(element) {
		if(this.size() == 1) {
			return;
		}
		var index = this.indexOf(element);
		
		if(index >= 0) {
			if(this._selected == index) {
				(index == this.indexOf('last')) ? this.previous() : this.next();
			}
			if(index < this._selected) {
				this._selected = Math.max(0, this._selected - 1);
			}
			//Notify that tab will be removed
			removed = this.tabs[index];
			this.tabs.splice(index, 1);
			this.notify('remove', removed);
			
			//Remove tabs
			removed.remove();
		}
		return this;
	},
	create: function(position) {
		if(this.size() == this.options.max) {
			return;
		}
		
		this.unselect();
		
		//Insert element
		var i = 0;
		while($('tab-content-' + i)) i++;

		var tab = $(this.tabs.first().cloneNode(true));
		tab['id'] = 'tab-content-' + i;
		this.get(position || 'last').insert({after: tab});
		this.tabs.push(tab);
		
		this.notify('create', tab);
		
		tab.getTabLinks().each(this.addLink.bind(this));
		this.select(tab);
	},
	size: function() {
		return this.tabs.size();
	},
	first: function() {
		return this.select(this.indexOf('first'));
	},
	last: function() {
		return this.select(this.indexOf('last'));
	},
	previous: function() {	
		return this.select(this.indexOf('previous'));
	},
	next: function() {	
		return this.select(this.indexOf('next'));
	},
	start: function () {
		if(this.options.delay != null) {
	        if (this.timer != null) {
				clearTimeout(this.timer);
	        }
			this.timer = setTimeout(this.next.bind(this), this.options.delay * 1000);
		}
    },
	pause: function () {
		this.stop();
		this._unlock();
    },
	stop: function () {
		if (this.timer) {
			clearTimeout(this.timer);
		}
	},
	_unlock: function () {
		this._locked = false;
		//this.controls.invoke('addClassName', this.options.disabledClassName);
    },
	_lock: function () {
    	if(this._locked == true) {
    		return false;
    	}
    	this._locked = true;
		//this.controls.invoke('removeClassName', this.options.disabledClassName);
    	return true;
    },
    _show: function(element) {
    	$(element).addClassName('tab-selected');
    	if(this.options.show) {
    		var show = this.options.show.bind(this);
    		show(element);
    	}
    },
    _hide: function(element) {
    	$(element).removeClassName('tab-selected');
    	if(this.options.hide) {
    		var hide = this.options.hide.bind(this);
    		hide(element);
    	}
    }
});

//Add methods to element
Element.addMethods({
	getTabTarget: function(element){
		var linkUrl = element.getAttribute('rel') || element.getAttribute('href');
		return linkUrl ? 
				linkUrl.replace(window.location.href.split('#')[0],'').split('#').last().replace(/#/,'') :
				null;
	},
	getTabAction: function(element) {
		var prefix = 'tab-action-';
		return $w(element.className)
			.find(function(name) {return  name.substr(0, prefix.length) == prefix})
			.substr(prefix.length);
	},
	getTabLinks: function(element){
		if(element.hasClassName('tab-content')) {
			return $$('.tab-action').findAll(function(el) {return  el.getTabTarget() == element.identify();});
		}
		return [];
	}
}
);

Object.Event.extend(Control.Tabs);

Prototype.provide('Control.Tabs', Control.Tabs);

