(function($)
{
	$.fn.udwtabs = {
		// stores tabSystems, tabs and current tab settings
		buildTests: false,
		arr_availableTabSystems: new Array(),
		str_widestTabClass: null
	};

	$(document).ready(function()
	{
		var sel = '.tabSystem';
		var currSel = sel,
			jRegs;
		while (true)
		{
			// get all tabSystems recursive beginning with the inner ones
			jRegs = $(currSel).not(currSel + ' ' + sel);
			if (jRegs.size() == 0)
			{
				break;
			}

			// tab click listener for the super container
			jRegs.click(function(e)
			{
				var jTarget = $(e.target);
				while (!jTarget.hasClass('tabSystem'))
				{
					if (jTarget.hasClass('tab'))
					{
						$.fn.udwtabs_setTabFocussed(jTarget.attr('id'));
						e.stopPropagation();
						break;
					}
					jTarget = jTarget.parent();
				}
			});

			var int_regIdIndex = 0;
			jRegs.each(function()
			{
				var jReg = $(this);

				// create id where needed
				if (jReg.attr('id') == '')
				{
					while($('#tabSystem' + int_regIdIndex).size() != 0)
					{
						int_regIdIndex++;
					}
					jReg.attr('id', 'tabSystem' + int_regIdIndex);
				}

				var regId = jReg.attr('id');
				var newReg = {
					tabSystemId: regId,
					availableTabs: new Array()
				};

				// process all direct child tabs and panes
				var jTabsAndPanes = jReg.find('.tab, .pane').not(currSel + ' ' + sel + ' *');
				var jTabs = jTabsAndPanes.filter('.tab');
				var jPanes = jTabsAndPanes.filter('.pane');

				// loop only for pairs of panes and tabs
				var count = Math.min(jTabs.size(), jPanes.size()),
					// indicates the position of each new tab in pixels from left
					int_pixelPositionFromLeft = 0,
					jCurrTab, jCurrPane, arr_newTab, pos;
				for(var i = 0; i < count; i++)
				{
					jCurrTab = $(jTabs.get(i));
					jCurrPane = $(jPanes.get(i));

					// catch invalid configuration
					if (
						(jCurrTab.hasClass('tabFocussed') && jCurrTab.hasClass('tabInactive'))
						|| (jCurrTab.hasClass('tabActive') && jCurrTab.hasClass('tabInactive'))
						|| (jCurrTab.hasClass('tabFocussed') && jCurrTab.hasClass('tabUnfocussed')))
					{
						continue;
					}

					// create ids where needed
					if (jCurrTab.attr('id') == '')
					{
						jCurrTab.attr('id', regId + '_tab' + i);
					}
					if (jCurrPane.attr('id') == '')
					{
						jCurrPane.attr('id', regId + '_pane' + i);
					}

					// store new tab
					arr_newTab = {
						tabId: jCurrTab.attr('id'),
						paneId: jCurrPane.attr('id'),
						width: null,
						marginLeft: parseInt(jCurrTab.css('margin-left')),
						marginRight: parseInt(jCurrTab.css('margin-right')),
						posLeft: int_pixelPositionFromLeft,
						isAvailable: !jCurrTab.hasClass('tabUnavailable'),
						isActive: jCurrTab.hasClass('tabActive') || !jCurrTab.hasClass('tabInactive'),
						isFocussed: jCurrTab.hasClass('tabFocussed')
					}
					jCurrTab.css({
						'margin-left': '0',
						'margin-right': '0'
					});

					$.fn.udwtabs_updateTabWidth(arr_newTab);

					$.fn.udwtabs_updateTabPosition(arr_newTab);

					if (arr_newTab['isAvailable'])
					{
						int_pixelPositionFromLeft += arr_newTab['width'] + arr_newTab['marginLeft'];
					}

					newReg['availableTabs'].push(arr_newTab);
				};

				$.fn.udwtabs['arr_availableTabSystems'].push(newReg);
    		});
			currSel += ' ' + sel;
		}

		// update tab appearance and finally activate tabs
		var arr_regs = $.fn.udwtabs['arr_availableTabSystems'],
			currTabs;
		for (var i in arr_regs)
		{
			currTabs = arr_regs[i]['availableTabs'];
			for (var j in currTabs)
			{
				$.fn.udwtabs_updateTabAppearance(currTabs[j]);
				$('#' + currTabs[j]['tabId']).removeClass('tabInit');
				$('#' + currTabs[j]['paneId']).removeClass('paneInit').css('backgroundImage', 'none');
			}
		}

		// TEST_START
		if ($.fn.udwtabs['buildTests'])
		{
			var currReg, jCurrTab, jCurrReg, arr_tests = ['Hide', 'Show', 'Activate', 'Deactivate', 'Focus', 'LongLabel', 'ShortLabel'];
			for (var i in $.fn.udwtabs['arr_availableTabSystems'])
			{
				currReg = $.fn.udwtabs['arr_availableTabSystems'][i];

				jCurrReg = $('#' + currReg['tabSystemId']);

				for (var j in arr_tests)
				{
					jCurrReg.before($('<div style="text-align:left" id="' + jCurrReg.attr('id') + '_' + arr_tests[j] + '"><div style="float:left;font-weight:bold;width:100px">' + arr_tests[j] + ':</div></div>'));
				}

				for (var j in currReg['availableTabs'])
				{
					jCurrTab = $('#' + currReg['availableTabs'][j]['tabId']);

					hideButton = $('<input class="' + currReg['availableTabs'][j]['tabId'] + '" type="button" value="' + jCurrTab.html() + '">').click(function()
					{
						$.fn.udwtabs_setTabAvailable($(this).attr('class'), false);
					});
					showButton = $('<input class="' + currReg['availableTabs'][j]['tabId'] + '" type="button" value="' + jCurrTab.html() + '">').click(function()
					{
						$.fn.udwtabs_setTabAvailable($(this).attr('class'), true);
					});
					activateButton = $('<input class="' + currReg['availableTabs'][j]['tabId'] + '" type="button" value="' + jCurrTab.html() + '">').click(function()
					{
						$.fn.udwtabs_setTabActive($(this).attr('class'), true);
					});
					deactivateButton = $('<input class="' + currReg['availableTabs'][j]['tabId'] + '" type="button" value="' + jCurrTab.html() + '">').click(function()
					{
						$.fn.udwtabs_setTabActive($(this).attr('class'), false);
					});
					focusButton = $('<input class="' + currReg['availableTabs'][j]['tabId'] + '" type="button" value="' + jCurrTab.html() + '">').click(function()
					{
						$.fn.udwtabs_setTabFocussed($(this).attr('class'));
					});
					longLabelButton = $('<input class="' + currReg['availableTabs'][j]['tabId'] + '" type="button" value="' + jCurrTab.html() + '">').click(function()
					{
						$.fn.udwtabs_setTabLabel($(this).attr('class'), 'Hallo Welt');
					});
					shortLabelButton = $('<input class="' + currReg['availableTabs'][j]['tabId'] + '" type="button" value="' + jCurrTab.html() + '">').click(function()
					{
						$.fn.udwtabs_setTabLabel($(this).attr('class'), 'x');
					});
					$('#' + jCurrReg.attr('id') + '_Hide').append(hideButton)
					$('#' + jCurrReg.attr('id') + '_Show').append(showButton);
					$('#' + jCurrReg.attr('id') + '_Activate').append(activateButton);
					$('#' + jCurrReg.attr('id') + '_Deactivate').append(deactivateButton);
					$('#' + jCurrReg.attr('id') + '_Focus').append(focusButton);
					$('#' + jCurrReg.attr('id') + '_LongLabel').append(longLabelButton);
					$('#' + jCurrReg.attr('id') + '_ShortLabel').append(shortLabelButton);
				}
			}
		}
		// TEST_END
	});

	// public: sets a given jQuery tab object (and the belonging pane) focussed and all others unfocussed
	$.fn.udwtabs_setTabFocussed = function(str_tabId)
	{
		// try to find a given tabSystem
		var arr_reg = $.fn.udwtabs_getTabSystemByTabId(str_tabId);
		if (!arr_reg)
		{
			// Fehler
			return false;
		}
		arr_tabs = arr_reg['availableTabs'];

		// look up given tabSystem
		for (var i in arr_tabs)
		{
			if (arr_tabs[i]['tabId'] == str_tabId)
			{
				var arr_tab = arr_tabs[i];
				break;
			}
		}
		if (typeof arr_tab == 'undefined')
		{
			// Fehler
			return false;
		}

		// does the current state of the tab allow it to be focussed?
		if (!arr_tab['isFocussed'] // 1) it is not yet focussed
				&& arr_tab['isAvailable'] // 2) it is available (meaning 'visible and not hidden'!)
				&& arr_tab['isActive']) // 3) it is active (meaning 'not disabled')
		{
			var currTab, isChosenTab;
			for (var i in arr_tabs)
			{
				currTab = arr_tabs[i];

				// focus the given tab and unfocus all others!!!
				isChosenTab = currTab['tabId'] == str_tabId;
				if (currTab['isFocussed'] || isChosenTab)
				{
					currTab['isFocussed'] = isChosenTab;
					$.fn.udwtabs_updateTabAppearance(currTab);
				}
			}
		}
		return true;
	};

	// public: sets a given tab unfocussed as long as another one can be focussed instead
	$.fn.udwtabs_setTabUnfocussed = function(str_tabId)
	{
		var arr_reg = $.fn.udwtabs_getTabSystemByTabId(str_tabId);
		if (!arr_reg)
		{
			// Fehler
			return false;
		}

		var str_tabToFocus, currTab, isGivenTabPassed = false;
		for (var i in arr_reg['availableTabs'])
		{
			// if possible choose the one tab directly following the disabled one.
			// otherwise the one directly before it
			currTab = arr_reg['availableTabs'][i];
			if (currTab['tabId'] != str_tabId)
			{
				if (currTab['isAvailable'] && currTab['isActive'])
				{
					str_tabToFocus = currTab['tabId'];
				}
				if (isGivenTabPassed && (typeof str_tabToFocus != 'undefined'))
				{
					break;
				}
			}
			else
			{
				isGivenTabPassed = true;
			}
		}

		// focus other tab
		if (typeof str_tabToFocus != 'undefined')
		{
			$.fn.udwtabs_setTabFocussed(str_tabToFocus);
			return true;
		}
		// there is no other tab to focus!!! Therefore the given tab CAN NOT be hidden!
		return false;
	};

	// public: sets a given tab (not the belonging pane!) available (meaning 'visible, not hidden'!)
	$.fn.udwtabs_setTabAvailable = function(str_tabId, bool_isAvailable)
	{
		// try to find a given tabSystem
		var arr_reg = $.fn.udwtabs_getTabSystemByTabId(str_tabId);
		if (!arr_reg)
		{
			// Fehler
			return false;
		}

		var arr_currTab;
		for (var i in arr_reg['availableTabs'])
		{
			arr_currTab = arr_reg['availableTabs'][i];

			// clear focussed tabs before deactivation
			if ((arr_currTab['tabId'] == str_tabId) && (arr_currTab['isAvailable'] != bool_isAvailable))
			{
				// set unavailable!
				if (!bool_isAvailable && arr_currTab['isFocussed'])
				{
					if (!$.fn.udwtabs_setTabUnfocussed(str_tabId))
					{
						return false;
					}
				}

				var offset = (arr_currTab['width'] + arr_currTab['marginLeft']) * (bool_isAvailable ? 1 : -1);
				arr_currTab['isAvailable'] = bool_isAvailable;
				$.fn.udwtabs_updateTabAppearance(arr_currTab);
			}
			// in case the offset changed, all following tabs must be moved
			else if (typeof offset != 'undefined')
			{
				arr_currTab['posLeft'] += offset;
				$.fn.udwtabs_updateTabPosition(arr_currTab);
			}
		}
		return true;
	};

	// public: sets a given tab (and the belonging pane!) active, while it's still visible
	$.fn.udwtabs_setTabActive = function(str_tabId, bool_isActive)
	{
		// try to find a given tabSystem
		var arr_reg = $.fn.udwtabs_getTabSystemByTabId(str_tabId);
		if (!arr_reg)
		{
			// Fehler
			return false;
		}

		var arr_currTab;
		for (var i in arr_reg['availableTabs'])
		{
			arr_currTab = arr_reg['availableTabs'][i];

			if (arr_currTab['tabId'] == str_tabId)
			{
				if (arr_currTab['isActive'] != bool_isActive)
				{
					// clear focussed tabs before deactivation
					if (!bool_isActive && arr_currTab['isFocussed'])
					{
						if (!$.fn.udwtabs_setTabUnfocussed(str_tabId))
						{
							return false;
						}
					}

					arr_currTab['isActive'] = bool_isActive;
					$.fn.udwtabs_updateTabAppearance(arr_currTab);
				}
				return;
			}
		}
	};

	// public: set new label for given tab
	$.fn.udwtabs_setTabLabel = function(str_tabId, str_newLabel)
	{
		var jTab = $('#' + str_tabId);
		if (jTab.text() == str_newLabel)
		{
			return;
		}

		// try to find a given tabSystem
		var arr_reg = $.fn.udwtabs_getTabSystemByTabId(str_tabId);
		if (!arr_reg)
		{
			// Fehler
			return false;
		}

		var currTab;
		for (var i in arr_reg['availableTabs'])
		{
			currTab = arr_reg['availableTabs'][i];

			// rename tab
			if (currTab['tabId'] == str_tabId)
			{
				var oldWidth = currTab['width'];

				jTab.text(str_newLabel);
				$.fn.udwtabs_updateTabWidth(currTab);

				if (!currTab['isAvailable'])
				{
					break;
				}
				var offset = currTab['width'] - oldWidth;
			}
			// in case the offset changed, all following tabs must be moved
			else if (typeof offset != 'undefined')
			{
				currTab['posLeft'] += offset;
				$.fn.udwtabs_updateTabPosition(currTab);
			}
		}
	};

	// private: updates all visible effects after the stored properties have been changed
	$.fn.udwtabs_updateTabAppearance = function(arr_tab)
	{
		var isFocussed = arr_tab['isFocussed'];
		var isActive = arr_tab['isActive'];

		$('#' + arr_tab['tabId'])
			.toggleClass('tabFocussed', isFocussed)
			.toggleClass('tabUnfocussed', !isFocussed)
			.toggleClass('tabActive', isActive)
			.toggleClass('tabInactive', !isActive)
			.toggle(arr_tab['isAvailable']);
		$('#' + arr_tab['paneId']).toggle(isFocussed);
	};

	// private: updates the position of the given tab (in pixels from left side)
	$.fn.udwtabs_updateTabPosition = function(arr_tab)
	{
		$('#' + arr_tab['tabId']).css('left', arr_tab['posLeft'] + arr_tab['marginLeft'] + 'px');
	};

	// private: updates the width of the given tab
	$.fn.udwtabs_updateTabWidth = function(arr_tab)
	{
		// unset fixed width and get JTab
		var jTab = $('#' + arr_tab['tabId']).width('');
		var jInnerParts = jTab.find('*').not('*:empty'),
			orgClass = jTab.attr('class');

		// find out class with maximum width
		if ($.fn.udwtabs['str_widestTabClass'] === null)
		{
			var wMax, w1, w2, w3,
				c1 = 'tab tabActive tabFocussed',
				c2 = 'tab tabActive tabUnfocussed',
				c3 = 'tab tabInactive tabUnfocussed';

			w1 = jTab.attr('class', c1).width();
			w2 = jTab.attr('class', c2).width();
			w3 = jTab.attr('class', c3).width();

			wMax = Math.max(w1, w2, w3);
			$.fn.udwtabs['str_widestTabClass'] = (wMax == w1) ? c1 : ((wMax == w2) ? c2 : c3);
		}

		// temporary set to maximum view width
		jTab.attr('class', $.fn.udwtabs['str_widestTabClass']);

		// now set all subsequent elements to fixed maximum widths
		jInnerParts.each(function()
		{
			var sub = $(this);
			sub.width(sub.width());
		});

		// finally set fixed width for tab itself, set back to original class and finally compute total tab width
		arr_tab['width'] = jTab.width(jTab.width()).attr('class', orgClass).outerWidth() + arr_tab['marginRight'];
	};

	// private: tries to returns a stored tabSystem and all its tabs
	$.fn.udwtabs_getTabSystemByTabId = function(str_tabId)
	{
		var jCurrObj = $('#' + str_tabId);
		while (true)
		{
			jCurrObj = jCurrObj.parent();
			if (jCurrObj.size() == 0)
			{
				return false;
			}
			if (jCurrObj.hasClass('tabSystem'))
			{
				// look up given tabSystem
				var arr_regs = $.fn.udwtabs['arr_availableTabSystems'];
				for (var i in arr_regs)
				{
					if (arr_regs[i]['tabSystemId'] == jCurrObj.attr('id'))
					{
						return arr_regs[i];
					}
				}
				return false;
			}
		}
	};
})(jQuery);
