var NGTab_Class = function()
{
    //private properties
	var _this = this;
	var ng_data = {};
    var selectedTabObj = null;

	//initialization specified properties
	this.contentId = null;
	this.tabConfigs = [];
	this.parentBuzz = null;

	//additional config properties
	this.cacheData = true;
	this.tabClass = "tab";
	this.tabClassActive = "tab active";
	this.templateLoadingId = null;  //What to show while loading a tab. Can be a DOM node ID, a javascript global variable name, or a literal string
	this.firstTabIndexSelected = 0;
	
	//controls how content panels are display
	//"single" - there is one panel, the content is cleared & replaced to show a new tab. Causes view events to be fired on every tab click
	//"multi" - a container div is created for each tab. Each tab is rendered once, thereafter it's shown & hidden. View is fired only on first tab click
	this.panelType = "single";
	
	//autorotation settings
	//Doing an aliased variable so it's a little easier to reference later and so it minifies better.
	var autoRotation = this.autoRotation = {
		enabled : false, //do autorotation?
		rotateDelay : 5000, //milliseconds between changing tabs
		pauseDelay : 60000 //milliseconds to pause for. negative value indicates to just stop rotation instead of pausing
	};
	
	//EVENT HOOKS============
	var events = {};
	/* available events: 
		preInitializeEvents
		postInitializeEvents
		preWireUpTabsEvents
		postWireUpTabsEvents
		preRenderBuzzEvents
		postRenderBuzzEvents
		postDeselectAllTabsEvents
		postHighlightTabEvents
		displayLoadingFailedEvents
		loadingTemplateNotSpecifiedEvents
		noConfigFoundEvents
		tabClickedEvents
	*/
	//END EVENT HOOKS========


	this.initialize = function(contentId, tabConfigArray, parentBuzzObj)
	{
		this.contentId = contentId;
		this.tabConfigs = tabConfigArray;
		this.parentBuzz = parentBuzzObj;

		executeEvents("preInitializeEvents");

		wireUpTabs(this);
		selectGivenTabByIndex(this, this.firstTabIndexSelected);

		if(autoRotation.enabled){
			this.startAutoRotation();
		}

		executeEvents("postInitializeEvents");
	}

	var autoRotateTimerHandle = null;
	var autoRotatePauseHandle = null;
	var autoRotateEventsBound = false;
	this.startAutoRotation = function(){
		if(autoRotateTimerHandle == null){
			autoRotateTimerHandle = setInterval(rotateToNextTab, autoRotation.rotateDelay);
			
			if(!autoRotateEventsBound){
				this.addEvent("tabClickedEvents", function(){
					_this.pauseAutoRotation();
				});
				autoRotateEventsBound = true;
			}
		}
	}
	
	var rotateToNextTab = function(){
		var selectedIdx = _this.getSelectedTabIndex();
		selectedIdx++;
		
		if(selectedIdx >= _this.tabConfigs.length){
			selectedIdx = 0;
		}
		
		selectGivenTabByIndex(_this, selectedIdx);
	}
	
	this.stopAutoRotation = function(){
		if(autoRotateTimerHandle != null){
			clearInterval(autoRotateTimerHandle);
			autoRotateTimerHandle = null;
		}
		
		if(autoRotatePauseHandle != null){
			clearTimeout(autoRotatePauseHandle);
			autoRotatePauseHandle = null;
		}
	}
	
	this.pauseAutoRotation = function(){
		this.stopAutoRotation();
		
		if(autoRotation.pauseDelay > 0){
			autoRotatePauseHandle = setTimeout(
				function(){
					rotateToNextTab();
					_this.startAutoRotation();
				}, 
				autoRotation.pauseDelay
			);
		}
	}

	var wireUpTabs = function(_this)
	{
		executeEvents("preWireUpTabsEvents");

		for (x = 0; x < _this.tabConfigs.length; x++)
		{
			var tab = document.getElementById(_this.tabConfigs[x].tabId);
			if (tab == null) { continue; }

			addOnClickEvent(tab, tabClicked, _this.tabConfigs[x].prependClickEvent);
		}

		executeEvents("postWireUpTabsEvents");
	}
	
	function tabClicked(evt){
		executeEvents("tabClickedEvents");
		_this.changeBuzzEvent(evt);
	}

	function selectGivenTabByIndex(_this, index)
	{
		var config = _this.getConfigByIndex(index);
		if (config == null) { return; }

		tabObj = document.getElementById(config.tabId);
		if (tabObj == null) { return; }

		_this.changeBuzz(tabObj)
	}

	var highlightTabObject = function(_this, tabObj)
	{
		if (tabObj == null) { return; }

		deselectAllTabs(_this);
		tabObj.className = _this.tabClassActive;
		selectedTabObj = tabObj;
		
	    executeEvents("postHighlightTabEvents", [tabObj]);
	}
	
	var deselectAllTabs = function(_this)
	{
		for (x = 0; x < _this.tabConfigs.length; x++)
		{
			var tab = document.getElementById(_this.tabConfigs[x].tabId);
			if (tab == null) { continue; }

			tab.className = _this.tabClass;
		}
		
		executeEvents("postDeselectAllTabsEvents");
	}
	
	this.getSelectedTabObject = function()
	{
		selectedTabObj = selectedTabObj || getItem(this.tabConfigs, function(config){
			var tabObj = document.getElementById(config.tabId);
			return (tabObj && tabObj.className == _this.tabClassActive);
		});
		
		return selectedTabObj;
	}
	
	this.getSelectedTabIndex = function(){
		var selected = this.getSelectedTabObject();
		
		for(var i = 0; i < this.tabConfigs.length; i++){
			if(this.tabConfigs[i].tabId == selected.id){
				return i;
			}
		}
		return 0;
	}

	this.getConfigByBuzzId = function(buzzId)
	{
		return getItem(this.tabConfigs, function(config){
			return (config.buzzId == buzzId);
		});
	}

	this.getConfigByIndex = function(index) {
		return this.tabConfigs[index] || null;
	}

	//NOTE: this will return the first one that it finds 
	//it's possible to have the same tabId declared more than once
	this.getConfigByTabId = function(tabId)
	{
		return getItem(this.tabConfigs, function(config){
			return (config.tabId == tabId);
		});
	}
	
	function getItem(arr, filterFunc){
		for(var i = 0; i < arr.length; i++){
			var item = arr[i];
			if(filterFunc(item)){
				return item;
			}
		}
		return null;
	}

	this.changeBuzzEvent = function(evt)
	{
	    evt = evt || window.event;
	    var obj = evt.target || evt.srcElement;
		this.changeBuzz(obj);
	}

	this.changeBuzz = function(sourceObj)
	{
		var config = this.getConfigByTabId(sourceObj.id);

		if (config == null)
		{
			executeEvents("noConfigFoundEvents");
			return;
		}

		highlightTabObject(this, sourceObj);
		
		if(config.renderBuzz){
			this.displayLoading();

			var dataLoaded = (this.cacheData ? this.buzzIsLoaded(config.buzzId) : false);
			
			if (!dataLoaded)
			{
				var targetId = this.contentId;
				
				if(this.panelType == "multi"){
					targetId = config.panelId = "tabPanel_" + Math.random().toString().substring(2);

					var panel = document.createElement("DIV");
					panel.id = config.panelId;
					panel.className = "ng_tabPanel";
					//panel.style.display = "none";
					panel.innerHTML = getLoadingMessage();
					
					document.getElementById(this.contentId).appendChild(panel);
				}
				
				//load buzz object
				var buzz = this.parentBuzz.createSlaveWidget(config.buzzId, {
					templateId : config.templateId,
					targetId : targetId, 
					extraArgs : config.extraArgs
				});
				
				ng_data["buzz_" + config.buzzId] = buzz;
			
				this.renderBuzz(config.buzzId, config);
			}
			else if(this.panelType == "single")
			{
				//use cached data
				this.renderBuzz(config.buzzId, config);
			}
			
			if(this.panelType == "multi"){
				var contentContainer = document.getElementById(this.contentId);
				for(var i = 0; i < contentContainer.childNodes.length; i++){
					var node = contentContainer.childNodes[i];
					if(node.style){
						node.style.display = "none";
					}
				}
				document.getElementById(config.panelId).style.display = "block";
			}
				
		} else {
			//We're not actually rendering the tab content, so just call the events
			executeEvents("preRenderBuzzEvents", [null, config]);
			executeEvents("postRenderBuzzEvents", [null, config]);
		}
	}

	this.renderBuzz = function(buzzId, config)
	{
		if (!this.buzzIsLoaded(buzzId)) { return; }

		var buzzObj = ng_data["buzz_" + buzzId];
		
		//pre render buzz event
		executeEvents("preRenderBuzzEvents", [buzzObj, config]);

		//use the built in renderer
		buzzObj.render();

		//post render buzz event
		executeEvents("postRenderBuzzEvents", [buzzObj, config]);
	}

	this.setContents = function(value)
	{
		var contents = document.getElementById(this.contentId);

		if (contents != null)
		{
			contents.innerHTML = value;
		}
	}

	this.displayLoading = function()
	{
		if(this.panelType == "multi"){
			return;
		}
		
		var loadingMessage = getLoadingMessage();
		
		if (loadingMessage == null) 
		{
			executeEvents("loadingTemplateNotSpecifiedEvents");
			return; 
		}

		var contents = document.getElementById(this.contentId);
		if (contents != null)
		{
			//clear it out
			this.setContents("");
            contents.innerHTML = loadingMessage;
		}
		else
		{
			executeEvents("displayLoadingFailedEvents");
		}
	}
	
	function getLoadingMessage(){
		if (!_this.templateLoadingId) {
			return null;
		}
	
		//NOTE: loading template does not get parsed by trimpath
		var templateElement = document.getElementById(_this.templateLoadingId);
		var template = (templateElement ? templateElement.value || templateElement.innerHTML : null) || 
						(typeof window[_this.templateLoadingId] != "undefined" ? window[_this.templateLoadingId] : null) || 
						_this.templateLoadingId;
		return template;
	}

	this.buzzIsLoaded = function(buzzId)
	{
		if (ng_data["buzz_" + buzzId] == undefined || ng_data["buzz_" + buzzId] == null) return false;
		return true;
	}

	this.removeEvents = function(eventName)
	{
		events[eventName] = null;
	}

	this.addEvent = function(eventName, action)
	{
		events[eventName] = events[eventName] || [];
		events[eventName].push(action);
	}

	//internal method
	var executeEvents = function(eventName, arrEventArgs)
	{
		if (events[eventName])
		{
			var arrEvents = events[eventName];
			for(var i = 0; i < arrEvents.length; i++){
				try{
					var func = arrEvents[i];
					func.apply(_this, arrEventArgs || []);
				} catch(e){
					debug("Error executing event " + eventName, e);
				}
			}
		}
	}
	
	var addOnClickEvent = function(obj, action, prepend)
	{
		var oldEvent = obj.onclick;
		if (typeof oldEvent != "function")
		{
			obj.onclick = function(evt) { action(evt); }
		}
		else
		{
			//see if we should add it before or after existing onclick events
			if (prepend == true)
			{
				obj.onclick = function(evt)
				{
					action(evt);
					oldEvent(evt);
				}
			}
			else
			{
				obj.onclick = function(evt)
				{
					oldEvent(evt);
					action(evt);
				}
			}
		}		
	}
	
	var debug = function(){
		if(ng_debug){
			return ng_debug;
		} else if(typeof console != "undefined"){
			return console.debug || console.log || console.warn || function(){};
		}
		return function(){};
	}();
}

var NGTabConfig_Class = function(buzzId, tabId, templateId, extraArgs)
{
	//Public constructor properties
	this.templateId = templateId;
	this.tabId = tabId;
	this.buzzId = buzzId;

	//Optional public properties
	this.prependClickEvent = false;
	
	//Optional property to not render a buzz (allows framework to be used as a generic tab framework)
	this.renderBuzz = true;
	
	//optional extra args to be passed into the child widget
	this.extraArgs = extraArgs;
}