/*globals $, window, myDeco, document, tmpl */
/*jslint forin: true, fragment: true, onevar: true, undef: true, nomen: false, eqeqeq: true, plusplus: true, bitwise: true, newcap: true, immed: true, regexp: false, white: false, plusplus: false */
/**
 * @fileOverview Component for replacing lightwindow with a little small functionality
 */

if (typeof myDeco.popin === 'undefined') {
	myDeco.popin = {
		/**
		 * Default options
		 */
		options: {
			base: 'popin',
			width: 616,
			height: 200,
			url: '',
			afterOpen: null,
			beforeClose: null,
			closeIn: 0,
			fullscreen: false,
			fullscreenMargin: 30,
			autoResizeX: false,
			autoResizeY: true,
			ajaxLoaderClass: 'ajaxLoader',
			template: null,
			borders: 50,
			showClose: true,
			closeText: '',
			className: '',
			tracking: false,
			context: false
		},
		
		/**
		 * observers cache
		 */
		_cclose: null,
		_ccloseByKeyPress: null,
		_cresize: null,
		
		/**
		 * Current window options
		 */
		windowOptions: {},
		
		/**
		 * Visibility marker
		 */
		visible: false,
		
		/**
		 * Content holder variable
		 */
		holder: '',
		
		/**
		 * Content layer
		 */
		contentContainer: '',
		
		/**
		 * Background layer
		 */
		background: '',
		
		/**
		 * Background opacity level, will fetch from css 'opacity' property; default is 1
		 * @type {Number}
		 */
		backgroundOpacity: 1,
		
		/**
		 * Init function
		 * @param {Object} options Defailt options for initialisation
		 */
		init: function(options) {
			if (this._cclose) {
				return;
			}
			this.options = $.extend(true, this.options, options || {});
			this._cclose = this.close.bind(this);
			this._ccloseByKeyPress = this._closeByKeyPress.bind(this);
			this._cresize = this.resize.bind(this);
		},
		
		/**
		 * Open popin with specified parameters, or set a new parameters and content on already opened popin
		 * @param {Object} options Options for new window
		 */
		open: function(options) {
			// lazy load
			this.init();
			
			if (this.visible && $.isFunction(this.windowOptions.beforeClose)) {
				this.windowOptions.beforeClose();
			}
			
			// extend options to full size
			this.windowOptions = $.extend(true, this.windowOptions, this.options, options || {});
			
			// return if url is empty
			if (this.windowOptions.url === '' && this.windowOptions.template === null) {
				return;
			}
	
			// content holder node
			var holderId = this.windowOptions.base + '-content-holder',
				// content node
				contentId = this.windowOptions.base + '-content',
				self = this,
				closeTitle;
			
			if (!this.visible) {
				this.holder = $('<div id="' + holderId + '" class="' + holderId + '" style="display:none;position:absolute"></div>')
					.appendTo(myDeco.body);
		
				this.contentContainer =  $('<div class="clearfix ' + contentId + '" id="' + contentId + '"></div>')
					.appendTo(this.holder);
		
				if (this.windowOptions.showClose) {
					this.holder.append('<a href="#" class="' + this.windowOptions.base +
						'-close-title">' + this.windowOptions.closeText + '</a>');
				}
				
				// push it into page
				this.contentContainer.hide();
	
				this.holder.css({
					display: 'block',
					left: parseInt(myDeco.window.scrollLeft() + myDeco.window.width() / 2, 10) + 'px',
					top: parseInt(myDeco.window.scrollTop() + myDeco.window.height() / 2, 10) + 'px',
					width: 0,
					height: 0
				});
				
				this._showBackground();
				
				// document wide listeners
				myDeco.window.keypress(this._ccloseByKeyPress).bind('resize', this._cresize);
				
				myDeco.body.bind('close.popin', this._cclose);
			} else {
				closeTitle = this.holder.find('>.' + this.windowOptions.base + '-close-title');
				if (this.windowOptions.showClose && closeTitle.size() === 0) {
					this.holder.append('<a href="#" class="' + this.windowOptions.base + '-close-title">' + this.windowOptions.closeText + '</a>');
				} else if (!this.windowOptions.showClose && closeTitle.size() !== 0) {
					closeTitle.remove();
				}
				
				if ($.isFunction(this.windowOptions.beforeCreate)) {
					this.windowOptions.beforeCreate(this.contentContainer);
				}
				this.holder.find('.' + this.windowOptions.base + '-close-title').unbind('click', this._cclose);
				this.contentContainer.find('.' + this.windowOptions.base + '-close').unbind('click', this._cclose);
				this._hideContent();
			}
			
			// cleanup class names
			this.holder.attr('className', holderId);
			this.contentContainer.attr('className', contentId);

			this.holder.toggleClass(this.windowOptions.base + '-fullscreen', this.windowOptions.fullscreen);
			
			if (this.windowOptions.className) {
				this.contentContainer.addClass(this.windowOptions.className);
			}
			
			if (this.windowOptions.holderClassName) {
				this.holder.addClass(this.windowOptions.holderClassName);
			}
			
			// hide problem elements in IE
			this._hideProblemElements();
			
			// update content
			if (this.windowOptions.template || this.windowOptions.url.indexOf('#') === 0) {
				// inline content
				this.windowOptions.template = this.windowOptions.template || $(this.windowOptions.url).html();
				this._processContent();
			} else {
				this.contentContainer.html('').addClass(this.windowOptions.ajaxLoaderClass);
				$.get(this.windowOptions.url, function(template) {
					self.windowOptions.template = template;
					self.contentContainer.removeClass(self.windowOptions.ajaxLoaderClass);
					self._processContent();
				});
			}
			
			return this;
		},
		
		_processContent: function() {
			var self = this, trackingLabel = this.windowOptions.tracking;
			
			this.contentContainer.html(this.windowOptions.context !== false ?
				tmpl(this.windowOptions.template, this.windowOptions.context || {}) :
				this.windowOptions.template);
				
			// show content
			this._showContent();
			
			// listeners
			this.holder.find('.' + this.windowOptions.base + '-close-title').bind('click', this._cclose);
			this.contentContainer.find('.' + this.windowOptions.base + '-close').bind('click', this._cclose);
			
			// after create callback
			if ($.isFunction(this.windowOptions.afterOpen)) {
				this.windowOptions.afterOpen(this.contentContainer);
			}
			
			// track this popin as a page view in google analytics
			if (trackingLabel) {
				if (typeof trackingLabel !== 'string') {
					trackingLabel = this.windowOptions.className || this.windowOptions.base;
				}
				myDeco.tracking.trackPageview(window.location.pathname + '#' + trackingLabel);
			}
	
			// resize area
			this.resize();
			
			this.visible = true;
	
			// set autoclose callback
			if (this.windowOptions.closeIn > 0) {
				// autoclose marker to content 
				this.contentContainer.timed = true;
				window.setTimeout(function() {
					if (self.contentContainer.timed) {
						self.close();
					}
				}, this.windowOptions.closeIn * 1000);
			}
		},
		
		/**
		 * Close active popin
		 * @param {Event} event Click event from elements or nothing in case of manual hitting
		 */
		close: function(e) {
			if (e) {
				e.stop();
			}
			
			// we do not want close an already closed popin
			if (!this.visible) {
				return false;
			}
			
			// run before close callback
			if ($.isFunction(this.windowOptions.beforeClose)) {
				this.windowOptions.beforeClose(this.contentContainer);
			}
			
			// stop our observers
			myDeco.window
				.unbind('keypress', this._ccloseByKeyPress)
				.unbind('resize', this._cresize);
			
			myDeco.body.unbind('close.popin', this._cclose);
			this.holder.find('.' + this.windowOptions.base + '-close-title').unbind('click', this._cclose);
			this.contentContainer.find('.' + this.windowOptions.base + '-close').unbind('click', this._cclose);
			
			// hide popin
			this.visible = false;
			this._hideHolder();
			this._hideBackground();
			// show problem elements
			this._showProblemElements();
		},
		
		/**
		 * Return content element, usable for manipulating with content elements
		 */
		content: function() {
			return this.contentContainer;
		},
		
		/**
		 * Return content holder element
		 */
		contentHolder: function() {
			return this.holder;
		},
		
		/**
		 * Return current state of window
		 */
		isOpen: function() {
			return this.visible;
		}, 
		
		/**
		 * Resize current popin to the new size
		 * @param {Number} width New popin width
		 * @param {Number} height New popin height
		 */
		resize: function(width, height) {
			if (typeof width === 'object'){
				width = undefined;
			}
			if (typeof height === 'object') {
				height = undefined;
			}
			var offsetLeft = myDeco.window.scrollLeft(),
				offsetTop = myDeco.window.scrollTop(),
				wWidth = myDeco.window.width(),
				wHeight = myDeco.window.height(),
				difference = 0,
				to = {
					width: width || (this.windowOptions.fullscreen ?
							wWidth - this.windowOptions.fullscreenMargin * 2 :
							this.windowOptions.width),
					height: height || (this.windowOptions.fullscreen ?
							wHeight - this.windowOptions.fullscreenMargin * 2 :
							this.windowOptions.height)
				},
				corner = {
					left: parseInt(offsetLeft + (wWidth - to.width) / 2, 10),
					top: parseInt(offsetTop + (wHeight - to.height) / 2, 10)
				};
			
			// move and resize
			this.holder.css({
				left: corner.left + 'px',
				top: corner.top + 'px',
				width: to.width + 'px',
				height: to.height + 'px'
			});
			
			if (!this.windowOptions.fullscreen) {
				if (this.windowOptions.autoResizeX) {
					to.width = this.contentContainer.innerWidth();
					difference = wWidth - this.contentContainer.outerWidth(true) - this.windowOptions.borders * 2;
					if (difference < 0) {
						to.width += difference;
					}
					corner.left = parseInt(offsetLeft + (wWidth - to.width) / 2, 10);
				}
				
				if (this.windowOptions.autoResizeY) { 
					to.height = this.contentContainer.innerHeight();
					difference = wHeight - this.contentContainer.outerHeight(true) - this.windowOptions.borders * 2;
					if (difference < 0) {
						to.height += difference;
					}
					corner.top = parseInt(offsetTop + (wHeight - to.height) / 2, 10);
				}
				
				if (this.windowOptions.autoResizeX || this.windowOptions.autoResizeY) {
					this.holder.css({
						left: corner.left + 'px',
						top: corner.top + 'px',
						width: to.width + 'px',
						height: to.height + 'px'
					});
				}
			}
			
			// finally fix background
			this._showBackground();
		},
		
		/**
		 * Show content layer, maybe smooth in feature
		 * 
		 * @private
		 */
		_showContent: function() {
			this.contentContainer.show();
		},
		
		/**
		 * Hide content layer, maybe smooth in feature
		 * 
		 * @private
		 */
		_hideContent: function() {
			this.contentContainer.hide();
		},
		
		/**
		 * Hide content holder, maybe smooth in feature
		 */
		_hideHolder: function() {
			this.holder.remove();
		},
		
		/**
		 * Show background overlay
		 * @param {Object} options New popin options
		 * 
		 * @private
		 */
		_showBackground: function() {
			var xScroll, yScroll, dimensions = {}, windowWidth, windowHeight,
				backgroundId = this.windowOptions.base + '-background';
			
			if (window.innerHeight && window.scrollMaxY) {
				xScroll = document.body.scrollWidth;
				yScroll = window.innerHeight + window.scrollMaxY;
			} else if (document.body.scrollHeight > document.body.offsetHeight) {
				xScroll = document.body.scrollWidth;
				yScroll = document.body.scrollHeight;
			} else {
				xScroll = document.body.offsetWidth;
				yScroll = document.body.offsetHeight;
			}
	
			if (document.documentElement && document.documentElement.clientHeight) {
				windowWidth = document.documentElement.clientWidth;
				windowHeight = document.documentElement.clientHeight;
			} else if (document.body) {
				windowWidth = document.body.clientWidth;
				windowHeight = document.body.clientHeight;
			}
			
			dimensions.height = Math.max(windowHeight, yScroll);
			dimensions.width = Math.max(windowWidth, xScroll);
	
			if (!this.background || this.background.size() === 0) {
				this.background = $('<div id="' + backgroundId + '" class="' + backgroundId + '" />').css({
					position: 'absolute',
					display: 'none',
					left: 0,
					top: 0
				});
				myDeco.body.append(this.background);
				
				// now try to find original opacity
				this.backgroundOpacity = (this.background[0].currentStyle ?
					this.background[0].currentStyle.opacity :
					                                 // Chromium returns opcity as a string with the
					                                 // comma as delimiter
					this.background.css('opacity')).toString().replace(',', '.');
				
				this.background
					.css({
						opacity: 0,
						display: 'block'
					})
					.show();
			} else {
				this.background.stop().queue([]);
			}
			
			this.background
				.width(dimensions.width)
				.height(dimensions.height)
				.fadeTo('fast', this.backgroundOpacity);
		},
		
		/**
		 * Hide background
		 * 
		 * @private
		 */
		_hideBackground: function() {
			this.background.fadeTo('fast', 0, function() {
				$(this).hide();
			});
		},
		
		/**
		 * Hide problem elements in IE 6
		 * 
		 * @private
		 */
		_hideProblemElements: function() {
			if (myDeco.browser.isIE6 && !this.visible) {
				$.each($('select, object, embed, iframe'), function(i, element) {
					element = $(element);
					element
						.data('visibility', element.css('visibility'))
						.css('visibility', 'hidden');
				});
			}
		},
		
		/**
		 * Restore problem elements to its previous state
		 * 
		 * @private
		 */
		_showProblemElements: function() {
			if (myDeco.browser.isIE6) {
				$.each($('select, object, embed, iframe'), function(i, element) {
					element = $(element);
					var visibility = element.data('visibility');
					if (visibility) {
						element.css('visibility', visibility);
					}
				});
			}
		},
		
		/**
		 * Keypress observer, waits for ESC key and close popin
		 * @param {Event} e Keypress event
		 */
		_closeByKeyPress: function(e) {
			if (e.keyCode === 27) {
				this.close();
			}
		},
		
		/**
		 * Extend current object with plugin.
		 * Plugin will be available as myDeco.popin.name
		 * @param {String} name Plugin name
		 * @param {Object} defoptions Default options for plugin
		 * @param {Function} callback Callback which will be started before afterOpen callback 
		 */
		plugin: function(name, defoptions, callback) {
			this.init();
			var self = this;
			if (typeof this[name] !== 'undefined') {
				throw "Name is already occuped";
			}
			
			defoptions = defoptions || {};
			callback = callback || function() {};
			this[name] = function(options) {
				options = options || {};
				var afterOpen = options.afterOpen ? function(div) {
					(callback.bind(self))(div);
					options.afterOpen(div);
				} : callback.bind(self);
				this.open($.extend(true, {}, defoptions, options, {afterOpen: afterOpen}));
			};
		}
	};
	
	$(function() {
		var popin = myDeco.popin;
		popin.init.apply(popin);
		
		/**
		 * Replacement for standard alert() function
		 * @param {Object} options Additionally to default options are:
		 *   closeButtonText {String} default 'Close' text will replace with this value
		 *   title {String} replacement for title, will remove if no specified
		 *   message {String} replacement for message, will remove if no specified
		 */
		popin.plugin('alert', {
			template: '<h2 class="top-line title">Alert</h2><p class="message"></p>' +
				'<div class="toolbar"><a href="#" class="button green-button popin-close">' +
				'<span class="close-button-text">Close</span></a></div>'
		}, function(div) {
			if (this.windowOptions.closeButtonText) {
				div.find('.close-button-text').html(this.windowOptions.closeButtonText);
			}
			if (this.windowOptions.title) {
				div.find('.title').html(this.windowOptions.title);
			} else {
				div.find('.title').remove();
			}
			if (this.windowOptions.message) {
				div.find('.message').html(this.windowOptions.message);
			} else {
				div.find('.message').remove();
			}
		});
		
		/**
		 * Replacement for standard alert() function
		 * @param {Object} options Additionally to default options are:
		 *   yesButtonText {String} default 'Yes' text will replace with this value
		 *   noButtonText {String} default 'No' text will replace with this value
		 *   yesButton {Function} defaults to nothing
		 *   noButton {Function} defaults to close popin
		 *   title {String} replacement for title, will remove if no specified
		 *   message {String} replacement for message, will remove if no specified
		 */
		popin.plugin('confirm', {
			template: '<h2 class="top-line title">Confirm</h2><p class="message"></p>' +
				'<div class="toolbar"><a href="#" class="button green-button yes-button">' +
				'<span class="yes-button-text">Yes</span></a>' +
				'<a href="#" class="button button-no-arrow no-button"><span class="no-button-text">No</span>' +
				'</a></div>'
		}, function(div) {
			if (this.windowOptions.yesButtonText) {
				div.find('.yes-button-text').html(this.windowOptions.yesButtonText);
			}
			if (this.windowOptions.noButtonText) {
				div.find('.no-button-text').html(this.windowOptions.noButtonText);
			}
			if (this.windowOptions.yesButton) {
				div.find('.yes-button').click(this.windowOptions.yesButton);
			}
			div.find('.no-button').click(this.windowOptions.noButton || this._cclose);
			if (this.windowOptions.title) {
				div.find('.title').html(this.windowOptions.title);
			} else {
				div.find('.title').remove();
			}
			if (this.windowOptions.message) {
				div.find('.message').html(this.windowOptions.message);
			} else {
				div.find('.message').remove();
			}
		});
		
		/**
		 * Replacement for lightwindow image show
		 * @param {Object} options Additionally to default options are:
		 *   title {String} replacement for title, will remove if no specified
		 */
		popin.plugin('image', {
			autoResizeX: true,
			template: '<h2 class="top-line title">Image</h2><div class="popin-image" style="position:relative" />'
		}, function(div) {
			var self = this;
			if (this.windowOptions.title) {
				div.find('.title').html(this.windowOptions.title);
			} else {
				div.find('.title').remove();
			}
			
			$('<img src="' + this.windowOptions.url + '" />').load(function(e) {
				var img = $(e.target),
					width, height, ccw, cch, chw, chh, wd, hd;
				div.find('.popin-image').css('height', 'auto').prepend(img);
				width = img.width();
				height = img.height();
				self.contentContainer.width(width);
				self.resize();
				
				// resize image to fit to content container
				ccw = self.contentContainer.outerWidth(true);
				cch = self.contentContainer.outerHeight(true);
				chw = self.holder.innerWidth();
				chh = self.holder.innerHeight();
				
				if (ccw > chw || cch > chh) {
					wd = ccw - chw;
					hd = cch - chh;
					if (wd > hd) {
						img.width(width - wd);
						self.contentContainer.width(width - wd);
					} else {
						img.height(height - hd);
						self.contentContainer.width(parseInt((height - hd) * width / height, 10));
					}
					
					self.resize();
				}
			});
		});
		
		popin.plugin('error', {
			template: '<div class="message"><div></div><div class="buttons"><a class="button popin-close">' +
						'<span class="close-button-text">Close</span></a></div></div>',
			showClose: false
		}, function(div) {
			if (this.windowOptions.closeButtonText) {
				div.find('.close-button-text').html(this.windowOptions.closeButtonText);
			}
			div.find('.message div:first').html(this.windowOptions.message || "Something goes wrong");
		});
	});
}
