/**
 * ReMooz - Image Zoombox
 * 
 * 
 * Inspired by so many boxes and zooms
 * 
 * @version		1.0rc1
 * 
 * @license		MIT-style license
 * @author		Harald Kirschner <mail [at] digitarald.de>
 * @copyright	Author
 */
var ReMooz = new Class({

	Implements: [Events, Options],

	options: {
		url: null,
		type: 'image',
		className: null,
		positionToCenter: false,
		dragging: true,
		shadow: true,
		resize: true,
		margin: 20,
		resizeFactor: 0.8,
		resizeLimit: false, // {x: 640, y: 640}
		hideSource: true,
		addClick: true,
		resizeOptions: {},
		resizeOpacity: 1,
		fxsOptions: {},
		generateTitle: null,
		showTitle: null,
		onBuild: $empty,
		onLoad: $empty,
		onOpen: $empty,
		onOpenEnd: $empty,
		onClose: $empty,
		onCloseEnd: $empty
	},

	initialize: function(element, options) {
		this.element = $(element);
		this.setOptions(options);
		this.url = this.options.url || this.element.href || this.element.src;

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

		this.element.addClass('remo-zoom-in').addEvent('trash', this.destroy.bind(this));

		this.clickEvent = function(e) {
			this.open.delay(1, this);
			return false;
		}.bind(this);
		if (this.options.addClick) this.bindToElement();
	},

	destroy: function(unload) {
		if (this.box && !unload) this.box.destroy();
		this.box = this.boxFx = this.content = null;
		return null;
	},

	bindToElement: function(el) {
		($(el) || this.element).addEvent('click', this.clickEvent);
		return this;
	},

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

	open: function(e) {
		if (this.opened) return (e) ? this.close() : this;
		this.opened = true;
		if (!this.box) this.build();
		this.coords = this.getSourceCoordinates();
		this.coords.opacity = 0.7;
		this.boxFx.set(this.coords);
		this.box.setStyle('display', '').addClass('remo-loading');
		this.boxDrag = this.boxDrag || new Drag.Move(this.box, { // inits here because of safari
			'snap': 15,
			'onStart': function() {
				if (!this.focussed && !this.loading) {
					ReMooz.focus(this);
					this.focusEvent = true;
				}
			}.bind(this),
			'onSnap': function() {
				this.dragging = true;
				this.box.addClass('remo-box-dragging');
			}.bind(this),
			'onComplete': function() {
				if (!this.dragging && !this.focusEvent) this.close();
				this.dragging = this.focusEvent = false;
				this.box.removeClass('remo-box-dragging');
			}.bind(this)
		}).detach();
		this.fireEvent('onLoad');
		this.loadImage();
		return this;
	},

	openEnd: function() {
		ReMooz.open(this);
		this.zoomed = true;
		if (this.options.dragging) this.boxDrag.attach();
		this.fxs.start(0, 1);
		this.fireEvent('onOpenEnd');
	},

	close: function() {
		if (!this.opened) return this;
		this.fireEvent('onClose');
		this.opened = this.zoomed = false;
		ReMooz.close(this);
		if (this.loading) {
			this.box.setStyle('display', 'none');
			return this;
		}
		this.boxDrag.detach();
		this.fxs.set(0);
		if (this.boxFx.timer) this.boxFx.clearChain();
		var vars = this.getSourceCoordinates();
		if (this.options.resizeOpacity != 1) vars.opacity = this.options.resizeOpacity;
		this.boxFx.start(vars).chain(this.closeEnd.bind(this));
		return this;
	},

	closeEnd: function() {
		this.element.setStyle('visibility', 'visible');
		this.box.setStyle('display', 'none');
		this.fireEvent('onCloseEnd');
	},

	loadImage: function() {
		this.loading = true;
		var loader = new Image();
		loader.onload = loader.onabort = loader.onerror = function(fast) {
			this.loading = loader.onload = loader.onabort = loader.onerror = null;
			if (!loader.width || !this.opened) {
				this.close();
				return;
			}
			var to = {x: loader.width, y: loader.height};
			if (!this.image)
			{
				 this.image = (Client.Engine.webkit419) ? new Element('img', {'src': loader.src}) : $(loader);
				 this.image.addClass('remo-img').inject(this.content);
			} else loader = null;
			this.openImage.create({
				'delay': (loader && fast !== true) ? 1 : null,
				'arguments': [to],
				'bind': this
			})();
		}.bind(this);
		loader.src = this.url;
		if (loader && loader.complete && loader.onload) loader.onload(true);
	},

	openImage: function(size) {
		if (this.options.hideSource) this.element.setStyle('visibility', 'hidden');
		this[(this.options.resize) ? 'zoomRelativeTo' : 'zoomTo'](size);
	},

	zoomRelativeTo: function(to) {
		var max = this.options.resizeLimit || {
			x: Client.getWidth() * this.options.resizeFactor,
			y: Client.getHeight() * this.options.resizeFactor
		};
		for (var i = 2; i--;) {
			if (to.x > max.x) {
				to.y *= max.x / to.x;
				to.x = max.x;
			} else if (to.y > max.y) {
				to.x *= max.y / to.y;
				to.y = max.y;
			}
		}
		return this.zoomTo({'x': to.x.toInt(), 'y': to.y.toInt()});
	},

	zoomTo: function(to) {
		var box = window.getSize();
		var pos = (!this.options.positionToCenter) ? {
			x: (this.coords.left + (this.coords.width / 2) - to.x / 2).toInt()
				.limit(box.scroll.x + this.options.margin, box.scroll.x + box.size.x - this.options.margin - to.x),
			y: (this.coords.top + (this.coords.height / 2) - to.y / 2).toInt()
				.limit(box.scroll.y + this.options.margin, box.scroll.y + box.size.y - this.options.margin - to.y)
		} :  {
			x: box.scroll.x + ((box.size.x - to.x) / 2).toInt(),
			y: box.scroll.y + ((box.size.y - to.y) / 2).toInt()
		};
		this.box.removeClass('remo-loading');
		this.fireEvent('onOpen');
		var vars = {'left': pos.x, 'top': pos.y, 'width': to.x, 'height': to.y};
		if (this.options.resizeOpacity != 1) vars.opacity = [this.options.resizeOpacity, 1];
		else this.box.setStyle('opacity', 1);
		this.boxFx.start(vars).chain(this.openEnd.bind(this));
	},

	generateTitle: function() {
		var title = this.options.title || this.element.getProperty('title');
		if (!title) return false;
		title = title.split(' :: ');
		var ret = [new Element('h4').setHTML(title[0])];
		if (title[1]) ret.push(new Element('p').setHTML(title[1]));
		return ret;
	},

	build: function() {
		this.box = new Element('div', {
			'class': 'remo-box',
			styles: {
				display: 'none',
				zIndex: ReMooz.options.zIndex
			},
			events: {
				click: function() {
					if (this.zoomed) return true;
					this.close();
					return false;
				}.bind(this)
			}
		});
		if (this.options.className) this.box.addClass(this.options.className);
		this.boxFx = new Fx.Styles(this.box, $merge({
			duration: 400,
			unit: 'px',
			transition: Fx.Transitions.Quart.easeOut,
			wait: false
		}, this.options.resizeOptions));

		if (this.options.shadow) {
			var shadow = new Element('div', {'class': 'remo-bg-wrap'}).inject(this.box);
			['n', 'ne', 'e', 'se', 's', 'sw', 'w', 'nw'].each(function(dir) {
				new Element('div', {'class': 'remo-bg remo-bg-' + dir}).inject(shadow);
			});
			var shadowFx = new Fx.Style(shadow, 'opacity', {wait: false}).hide();
			this.addEvent('onOpen', shadowFx.start.pass(1, shadowFx))
				.addEvent('onClose', shadowFx.hide.bind(shadowFx));
		}
		this.content = new Element('div', {'class': 'remo-content'}).inject(this.box);

		var btn = new Element('a', {
			'class': 'remo-btn-close',
			events: {'click': this.close.bind(this)}
		}).inject(this.box);
		this.fxs = new Fx.Style(btn, 'opacity', $merge({
			duration: 300,
			wait: false
		}, this.options.fxsOptions));
		var title = (this.options.generateTitle || this.generateTitle).call(this);
		if (title) {
			this.title = new Element('div', {'class': 'remo-title'})
				.inject(new Element('div', {'class': 'remo-title-wrap'}).inject(this.box));
			new Element('div', {'class': 'remo-title-bg'}).setOpacity(0.8).inject(this.title);
			new Element('div', {'class': 'remo-title-txt'})
				[$type(title) == 'string' ? 'setHTML' : 'adopt'](title).inject(this.title);
		}
		this.fxs.element = $$(btn, this.title);
		this.fxs.set(0);
		this.box.inject(document.body);
	}

});

ReMooz.factory = $extend;

ReMooz.factory({

	options: {
		zIndex: 41,
		zIndexFocus: 42,
		query: 'a.remooz',
		optionsField: 'rel',
		classOptions: {}
	},

	initialize: function(elements, options) {
		this.setOptions(options);
		return $$(elements || this.options.query).map(function(el) {
			var obj = el.getProperty(this.options.optionsField);
			if (obj && (obj = Json.decode(obj, true))) obj = $merge(obj, this.options.classOptions);
			return (el.$attributes.remooz = new ReMooz(el, obj || this.options.classOptions));
		}, this);
	},

	stack: [],

	open: function(obj) {
		this.focus(obj);
	},

	close: function(obj) {
		var last = this.stack.length - 1;
		if (this.stack.indexOf(obj) == last) this.focus(this.stack[last - 1]);
		this.stack.remove(obj);
	},

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

});

ReMooz.factory(new Options);