/**
 * Show / hide an element and apply this to all parents, if necessary.
 *
 * This function can be called many(!) times so prototypejs methods are avoided for performance reasons
 * @param elementId
 * @param display
 */
function showElement(elementId, display)
{
	var elem = $(elementId),
		isTabContainer;

	if (!elem) return;

	// Stop when one of the elements in the tree is already visible / hidden
	if ((elem.style.display == 'none') != display) {
		return;
	}

	// show / hide the tab that accompanies the tab content
	isTabContainer = /^div$/i.test(elem.nodeName) && /(^|\s)tabcontent(\s|$)/i.test(elem.className);
	if (isTabContainer) {
		// Find the tab by converting the tab content id to the tab id and show / hide it
		showTab(elem.id.replace(/^tab_(.*?)_content(_FormTab.*?)$/, 'tablinks_$1$2'), display);
	}

	// tabcontainers shouldnt be positioned because of the way SPITabs shows/hides tabcontent
	if (display) {
		if (!isTabContainer) {
			elem.style.position = '';
		}
		elem.style.visibility = '';
		elem.style.display = '';
	} else {
		if (!isTabContainer) {
			elem.style.position = 'absolute';
		}
		elem.style.visibility = 'hidden';
		elem.style.display = 'none';
	}

	// Find the next relevant parent
	var parent = elem.parentNode;
	while (parent && !isFormNode(parent)) {
		parent = parent.parentNode;
	}

	if (parent) {
		if (!display) {
			// Only hide the parent if relevant siblings of the current element are hidden
			var hideParent = true;

			// Check if any sibling is visible
			for (var i = 0, l = elem.parentNode.childNodes.length; i < l; i++) {
				var sibling = elem.parentNode.childNodes[i];

				if (sibling.nodeType != 1 || (sibling.style && sibling.style.display == 'none')) {
					continue;
				}

				if (isFormNode(sibling)) {
					hideParent = false;
					break;
				}
			}

			if (!hideParent) {
				return;
			}
		}

		// Recurse up the DOM-tree
		showElement(parent, display);
	}
}

/**
 * Check whether the given node is a form node (i.e. relevant to showElement)
 * @param node
 */
function isFormNode(node)
{
	// Make sure the required regular expression object is available.
	if (!_isFormNodeRegExp) {
		// List of elements that represent form nodes
		formNodeElements = {
			// TAG		// CLASSES
			fieldset: 	[],
			table:		['form-column-container'],
			td:			['form-column'],
			div:		['tabcontent', 'formpanel', 'field', 'formrow'],
			span:		['formpanelelement'],
		};

		// Build regular expression
		regexp = [];
		for (tag in formNodeElements) {
			var classes = formNodeElements[tag];
			pattern = '^' + tag + ':';
			if (classes.length) {
				pattern += '.*\\s(' + classes.join('|') + ')(\\s|$)';
			}
			regexp.push(pattern);
		}
		regexp = regexp.join('|');
		_isFormNodeRegExp = new RegExp(regexp, 'i');
		_isFormNodeRegExp.compile(_isFormNodeRegExp);
	}

	// Test the tag/class of the node to see if it is a form node
	return _isFormNodeRegExp.test(node.nodeName + ': ' + node.className);
}
var _isFormNodeRegExp = false;

function showTab(elementId, display) {
	var elem = $(elementId),
		siblings;

	if (!elem) return;

	// Don't change the display value when it isn't necessary because it will cause a redraw in Internet Explorer
	if ((elem.style.display == 'none') != display) {
		return;
	}

	if (display) {
		elem.style.position = '';
		elem.style.visibility = '';
		elem.style.display = '';
	} else {
		elem.style.position = 'absolute';
		elem.style.visibility = 'hidden';
		elem.style.display = 'none';

		if (elem.hasClassName('active')) {
			if (siblings = elem.nextSiblings()) {
				siblings[0].down('a').onclick();
			} else if (siblings = elem.previousSiblings()) {
				siblings[siblings.length-1].down('a').onclick();
			}
		}
	}
}

function requiredField(fieldName, display) {

	var child = $(fieldName).up('div.field');

	if (display) {
		child.addClassName('req');
		if(!child.down('label span.req')) {
			child.down('label').insert(  new Element('span', { 'class': 'req' }).update('*') );
		}
	} else {
		child.removeClassName('req');
		if(child.down('label span.req')) {
			child.down('label span.req').remove();
		}
	}
}

function count(values) {
	if (Object.isArray(values)) {
		return values.length;
	}
	if ($H(values)) {
		return $H(values).values().length;
	}
	return 1;
}

function in_array(needle, values) {
	if (Object.isArray(values)) {
		return values.indexOf(needle.toString()) > -1;
	}
	return false;
}

function array_filter(array) {
	var array2 = {};
	$H(array).each(function(pair) {
		if (pair.value == 1) {
			array2[pair.key] = 1;
		}
	});
	return array2;
}

function valueFromArray(array, key) {
	var hash = $H(array);
	if (hash) {
		return hash.get(key);
	}
	return null;
}

function array_key_exists(key, array) {
	return !Object.isUndefined($H(array).get(key));
}

function substr(string, start, length)
{
	return string.substring(start, length);
}

// Module specific
var Relation__getRelationParentsCache = {};
function Relation__getRelationParents(relationId, onlyDirect, includeRelationId)
{
	onlyDirect = Object.isUndefined(onlyDirect) ? true : onlyDirect;
	includeRelationId = Object.isUndefined(onlyDirect) ? false : includeRelationId;

	if (!Object.isArray(relationId)) {
		relationId = [relationId];
	}
	if (relationId.length <= 0) {
		return [];
	}

	var key = Object.inspect(relationId) + '_' + (onlyDirect ? 1 : 0) + '_' + (includeRelationId ? 1 : 0);
	if (Object.isUndefined(Relation__getRelationParentsCache[key])) {
		var RPCClient = new PBJSONRPCClient(PbLib.getNewURI('l/relation/rpc/relation'));
		Relation__getRelationParentsCache[key] = RPCClient.call('getRelationParents', relationId, onlyDirect, includeRelationId);
	}

	return Relation__getRelationParentsCache[key];
}
