(function () {
	
	var max_zIndex = 1;
	
	var P = jQuery.Popup = function (node, options) {
		this.options = $.extend({
			constrain: null,
			position: 'center',
			zIndex: null,
			
			offset: [0,0],
			
			show_arrow: true,
			show_close: true,
			
			visible: false
		}, options || {});
		
		this.node = (node && node.length ? node : null);
		
		if (this.options.visible) {
			this.render();
		}
	};
	
	jQuery.Popup.CLASS_NAME = 'popup';
	jQuery.Popup.ARROW_POSITIONS = {'left': 'l', 'top': 't', 'right': 'r', 'bottom': 'b'};
	jQuery.Popup.POPUP_OFFSETS = {'left': [20, -53], 'top': [-90, 20], 'right': [-20, -53], 'bottom': [-90, -20]};
	
	jQuery.Popup.prototype = {
		/* Popup options */
		options: null,
		
		/* Container node */
		container: null,
		
		/* Content node */
		content: null,
		
		/* Mask node */
		mask: null,
		
		/* Inner content node */
		node: null,
		
		/**
		 * Show popup
		 */
		show: function () {
			this.options.visible = true;
			
			if (!this.container) {
				this.render();
			}
			
			this.bringToFront();
			
			this.container.show();
			this.syncUI();
		},
		
		/**
		 * Hide popup
		 */
		hide: function () {
			this.options.visible = false;
			
			if (this.container) {
				this.container.hide();
			}
		},
		
		/**
		 * Bring popup in front of other popups if popup
		 * doesn't have zIndex set in options
		 */
		bringToFront: function () {
			if (!this.options.zIndex) {
				max_zIndex++;
				this.container.css('zIndex', max_zIndex);
			}
		},
		
		/**
		 * Reposition popup
		 */
		_syncUIPosition: function () {
			var offset = [0, 0];
				
			if (this.options.constrain && this.options.constrain.length) {
				var position = this.options.position in P.POPUP_OFFSETS ? this.options.position : 'left';
				offset = P.POPUP_OFFSETS[position];
				offset = [offset[0], offset[1]];
				
				var target_pos = this.options.constrain.offset();
				offset[0] += target_pos.left;
				offset[1] += target_pos.top;
				
				switch(position) {
					case 'left':
						offset[0] += this.options.constrain.outerWidth();
						offset[1] += ~~(this.options.constrain.outerHeight()/2);
						break;
					case 'top':
						offset[1] += this.options.constrain.outerHeight();
						offset[0] += ~~(this.options.constrain.outerWidth()/2);
						break;
					case 'right':
						offset[0] -= this.container.width();
						offset[1] += ~~(this.options.constrain.outerHeight()/2);
						break;
					case 'bottom':
						offset[1] -= this.container.height();
						offset[0] += ~~(this.options.constrain.outerWidth()/2);
						break;
				}
				
			} else {
				offset[0] += ~~(($(window).width() - this.container.width()) / 2);
				offset[1] += ~~(($(window).height() - this.container.height()) / 2) + $(window).scrollTop();
			}
			
			offset[0] += this.options.offset[0];
			offset[1] += this.options.offset[1];
			
			this.container.css({
				left: offset[0] + 'px',
				top: offset[1] + 'px'
			});
		},
		
		/**
		 * Returns classname which is dash separated string
		 * of popup classname and arguments
		 * 
		 * @return Classname
		 * @type {String}
		 */
		_getClass: function () {
			var c = [P.CLASS_NAME];
			c = c.concat.apply(c, arguments);
			
			return c.join('-');
		},
		
		/**
		 * Create popup
		 */
		buildUI: function () {
			var tpl_arrow = '';
			
			if ((this.options.show_arrow && this.options.position in P.ARROW_POSITIONS) || this.options.constrain) {
				var pos = P.ARROW_POSITIONS[this.options.position] || 'l';
				tpl_arrow = '<div class="' + this._getClass('arrow') + ' ' + this._getClass('arrow', pos) + '"></div>';
			}
			
			var tpl =  '<div class="' + this._getClass() + '">\
							<a class="' + this._getClass('close') + (!this.options.show_close ? ' hidden' : '') + '"></a>\
							' + tpl_arrow + '\
							<div class="c-t"></div>\
							<div class="c-b"></div>\
							<div class="c-l"></div>\
							<div class="c-r"></div>\
							<div class="c-lt"></div><div class="c-rt"></div><div class="c-rb"></div><div class="c-lb"></div>\
							<div class="' + this._getClass('content') + '"></div>\
						</div>';
			
			this.container = $(tpl).appendTo(document.body);
			this.content = this.container.find('div.' + this._getClass('content'));
			
			if (this.options.zIndex) {
				this.container.css('zIndex', this.options.zIndex);
			}
			
			if (this.node && this.node.length) {
				this.content.append(this.node);
			}
		},
		
		/**
		 * Bind event listeners
		 */
		bindUI: function () {
			this.container.find('a.' + this._getClass('close')).click($.proxy(function() {
				this.hide();
			}, this));
		},
		
		/**
		 * Set constrain target
		 */
		constrainTo: function (target) {
			this.options.constrain = target;
			
			if (this.options.visible) {
				this.syncUI();
			}
		},
		
		/**
		 * Position popup
		 */
		syncUI: function () {
			this._syncUIPosition();
			this.container.reflow();
		},
		
		render: function () {
			this.buildUI();
			this.bindUI();
			this.syncUI();
		}
	};
	
	/**
	 * jQuery popup plugin
	 * 
	 * @param {Object} options
	 */
	jQuery.fn.popup = function (options) {
		
		var popup = $(this).data('popup');
		if (popup) return popup;
		
		popup = new jQuery.Popup($(this), options);
		$(this).data('popup', popup);
		
		return popup;
	}
	
})();