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 self = this;
				function postCreateCallback(args) {
					var buzz = args.buzzObj;

					ng_data["buzz_" + config.buzzId] = buzz;
					self.renderBuzz(config.buzzId, config);
				}

				var extraArgs = ng_clone(config.extraArgs) || {};
				extraArgs.templateId = config.templateId;

				this.parentBuzz.loadSubordinateWidget(config.buzzId, targetId, extraArgs, postCreateCallback);
			}
			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;
}
