//= require <mootools-core-1.3-full-nocompat>
//= require <uiframework/UICEvent>
//= require <uiframework/UIComponent>
//= require <uiframework/ListData>
//= require <uiframework/UIDUtil>
//= require <uiframework/UIButton>
var TabBar = function(){
	this.Extends = UIComponent

	this.tabs = null;

	this._dataProvider = null;
	this._selectedIndex = -1;
	this._tabSkins = null;
	this._labelStyles = null;
	this._freeTabs = null;
	this._tabWidth = 0;
	this._tabHight = 0;
	this._horizontalGap = 3;
	this._animate = true;
	
	this._animateIn = null;
	this._animateOut = null;
	this._activeTweenList = null;
	this._labelField = "label";

	this.tabSkinChanged = false;
	this.selectedIndexChanged = false;
	this.dataProviderChanged = false;
	this.labelStylesChanged = false;
	this.selectedItem = null;
	this.animationTween = null;
	this.inTween = false;

	this.initialize = function(options)
	{
		this.parent("div", options);
		this.tabs = [];
		this._tabSkins = {};
		this._freeTabs = [];

		this._animateIn = []
		this._animateOut = [];

		this.tweenCompleteHandler = this.tweenCompleteHandler.bind(this);
	};

	//----------------------------------------------------------------------
	// UIComponent overrides
	this.commitProperties = function()
	{
		this.parent();
		if (this.dataProviderChanged)
		{
			this.buildTabBar();
			this.dataProviderChanged = false;
		}

		if (this.tabSkinChanged)
		{
			var i = this.tabs.length;
			while(i--)
			{
				var tab = this.tabs[i];
				tab.buttonSkins(this._tabSkins);
			}
			this.tabSkinChanged = false;
		}

		if (this.labelStylesChanged)
		{
			this.applyLabelStyles();
			this.labelStylesChanged = false;
		}

		if (this.selectedIndexChanged)
		{
			if (!this.inTween)
			{
				if (this._selectedIndex != -1)
					this.setSelectedItem(this._dataProvider[this._selectedIndex]);
				this.selectedIndexChanged = false;
			}
		}
	};
    
	this.updateDisplayList = function(width, height)
	{
		this.parent(width, height);
		var posX = 0;
		var len = this.tabs.length;
		for (var i = 0; i < len; i++)
		{
			var tab = this.tabs[i];
			if (!tab)
				continue;
			var _w = this._tabWidth ? this._tabWidth : tab.getLabelNode().width();
			tab.setActualSize(_w, this._tabHeight);
			tab.move(posX, 0);
			posX += _w + this._horizontalGap;
		}
	};

	//----------------------------------------------------------------------
	// Helper functions

	this.applyLabelStyles = function()
	{
		if (!this.tabs)
			return;
		var i = this.tabs.length;
		while(i--)
		{
			var tab = this.tabs[i];
			if (!tab)
				continue;
			tab.labelStyles(this._labelStyles);
		}
	}

	/**
     * @private
     */
	this.buildTabBar = function()
	{
		if (!this._dataProvider)
			return;
		// Re-use the old tabs if any exist.
		var len = this._dataProvider.length;
		//this.tabs = [];
		for (var i = 0; i < len; i++)
		{
			var item = this._dataProvider[i];
			if (!item) continue;
			var tab = this.getFreeTab();
			tab.data = new ListData(item, UIDUtil.createUID(), this, i, 0);
			tab.labelText(item[this._labelField]);
			tab.labelStyles(this._labelStyles);
			this.addChild(tab);
			this.tabs.push(tab);
			if (this._animate)
				this._animateIn.push(tab);
		}
		if (this._animateIn.length)
			this.invalidateAnimationQueue();
	};

	/**
     * @private
     */
	this.getFreeTab = function()
	{
		var opacity = this._animate ? 0 : 1;
		var tab = this._freeTabs.pop();
		if (!tab)
			tab = new UIButton('div');
		tab.addEventListener("buttonDown", this.tab_buttonDownHandler, false, 0, this);
		tab.buttonSkins(this._tabSkins);
		tab.setStyles({
			"cursor":"pointer",
			"opacity":opacity
		});
		tab.selected(false);
	
		return tab;
	};

	/**
     * @private
     */
	this.removeAllTabs = function()
	{
		var len = this.tabs.length;
		for(var i = 0; i < len; i++)
			this.removeTabAt(i, this._animate);
		this._selectedIndex = -1;
	};

	/**
     * @private
     */
	this.storeTab = function(tab)
	{
		tab.removeEventListener("buttonDown", this.tab_buttonDownHandler, false);
		tab.get("tween").cancel();
		if (this.contains(tab))
			this.removeChild(tab);
		this._freeTabs.push(tab);
		var i = this.tabs.length;
		while(i--)
		{
			if (this.tabs[i] == tab)
			{
				this.tabs.splice(i, 1);
				break;
			}
		}
		// Update rowIndicies
		i = this.tabs.length;
		while(i--)
		{
			tab = this.tabs[i];
			if (tab && tab.data)
				tab.data.rowIndex = i;
		}
	};

	/**
     * Gets / sets the tab width.
     * 
     * @parma value Number indicating the new tab width.
     */
	this.tabWidth = function(value)
	{
		if (arguments.length && this._tabWidth != value)
		{
			this._tabWidth = value;

			this.invalidateDisplayList();
		}
		return this._tabWidth;
	};

	/**
     * Gets / sets the tab height.
     *
     * @parma value Number indicating the new tab height.
     */
	this.tabHeight = function(value)
	{
		if (arguments.length && this._tabHeight != value)
		{
			this._tabHeight = value;

			this.invalidateDisplayList();
		}
		return this._tabHeight;
	};

	/**
     * Gets / sets the horizontal gap between tabs.
     *
     * @parma value Number indicating the new horizontal gap.
     */
	this.horizontalGap = function(value)
	{
		if (arguments.length && value != this._horizontalGap)
		{
			this._horizontalGap = value;

			this.invalidateDisplayList();
		}
		return this._horizontalGap;
	};

	/**
     * Gets/Sets the skins for each tab state.
     *
     * @param value Object containing the key=>value pairs of the skins to set.
     * @return Object containing the values for each skin state.
     */
	this.tabSkins = function(value)
	{
		if (arguments.length && this._tabSkins != value)
		{
			this._tabSkins = value;
			this.tabSkinChanged = true;

			this.invalidateProperties();
			this.invalidateDisplayList();
		}
		return this.tabSkins;
	};

	/**
     * Sets the dataProvider for the tabBar control.
     * The dataProvider typically contains Objects that contain
     * data including the text label for each tab.
     * 
     * @param value array of Objects used to create each tab.
     */
	this.dataProvider = function(value)
	{
		if (arguments.length)
		{
			if (this.inTween && this._activeTweenList == this._animateIn)
			{
				var i = this._animateIn.length;
				while(i--)
					this.storeTab(this._animateIn[i]);

				this._animateIn = [];
				this._activeTweenList = null;
				this.inTween = false;
			}
			this.removeAllTabs();

			this._dataProvider = value;
			this.dataProviderChanged = true;
			
			if (this.hasEventListener("dataChanged"))
				this.dispatchEvent(new UICEvent("dataChanged", false, false, value));

			this.invalidateProperties();
			this.invalidateDisplayList();
		}
		return this._dataProvider || [];
	};

	/**
     * Gets / sets the labelField which determines the
     * property of an Object in the dataProvider
     * used to display text in each tab.
     * @param value String the property name of an object in the dataProvider used to display tab text.
     * @return String the current labelField value.
     */
	this.labelField = function(value)
	{
		if (arguments.length && this._labelField != value)
		{
			this._labelField = value;
	    
			if (this._dataProvider && this._dataProvider.length)
				this.buildTabBar();
		}
		return this._labelField;
	};

	/**
     * Adds a tab at the specified index with the specified data
     * used as the descriptor.
     *
     * @param index uint representing the location of the new tab.
     * @param data Object the data descriptor for the new tab.
     * @param animate Boolean indicating whether or not to transition the new tab in.
     */
	this.addTabAt = function(index, data, animate)
	{
		if (typeof index != "number" || index < 0)
			index = 0;
		var tab = this.getFreeTab();
		tab.data = new ListData(data, UIDUtil.createUID(), this, index, 0);
		tab.labelText(data[this._labelField]);
		this.addChild(tab);
		this.tabs.splice(index, 0, tab);
		this._dataProvider.splice(index, 0, data);
		if (animate)
		{
			this._animateIn[index] = tab;
			this.invalidateAnimationQueue();
		}
		this.invalidateDisplayList();
	};

	/**
     * Removes a tab at the given index.
     * 
     * @param index uint representing the location of the tab to remove.
     * @param animate Boolean indicating whether or not to transition the new tab in.
     */
	this.removeTabAt = function(index, animate)
	{
		var tab = this.tabs[index];
		if (!tab || !tab.data)
			return;
		if (animate)
		{
			tab.get("tween").cancel();
			this._animateOut.unshift(tab);
			this.inTween = true;
			this.addEventListener("updateComplete", this.processAnimationQueue, false, 0, this);
		}
		else
			this.storeTab(tab);
		// Remove it from the dataprovider if it exists there
		var i = this._dataProvider.length;
		while(i--)
		{
			var item = this._dataProvider[i];
			if (item == tab.data.data)
				this._dataProvider.splice(index, 1);
		}
		tab.data = null;
		this.invalidateDisplayList();
	};

	this.invalidateAnimationQueue = function()
	{
		if (this.inTween)
			return;
		this.inTween = true;
		this.addEventListener("updateComplete", this.processAnimationQueue, false, 0, this);

		this.invalidateDisplayList();
	}

	/**
     * Gets / Sets the selected index of the tabBar.
     * 
     * @param index int specifying the new selected index.
     * @returns int the current selected index of the tabBar.
     */
	this.selectedIndex = function(index /* int */ )
	{
		if (typeof index == "number" && this._selectedIndex != index)
		{
			this._selectedIndex = index;
			this.selectedIndexChanged = true;
	    
			this.invalidateProperties();
			this.invalidateDisplayList();
		}
		return this._selectedIndex;
	};

	/**
     * @private
     * Sets the selected tab based on the specified
     * dataProvider item.
     * 
     * @param item Object contained in the dataprovider that produced a tab in the tabBar.
     * @return Boolean indicating whether or not the item was successfully selected.
     */
	this.setSelectedItem = function(item /* Object */)
	{
		if (this.selectedItem)
			this.selectedItem.selected(false);
		//------------------------------------
		var index = this._dataProvider.indexOf(item);
		if (index == -1)
			return false;
		var tab = this.tabs[index];
		if (this.selectedItem == tab)
			return false;
		this.selectedItem = tab;
		this.selectedItem.selected(true);
		return true;
	};

	/**
     * @private
     * Selects a tab based on user input
     * and dispatches a "tabChanged" event with the
     * object contained in the dataProvider as the 'data' property
     * in the event dispatched.
     */
	this.tab_buttonDownHandler = function (event)
	{
		var newIndex = event.target.data.rowIndex;
		if (this._selectedIndex != newIndex)
		{
			this.selectedIndex(newIndex);
			this.dispatchEvent(new UICEvent("tabChanged", false, false, this._dataProvider[this._selectedIndex]));
		}
	};

	/**
     * @private
     */
	this.processAnimationQueue = function()
	{
		this.removeEventListener("updateComplete", this.processAnimationQueue, false);
		
		if (!this._animateOut.length && !this._animateIn.length)
		{
			this.inTween = false;
			
			this.invalidateProperties();
			this.invalidateDisplayList();
			return;
		}
		var kind = "in";
		//-----------------------------
		if (this._animateOut.length)
			kind = "out";
		this._activeTweenList = this._animateOut.length ? this._animateOut : this._animateIn;
		var tab;
		var len = this._activeTweenList.length;
		for (var i = 0; i < len; i++)
		{
			tab = this._activeTweenList[i];
			if (!tab)
				continue;
			tab.fade(kind);
		}
		if (tab && !this.animationTween)
		{
			this.animationTween = tab.get('tween');
			this.animationTween.addEvent("complete",  this.tweenCompleteHandler);
		}
	};

	/**
     * @private
     */
	this.tweenCompleteHandler = function()
	{
		if (this._activeTweenList == this._animateOut)
		{
			var i = this._activeTweenList.length;
			while(i--)
				this.storeTab( this._activeTweenList[i]);
			this._animateOut = [];
		}
		else
			this._animateIn = [];
		
		this._activeTweenList = null;
		
		this.animationTween.removeEvent("complete", this.tweenCompleteHandler);
		this.animationTween = null;
		this.addEventListener("updateComplete", this.processAnimationQueue, false, 0, this);
		
		this.invalidateProperties();
		this.invalidateDisplayList();
	};

	this.labelStyles = function(value /* Object */)
	{
		if (arguments.length)
		{
			this._labelStyles = value;

			this.invalidateProperties();
			this.invalidateDisplayList();
		}
	};
};
TabBar = new Class(new TabBar());
