/**
 * NodeBase
 */
var NodeBase = stdClass.extend({
	// Static properties
	activeNodes: [],
	nodeContainer: null,
	mousePosition: {
		x: null,
		y: null
	},

	settings: function(settings) {
		if (typeof(this.s) == 'undefined') {
			this.base();

			this.moveOnPointerMove = false;
			Object.extendProperties(this.s, {
				useIframe: (document.all && !window.XMLHttpRequest) ? true : false,
				nodeTagName: 'div',
				iframeShrink: 0,
				nodeId: null,
				groupId: 'generic',
				groupLimit: null,
				classNames: [],
				zIndex: 100,
				visible: false,
				extant: false,
				position: {
					exemplarAnchor: 'center middle',
					selfAnchor: 'center middle',
					exemplar: 'viewport',
					offsetY: 0,
					offsetX: 0
				}
			});
			Object.extendProperties(this.s, settings);

			Object.extendProperties(this.n, {
				superNode: null,
				subNode: null,
				iframe: null
			});
		}
	},

	constructor: function(settings) {
		this.settings(settings);
	},

	watchMouse: function(e) {
		if (typeof(e) == 'undefined') {
			this.eObserve(document, 'mousemove', this.watchMouse.bind(this));
		} else {
			this.mousePosition = {
				x: Event.pointerX(e),
				y: Event.pointerY(e)
			};
			if (this.moveOnPointerMove) {
				this.moveOnPointerMove = false;
				this.position();
			}
		}
	},

	// Public methods
	hide: function() {
		Element.hide(this.n.superNode);
		this.s.visible = false;
	},

	show: function() {
		if (this.s.groupLimit !== null) {                                // Check the registry for group conflicts
			var permitToRemain = this.s.groupLimit-1;
			for (var x=0; x<this.activeNodes.length; x++) {
				if (this.activeNodes[x]._isVisible() && this.activeNodes[x].getGroupId() == this.s.groupId) {
					if (permitToRemain--<=0) this.activeNodes[x].hide();
				}
			}
		}
		if (!this.n.superNode) this.create();
		this.position();
		Element.show(this.n.superNode);
		this._sizeIframe();
		this.s.visible = true;
	},

	create: function() {
		if (this.n.subNode) this.destroy();
		if (!this.s.zIndex) this.s.zIndex = 1000 + this.activeNodes.length;
		this.n.superNode = this._createNode(this.s.nodeTagName);
		this.n.superNode.style.position = 'absolute';
		this.n.superNode.className = 'superNode';
		//this.n.superNode.style.zIndex = this.s.zIndex-1;

		this.n.subNode = document.createElement(this.s.nodeTagName);
		this.n.subNode.style.zIndex = this.s.zIndex;
		this.s.classNames.push('subNode');
		if (this.s.classNames.length > 0) this.n.subNode.className = this.s.classNames.join(' ');
		this.n.superNode.appendChild(this.n.subNode);

		this._createIframe();
		this.hide();
		this._register();
		this.watchMouse();
		this.s.extant = true;
	},

	destroy: function() {
		DOM.remove(this.n.subNode);
		this.n.subNode = null;
		this.n.iframe = null;
		this.c.events = [];
		this._unRegister();
		this.s.extant = false;
		this.s.visible = false;
	},

	position: function (settings) {
		Object.extendProperties(this.s.position, settings);

		var things = {
			self: {
				anchors: this.s.position.selfAnchor.split(' '),
				node: this.n.superNode,
				height: Element.getDimensions(this.n.superNode).height,
				width: Element.getDimensions(this.n.superNode).width,
				top: Position.cumulativeOffset(this.n.superNode)[1],
				left: Position.cumulativeOffset(this.n.superNode)[0]
			},
			exemplar: {
				anchors: this.s.position.exemplarAnchor.split(' '),
				node: this.n.superNode,
				height: Element.getDimensions(this.n.superNode).height,
				width: Element.getDimensions(this.n.superNode).width,
				top: Position.cumulativeOffset(this.n.superNode)[1],
				left: Position.cumulativeOffset(this.n.superNode)[0]
			}
		};
		switch (this.s.position.exemplar) {
			case 'viewport':
				Object.extend(things.exemplar, {
					width: getViewportSize().width,
					height: getViewportSize().height,
					top: 0,
					left: 0
				});
			break;
			case 'pointer':
				Object.extend(things.exemplar, {
					width: 0,
					height: 0,
					top: this.mousePosition.y,
					left: this.mousePosition.x
				});
				// If the mouse has not moved yet, values will be 0,0 - so set a flag that
				// on next mouse move, div must be positioned.
				if (things.exemplar.top == 0 && things.exemplar.left == 0) this.moveOnPointerMove = true;
			break;
			case 'document':
				things.exemplar = document;
			default:                                                       // Assuming it's a DOM node
				Object.extend(things.exemplar, {
					node: this.s.position.exemplar,
					height: Element.getDimensions(this.s.position.exemplar).height,
					width: Element.getDimensions(this.s.position.exemplar).width,
					top: Position.cumulativeOffset(this.s.position.exemplar)[1],
					left: Position.cumulativeOffset(this.s.position.exemplar)[0]
				});
			break;
		}
		for (thing in things) {
			if (typeof(Object.prototype[thing]) == 'undefined') {
				Object.extend(things[thing], {
					right: things[thing].left+things[thing].width,
					bottom: things[thing].top+things[thing].height,
					center: things[thing].left+(things[thing].width/2),
					middle:	things[thing].top+(things[thing].height/2)
				});
			}
		}

		var newPosition = {
			top: this.s.position.offsetY,
			left: this.s.position.offsetX
		};
		//start it out in the position of the exemplar top left is equal to the self top left
		newPosition.left += things.exemplar.left;
		newPosition.top += things.exemplar.top;
		
		//if they want it horizontally centered in the exemplar, add half the width of the exemplar
		newPosition.left += (things.exemplar.anchors.inArray('center') != -1) ? things.exemplar.width/2 : 0;
		//if they want it on the right of the exemplar, add the whole width of the exemplar
		newPosition.left += (things.exemplar.anchors.inArray('right') != -1) ? things.exemplar.width : 0;
		//if they want it vertically centered in the exemplar, add half the height of the exemplar
		newPosition.top += (things.exemplar.anchors.inArray('middle') != -1) ? things.exemplar.height/2 : 0;
		//if they want it on the bottom of the exemplar, add the whole height of the exemplar
		newPosition.top += (things.exemplar.anchors.inArray('bottom') != -1) ? things.exemplar.height : 0;
		
		//if they want it horizontally centered in itself, subtract half the width of itself
		newPosition.left -= (things.self.anchors.inArray('center') != -1) ? things.self.width/2 : 0;
		//if they want it at the right of itself, subtract the whole width of itself
		newPosition.left -= (things.self.anchors.inArray('right') != -1) ? things.self.width : 0;
		//if they want it vertically centered in itself, subtract half the height of itself
		newPosition.top -= (things.self.anchors.inArray('middle') != -1) ? things.self.height/2 : 0;
		//if they want it at the bottom of itself, subtract the whole height of itself
		newPosition.top -= (things.self.anchors.inArray('bottom') != -1) ? things.self.height : 0;
		
		/* Adam Removed. Kramer, please look at it and see if you agree that mine is correct.
		newPosition.left += (things.exemplar.anchors.inArray('left') != -1) ? things.exemplar.left : 0;
		newPosition.left += (things.exemplar.anchors.inArray('center') != -1) ? things.exemplar.center : 0;
		newPosition.left += (things.exemplar.anchors.inArray('right') != -1) ? things.exemplar.right : 0;
		newPosition.top += (things.exemplar.anchors.inArray('top') != -1) ? things.exemplar.left : 0;
		newPosition.top += (things.exemplar.anchors.inArray('middle') != -1) ? things.exemplar.middle : 0;
		newPosition.top += (things.exemplar.anchors.inArray('bottom') != -1) ? things.exemplar.bottom : 0;

		newPosition.left -= (things.self.anchors.inArray('left') != -1) ? things.self.left : 0;
		newPosition.left -= (things.self.anchors.inArray('center') != -1) ? things.self.center : 0;
		newPosition.left -= (things.self.anchors.inArray('right') != -1) ? things.self.right : 0;
		newPosition.top -= (things.self.anchors.inArray('top') != -1) ? things.self.left : 0;
		newPosition.top -= (things.self.anchors.inArray('middle') != -1) ? things.self.middle : 0;
		newPosition.top -= (things.self.anchors.inArray('bottom') != -1) ? things.self.bottom : 0;
		*/

		Object.extend(this.n.superNode.style, {
			top: newPosition.top+'px',
			left: newPosition.left+'px'
		});
	},

	setContent: function(html) {
		this.n.subNode.innerHTML = html;
		this._sizeIframe();
	},

	withAllRegisteredNodes: function(func) {
		// Executes a callback on all nodes in the node registry.
		for (var x=0; x<this.activeNodes.length; x++) {
			func(this.activeNodes[x]);
		}
	},

	getGroupId: function() {
		return this.s.groupId;
	},

	// Private methods
	_sizeIframe: function() {
		if (this.s.useIframe && this.n.iframe) {
			var nodeSize = Element.getDimensions(this.n.subNode);
			this.n.iframe.style.height = Math.max(0, (nodeSize['height'] - this.s.iframeShrink))+'px';
			this.n.iframe.style.width = Math.max(0, (nodeSize['width'] - this.s.iframeShrink))+'px';
		}
	},

	_createIframe: function() {
		if (this.s.useIframe && !this.n.iframe) {
			this.n.iframe = document.createElement('iframe');
			this.n.iframe.setAttribute('href','javascript:void();');
			Object.extend(this.n.iframe.style, {
				scrolling:    'no',
				position:     'absolute',
				marginwidth:  0,
				marginheight: 0,
				top:          0,
				left:         0,
				frameborder:  0,
				zIndex:       -1,
				display:      'block'
			});
			this.n.superNode.appendChild(this.n.iframe);
			this._sizeIframe();
		}
	},

	_createNode: function(tagName) {
		if (this.nodeContainer === null) {
			this.nodeContainer = document.createElement('div');
			this.n._body.appendChild(this.nodeContainer);
		}
		var node = document.createElement(tagName);
		this.nodeContainer.appendChild(node);
		return node;
	},

	_isExtant: function() {
		return this.s.extant;
	},

	_isVisible: function() {
		return this.s.visible;
	},

	_register: function() {
		this.s.nodeId = this.activeNodes.length;
		this.activeNodes.push(this);
	},

	_unRegister: function() {
		this.eUnObserveAll();
		this.activeNodes.splice(this.s.nodeId,1);
	}
});