/**
 * ReMooz - Zoomer
 *
 * Inspired by so many boxes and zooms
 *
 * @version		1.0
 *
 * @license		MIT-style license
 * @author		Harald Kirschner <mail [at] digitarald.de>
 * @copyright	Author
 */

var ReMooz = new Class({

	Implements: [Events, Options, Chain],

	options: {
		link: null,
		type: 'image',
		container: null,
		className: null,
		centered: false,
		dragging: true,
		closeOnClick: true,
		shadow: (Browser.Engine.trident) ? 'onOpenEnd' : false, // performance
		resize: true,
		margin: 40,
		resizeFactor: 0.90,
		resizeLimit: false, //{x: 800, y: 800}
		fixedSize: false,
		cutOut: true,
		addClick: true,
		opacityLoad: 0.6,
		opacityResize: 1,
		opacityTitle: 1,
		resizeOptions: {},
		fxOptions: {},
		closer: false,
		parse: false, // 'rel'
		parseSecure: false,
		temporary: false,
		onBuild: $empty,
		onLoad: $empty,
		onOpen: $empty,
		onOpenEnd: $empty,
		onClose: $empty,
		onCloseEnd: $empty,
		generateTitle: function(el) {
			var text = el.get('rel');
			if (!text) return false;
			var title = text.split(' :: ');
			var head = new Element('h6', {'html': title[0]});
			return (title[1]) ? [head, new Element('p', {'html': title[1]})] : head;
		
		} 
	},

		initialize: function(element, options) {
			this.element = $(element);
			this.setOptions(options);
			if (this.options.parse) {
				var obj = this.element.getProperty(this.options.parse);
				if (obj && (obj = JSON.decode(obj, this.options.parseSecure))) this.setOptions(obj);
			}
			var origin = this.options.origin;
			this.origin = ((origin) ? $(origin) || this.element.getElement(origin) : null) || this.element;
			this.link = this.options.link || this.element.get('href') || this.element.get('src');
			this.container = $(this.options.container) || this.element.getDocument();
			this.bound = {
				'click': function(e) {
					this.open.delay(1, this);
					return false;
				}.bind(this),
				'close': this.close.bind(this),
				'dragClose': function(e) {
					if (e.rightClick) return;
					this.close();
				}.bind(this)
			};
			if (this.options.addClick) this.bindToElement();
		},

		destroy: function() {
			if (this.box) this.box.destroy();
			this.box = this.tweens = this.body = this.content = null;
		},

		bindToElement: function(element) {
			($(element) || this.element).addClass('remooz-element').addEvent('click', this.bound.click);
			return this;
		},

		getOriginCoordinates: function() {
			var coords = this.origin.getCoordinates();
			delete coords.right;
			delete coords.bottom;
			return coords;
		},

		open: function(e) {
			if (this.opened) return (e) ? this.close() : this;
			this.opened = this.loading = true;
			if (!this.box) this.build();
			this.coords = this.getOriginCoordinates();
			this.coords.opacity = this.options.opacityLoad;
			this.coords.display = '';
			this.tweens.box.set(this.coords);
			this.box.addClass('remooz-loading');
			ReMooz.open(this.fireEvent('onLoad'));
			this['open' + this.options.type.capitalize()]();
			return this;
		},

		finishOpen: function() {
			this.tweens.fade.start(0, 1);
			this.drag.attach();
			this.fireEvent('onOpenEnd').callChain();
		},

		close: function() {
			if (!this.opened) return this;
			this.opened = false;
			ReMooz.close(this.fireEvent('onClose'));
			if (this.loading) {
				this.box.setStyle('display', 'none');
				return this;
			}
			this.drag.detach();
			this.tweens.fade.cancel().set(0).fireEvent('onComplete');
			if (this.tweens.box.timer) this.tweens.box.clearChain();
			var vars = this.getOriginCoordinates();
			if (this.options.opacityResize != 1) vars.opacity = this.options.opacityResize;
			this.tweens.box.start(vars).chain(this.closeEnd.bind(this));
			return this;
		},

		closeEnd: function() {
			if (this.options.cutOut) this.element.setStyle('visibility', 'visible');
			this.box.setStyle('display', 'none');
			this.fireEvent('onCloseEnd').callChain();
			if (this.options.temporary) this.destroy();
		},

		openImage: function() {
			var tmp = new Image();
			tmp.onload = tmp.onabort = tmp.onerror = function(fast) {
				this.loading = tmp.onload = tmp.onabort = tmp.onerror = null;
				if (!tmp.width || !this.opened) {
					this.fireEvent('onError').close();
					return;
				}
				var to = {x: tmp.width, y: tmp.height};
				if (!this.content) this.content = $(tmp).inject(this.body);
				else tmp = null;
				this[(this.options.resize) ? 'zoomRelativeTo' : 'zoomTo'].create({
					'delay': (tmp && fast !== true) ? 1 : null,
					'arguments': [to],
					'bind': this
				})();
			}.bind(this);
			tmp.src = this.link;
			if (tmp && tmp.complete && tmp.onload) tmp.onload(true);
		},

		/**
		 * @todo Test implementation
		 */
		openElement: function() {
			this.content = this.content || $(this.link) || $E(this.link);
			if (!this.content) {
				this.fireEvent('onError').close();
				return;
			}
			this.content.inject(this.body);
			this.zoomTo({x: this.content.scrollWidth, y: this.content.scrollHeight});
		},

		zoomRelativeTo: function(to) {
			var scale = this.options.resizeLimit;
			if (!scale) {
				scale = this.container.getSize();
				scale.x *= this.options.resizeFactor;
				scale.y *= this.options.resizeFactor;
			}
			for (var i = 2; i--;) {
				if (to.x > scale.x) {
					to.y *= scale.x / to.x;
					to.x = scale.x;
				} else if (to.y > scale.y) {
					to.x *= scale.y / to.y;
					to.y = scale.y;
				}
			}
			return this.zoomTo({x: to.x.toInt(), y: to.y.toInt()});
		},

		zoomTo: function(to) {
			to = this.options.fixedSize || to;
			var box = this.container.getSize(), scroll = this.container.getScroll();
			var pos = (!this.options.centered) ? {
				x: (this.coords.left + (this.coords.width / 2) - to.x / 2).toInt()
					.limit(scroll.x + this.options.margin, scroll.x + box.x - this.options.margin - to.x),
				y: (this.coords.top + (this.coords.height / 2) - to.y / 2).toInt()
					.limit(scroll.y + this.options.margin, scroll.y + box.y - this.options.margin - to.y)
			} :  {
				x: scroll.x + ((box.x - to.x) / 2).toInt(),
				y: scroll.y + ((box.y - to.y) / 2).toInt()
			};
			if (this.options.cutOut) this.element.setStyle('visibility', 'hidden');
			this.box.removeClass('remooz-loading');
			var vars = {left: pos.x, top: pos.y, width: to.x, height: to.y};
			if (this.options.opacityResize != 1) vars.opacity = [this.options.opacityResize, 1];
			else this.box.set('opacity', 1);
			this.tweens.box.start(vars).chain(this.finishOpen.bind(this));
			this.fireEvent('onOpen');
		},

		build: function() {
			this.addEvent('onBlur', function() {
				this.focused = false;
				this.box.removeClass('remooz-box-focus').setStyle('z-index', ReMooz.options.zIndex);
			}, true);
			this.addEvent('onFocus', function() {
				this.focused = true;
				this.box.addClass('remooz-box-focus').setStyle('z-index', ReMooz.options.zIndexFocus);
			}, true);

			var classes = ['remooz-box', 'remooz-type-' + this.options.type, 'remooz-engine-' + Browser.Engine.name + Browser.Engine.version];
			if (this.options.className) classes.push(this.options.className);
			this.box = new Element('div', {
				'class': classes.join(' '),
				'styles': {
					'display': 'none',
					'top': 0,
					'left': 0,
					'zIndex': ReMooz.options.zIndex
				}
			});

			this.tweens = {
				'box': new Fx.Morph(this.box, $merge({
						'duration': 400,
						'unit': 'px',
						'transition': Fx.Transitions.Quart.easeOut,
						'chain': 'cancel'
					}, this.options.resizeOptions)
				),
				'fade': new Fx.Tween(null, $merge({
						'property': 'opacity',
						'duration': (Browser.Engine.trident) ? 0 : 300,
						'chain': 'cancel'
					}, this.options.fxOptions)).addEvents({
						'onComplete': function() {
							if (!this.element.get('opacity')) this.element.setStyle('display', 'none');
						},
						'onStart': function() {
							if (!this.element.get('opacity')) this.element.setStyle('display', '');
						}
					}
				)
			};
			this.tweens.fade.element = $$();

			if (this.options.shadow) {
				if (Browser.Engine.webkit420) {
					this.box.setStyle('-webkit-box-shadow', '0 0 10px rgba(0, 0, 0, 0.7)');
				} else if (!Browser.Engine.trident4) {
					var shadow = new Element('div', {'class': 'remooz-bg-wrap'}).inject(this.box);
					['n', 'ne', 'e', 'se', 's', 'sw', 'w', 'nw'].each(function(dir) {
						new Element('div', {'class': 'remooz-bg remooz-bg-' + dir}).inject(shadow);
					});
					this.tweens.bg = new Fx.Tween(shadow, {
						'property': 'opacity',
						'chain': 'cancel'
					}).set(0);
					this.addEvent(this.options.shadow, this.tweens.bg.set.bind(this.tweens.bg, 1), true);
					this.addEvent('onClose', this.tweens.bg.set.bind(this.tweens.bg, 0), true);
				}
			}

			if (this.options.closer) {
				var closer = new Element('a', {
					'class': 'remooz-btn-close',
					'events': {'click': this.bound.close}
				}).inject(this.box);
				this.tweens.fade.element.push(closer);
			}
			this.body = new Element('div', {'class': 'remooz-body'}).inject(this.box);

			var title = this.options.title || this.options.generateTitle.call(this, this.element);
			if (title) { // thx ie6
				var title = new Element('div', {'class': 'remooz-title'}).adopt(
					new Element('div', {'class': 'remooz-title-bg', 'opacity': this.options.opacityTitle}),
					new Element('div', {'class': 'remooz-title-content'}).adopt(title)
				).inject(this.box);
				this.tweens.fade.element.push(title);
			}
			this.tweens.fade.set(0).fireEvent('onComplete');

			this.drag = new Drag.Move(this.box, {
				'snap': 15,
				'preventDefault': true,
				'onBeforeStart': function() {
					if (!this.focused && !this.loading) ReMooz.focus(this);
					else if (this.loading || this.options.closeOnClick) this.box.addEvent('mouseup', this.bound.dragClose);
				}.bind(this),
				'onSnap': function() {
					this.box.removeEvent('mouseup', this.bound.dragClose);
					if (!this.options.dragging) this.drag.stop();
					else this.box.addClass('remooz-box-dragging');
				}.bind(this),
				'onComplete': function() {
					this.box.removeClass('remooz-box-dragging');
				}.bind(this)
			});
			this.drag.detach();

			this.fireEvent('onBuild', this.box, this.element);
			this.box.inject(this.element.getDocument().body);
		}

	});

	ReMooz.factory = function(extended) {
		return $extend(this, extended);
	};

	ReMooz.factory(new Options).factory({

		options: {
			zIndex: 41,
			zIndexFocus: 42,
			query: 'a.remooz',
			modal: false
		},

		assign: function(elements, options) {
			return $$(elements).map(function(element) {
				return new ReMooz(element, options);
			}, this);
		},

		stack: [],

		open: function(obj) {
			var last = this.stack.getLast();
			this.focus(obj);
			if (last && this.options.modal) last.close();
		},

		close: function(obj) {
			var length = this.stack.length - 1;
			if (length > 1 && this.stack[length] == obj) this.focus(this.stack[length - 1]);
			this.stack.erase(obj);
		},

		focus: function(obj) {
			var last = this.stack.getLast();
			obj.fireEvent('onFocus', [obj]);
			if (last == obj) return;
			if (last) last.fireEvent('onBlur', [last]);
			this.stack.erase(obj).push(obj);
		}

	});
